Threat Research
Stay on top of the latest threat research, information on in-the-wild cyber attacks and cyber operations from Juniper Threat Labs.
Juniper Employee , Juniper Employee Juniper Employee
Threat Research
Stalking Stalkerware: A Deep Dive Into FlexiSPY
Dec 20, 2019



In October, the FTC announced it had reached a settlement effectively shutting down Retina-X Studios, maker of MobileSpy, PhoneSheriff and TeenShield. According to the FTC:


“Retina-X did not make sure purchasers were using the apps for legitimate purposes. In fact, to install the apps, purchasers often had to weaken the security protections on your smartphone (sometimes called jailbreaking or rooting). Plus, once a purchaser installed the app on your phone, they could remove the icon, so you wouldn’t know they were monitoring you.”


Eva Galperin, a security researcher from the Electronic Frontier Foundation’s Threat Lab, has been leading the fight against stalkerware and personally helping victims reclaim their digital privacy. But despite the recent actions by the FTC and attempts by device makers to purge their online app stores of stalkerware, this sort of surveillance software is still readily available online. In this blog, we’ll take a deep dive into one example of this kind of threat.


Introducing FlexiSPY

FlexiSPY boasts that one can “Spy On Any Computer With Our Powerful Computer Monitoring Software”.




They claim to have “invented the world's first commercial Spy phone Application in 2006”. They also claim to have been “featured” in numerous publications:




The actual media coverage, however, is far from glowing:



The available features vary by device and operating system but include a comprehensive selection of tools to electronically surveil and physically track the victim.




Among these features are GPS tracking:




Covert photography:




And interception of email, calls and messaging:




Despite a small print disclaimer about “sneaky” usage, FlexiSPY is designed to be installed surreptitiously and to remain hidden from the victim while in use (note: the “user” referred to below is the person installing the app, not the unwitting victim actually using the phone).




FlexiSPY even offers an installation service and pre-hacked phones:




FlexiSPY for Android

The sample we’ll analyze was released earlier this year and was first submitted to VirusTotal in September. Like most Android apps, FlexiSPY is written primarily in Java but it also includes several native libraries compiled for the ARM processors found in most Android devices. The Java portions of the app can mostly be decompiled back to Java source code. However, the native libraries are harder to reverse-engineer and (as we’ll see later) can be used to hide application data.


Installation and Capabilities

FlexiSPY is designed to be installed secretively. On iPhones and Android devices, FlexiSPY specifies that the device should be jailbroken or rooted, giving the application low-level system privileges to circumvent routine protections between installed apps and to hide the FlexiSPY app from the device’s owner. And since the end user will never see the app or the installation process, FlexiSPY is free to request a wide range of Android permissions:




These permissions grant the app access to the devices call log, contacts, camera, messages, microphone, etc. In addition, FlexiSPY taps into Android’s accessibility features to spy on other apps such as Gmail.




By intercepting accessibility events, FlexiSPY is able to exfiltrate emails and contact information from the victim’s Gmail account.


If the victim becomes suspicious or the stalker needs to cover their tracks, FlexiSPY can remotely wipe all data from the device.


In the code responsible for displaying the end user license agreement to the person installing the software (not the victim!), we see a hint of the obfuscation FlexiSPY uses to avoid detection by antimalware and spyware-detection software. The URL is deliberately encoded so that their website does not appear as a plaintext string in the app.




Decoding the string "aHR0cHM6Ly93d3cuZmxleGlzcHkuY29tL2VuL2luc3RhbGxhdGlvbi1ldWxhLmh0bQ==" produces the URL “”. 


On installation, FlexiSPY presents itself as an innocuous-looking “Sync Services” app:




The installation process vaguely describes the application “collect[ing] and encrypt[ing]” the device’s data:





As mentioned above, FlexiSPY can hide its own app icon and other traces from the device’s user. FlexiSPY claims this is “to stop the child from getting around family rules” or “to preserve screen real estate.” In practice, this is an effective way to prevent the victim from detecting that their device has been compromised. The icon can be hidden during installation:




The icon disappears completely from the desktop once this option is enabled:




In addition, this setting can be changed remotely through FlexiSPY’s web portal:




As we’ll see in the next section, FlexiSPY goes to great lengths to hide the true name and purpose of its application, both from the end user directly and from spyware scanners.


Evasion and Obfuscation

We previously saw that FlexiSPY used base64-encoding to obfuscate URLs. The app also uses RSA and AES encryption to hide various components from static analysis. For example, the app has the encrypted file “5009” included as an asset. Searching the decompiled Java code, we find that this filename is hardcoded as PCF_FILENAME.




At runtime, the complete path to this file becomes an instance variable mPcfPath of an AppEngine object:




This path is used in a call to loadProductConfiguration:




Unfortunately, loadProductConfiguration couldn’t be decompiled correctly.




Nevertheless, there’s enough information to see that the file is decrypted with ConfigDecryptor.doDecrypt():




From there, we see that the “5009” file is encrypted with AES. 




Cracking the encryption is infeasible, leaving us with the option of either capturing the decrypted data from the running app or finding the encryption key and initialization vector. Fortunately, the latter is not too difficult. The actual encryption and decryption use standard Java libraries, including Cipher and SecretKeyFactory.




The initialization vector is generated by getIvParameterSpec().




The string of raw bytes is passed to the function d().




We’ve finally caught a break! All of these functions are standard parts of the Java crypto libraries, so we can drop this snippet into a freestanding Java program and extract the 16 byte initialization vector. Now, we move on to the secret key. Going back to the doDecrypt method above, we see that the raw encryption key comes from the function getKey():




Note the call to System.loadLibrary("flsonyconfig"). This loads one of the native ARM libraries included in the app and makes it accessible via the Java Native Interface (JNI). The function T().h() is itself a JNI function:




The “native” keyword tells the Java compiler that the actual implementation of h() is in a native library. In the library, we find a function called Java_k_v_T_h, which indicates that it is the native implementation of the function k.v.T.h(). Native code generally can’t be decompiled as cleanly or reliably as Java bytecode but the result is good enough to reveal what is going on.




What we see are a series of function calls that each return a single character, which is then incremented by 25 (0x19 in hexadecimal):




All of these characters/bytes sit consecutively on the program’s call stack. The parameter param_1 is a pointer to a JNIenv object, which includes a table of functions that JNI user code can call. The function at offset 0x2c0 is NewByteArray(), so we can translate:


uVar1 = (**(code **)(*param_1 + 0x2c0))(param_1,0x10);




uVar1 = NewByteArray(0x10);


which allocates a 16 byte array for the raw encryption key. Likewise, since the offset 0x340 corresponds with the JNI function SetByteArrayRegion, we can translate:


 (**(code **)(*param_1 + 0x340))(param_1,uVar1,0,0x10,&cStack44);






which copies the 16 consecutive bytes starting at the location of cStack44 into the newly created buffer, a pointer to which is returned to the calling function.


By taking these 16 bytes and calling KeyGenerator with the appropriate parameters, we can generate the encryption key. We can now decrypt 5009, which turns out to be an XML-formatted configuration file (formatted for clarity):




This trick of embedding encryption keys in native code is used repeatedly. In FlexiSPY’s “crackmitigation” module, designed to prevent unauthorized/unpaid use of the app, we see the following:




Both fpu() and jbmc() are native functions that return obfuscated but hardcoded strings of bytes, as above. But jbmc() adds an extra hurdle by requiring an arbitrary parameter that is ultimately ignored.




The parameter param_2 is the location of the argument array and param_3 is the number of arguments. It doesn’t matter what argument is passed, as long as there is at least one. Then dhj_bwl() reconstructs the raw encryption key as before. Here, the FlexiSPY developers chose to use RSA encryption.




Having recovered both the ciphertext and the key, we can decrypt the ciphertext to get the URL "".


Elsewhere, strings are obfuscated with a simple hardcoded XOR cipher:




“PQYUOBkGHxg2AUAfBRkXC1oXGR9xGl8Y” becomes and “PQYUOBkGHxg2AXM7ChIbB1IIDAg7CV8cMQYdAgsy” becomes The domain is associated with FlexiSPY’s “employee behaviour monitoring” product:




Encryption is used inconsistently. At one point in the code, the URL and credentials for one of FlexiSPY’s servers are not encrypted at all but merely base64-encoded.




On the device, the app’s logs are DES-encrypted with a hardcoded key.




This is intended to make it harder to debug and modify the application but easy to circumvent:




A Security Flaw

FlexiSPY boasts on their website that their data has not been hacked, unlike other stalkerware makers. 




Indeed, a number of stalkerware companies have been hacked, including Retina-X, Xnore and mSpy. But a flaw in FlexiSPY’s encryption makes it possible for an eavesdropper to intercept any of the personal data transmitted from the app to FlexiSPY’s servers. It begins with FlexiSPY’s failure to use https and instead rely on a home-rolled encryption protocol. Each session with the server is initiated by generating a 16-byte (128-bit) AES encryption key. A full 128-bit key is generated but then the last 48 bits are overwritten with hardcoded data.




The unmodified key is sent to FlexiSPY’s servers over plain http with the command code 100 (64 in hexadecimal) and the server replies with the same command code and 112 bytes of encrypted data:




The data is encrypted with the modified 128-bit AES key, which uses the first 10 bytes of the key sent by the client and the 6 hard-coded bytes above, and the initialization vector is the same one we found in the previous section. Upon decryption, this reveals a session ID and the server’s RSA public key:




We can see this exchange reflected in the device logs. Note that 1768422606 is 0x6967FAD0 in hexadecimal:




Following this exchange, the app begins generating a new AES key for each transmission. This key is encrypted using the server’s RSA public key and transmitted along with the AES-encrypted data. Because we only have the server’s public key and not the corresponding private key, we can’t decrypt these transmissions based on intercepted network traffic alone. But the initial key exchange happens over plain http and includes no validation, so an attacker with control of the local network (using a rogue access point like the WiFi Pineapple, for example) can intercept the initial AES key and reply with their own RSA public key and a new session identifier. Then, every subsequent selfie, SMS and surreptitious recording exfiltrated from the device can be decrypted by the attacker for the duration of the session.


We were able to reproduce this vulnerability on a test device at Juniper Threat Labs by redirecting traffic from to a server that responded with a hardcoded session identifier 3134983653 (0xBADC0DE5 in hexadecimal) and RSA public key, all encrypted using the app-generated AES key. The FlexiSPY app accepted the spoofed session identifier and key as seen in its log files:




We were able to successfully decrypt subsequent data sent from the app, thus circumventing the entire encryption scheme.


Detection and Remediation

Both network-based and endpoint protection have limited utility against the stalkerware model. SkyATP and JATP will detect the app when it is downloaded, but the attacker can choose to download the app on an unsecured network or simply purchase a pre-compromised phone. Similarly, being installed on a jailbroken or rooted phone gives the stalkerware app superuser access that can easily defeat an antivirus app installed directly on the device. Remediation should involve — at a minimum — a complete factory reset of the device. For a device that may have been purchased directly from a stalkerware vendor, replacing the device is the most secure option.


Indicators of compromise include network traffic to or from the following domains:


SHA256 hashes for other known versions of the FlexiSPY app:




Despite the FBI’s takedown of Retina-X and the continued purging of apps on the Google and Apple app stores, stalkerware like FlexiSPY remains a severe threat to safety and privacy. These stalkerware apps are full-featured, allowing a stalker to access virtually any media or data on the phone, track the device’s location, record audio and video remotely and much more. On a jailbroken or rooted device, all of this may be invisible to the typical user, while prevention and mitigation remain ongoing challenges. 


Top Kudoed Members