Introduction
In May 2020, Bitdefender released a white paper containing a detailed analysis of Mandrake, a sophisticated Android cyber-espionage platform, which had been active in the wild for at least four years.
In April 2024, we discovered a suspicious sample that appeared to be a new version of Mandrake. Ensuing analysis revealed as many as five Mandrake applications, which had been available on Google Play from 2022 to 2024 with more than 32,000 installs in total, while staying undetected by any other vendor. The new samples included new layers of obfuscation and evasion techniques, such as moving malicious functionality to obfuscated native libraries, using certificate pinning for C2 communications, and performing a wide array of tests to check if Mandrake was running on a rooted device or in an emulated environment.
Our findings, in a nutshell, were as follows.
- After a two-year break, the Mandrake Android spyware returned to Google Play and lay low for two years.
- The threat actors have moved the core malicious functionality to native libraries obfuscated with OLLVM.
- Communication with command-and-control servers (C2) uses certificate pinning to prevent capture of SSL traffic.
- Mandrake is equipped with a diverse arsenal of sandbox evasion and anti-analysis techniques.
Kaspersky products detect this threat as HEUR:Trojan-Spy.AndroidOS.Mandrake.*.
Technical details
Background
The original Mandrake campaign with its two major infection waves, in 2016–2017 and 2018–2020, was analyzed by Bitdefender in May 2020. After the Bitdefender report was published, we discovered one more sample associated with the campaign, which was still available on Google Play.
In April 2024, we found a suspicious sample that turned out to be a new version of Mandrake. The main distinguishing feature of the new Mandrake variant was layers of obfuscation designed to bypass Google Play checks and hamper analysis. We discovered five applications containing Mandrake, with more than 32,000 total downloads. All these were published on Google Play in 2022 and remained available for at least a year. The newest app was last updated on March 15, 2024 and removed from Google Play later that month. As at July 2024, none of the apps had been detected as malware by any vendor, according to VirusTotal.
Applications
Package name | App name | MD5 | Developer | Released | Last updated on Google Play | Downloads |
com.airft.ftrnsfr | AirFS | 33fdfbb1acdc226eb177eb42f3d22db4 | it9042 | Apr 28, 2022 |
Mar 15, 2024 |
30,305 |
com.astro.dscvr | Astro Explorer | 31ae39a7abeea3901a681f847199ed88 | shevabad | May 30, 2022 |
Jun 06, 2023 |
718 |
com.shrp.sght | Amber | b4acfaeada60f41f6925628c824bb35e | kodaslda | Feb 27, 2022 |
Aug 19, 2023 |
19 |
com.cryptopulsing.browser | CryptoPulsing | e165cda25ef49c02ed94ab524fafa938 | shevabad | Nov 02, 2022 |
Jun 06, 2023 |
790 |
com.brnmth.mtrx | Brain Matrix | – | kodaslda | Apr 27, 2022 |
Jun 06, 2023 |
259 |
We were not able to get the APK file for com.brnmth.mtrx, but given the developer and publication date, we assume with high confidence that it contained Mandrake spyware.
Malware implant
The focus of this report is an application named AirFS, which was offered on Google Play for two years and last updated on March 15, 2024. It had the biggest number of downloads: more than 30,000. The malware was disguised as a file sharing app.
According to reviews, several users noticed that the app did not work or stole data from their devices.
Infection chain
Like the previous versions of Mandrake described by Bitdefender, applications in the latest campaign work in stages: dropper, loader and core. Unlike the previous campaign where the malicious logic of the first stage (dropper) was found in the application DEX file, the new versions hide all the first-stage malicious activity inside the native library libopencv_dnn.so, which is harder to analyze and detect than DEX files. This library exports functions to decrypt the next stage (loader) from the assets/raw folder.
Interestingly, the sample com.shrp.sght has only two stages, where the loader and core capabilities are combined into one APK file, which the dropper decrypts from its assets.
While in the past Mandrake campaigns we saw different branches (“oxide”, “briar”, “ricinus”, “darkmatter”), the current campaign is related to the “ricinus” branch. The second- and third-stage files are named “ricinus_airfs_3.4.0.9.apk”, “ricinus_dropper_core_airfs_3.4.1.9.apk”, “ricinus_amber_3.3.8.2.apk” and so on.
When the application starts, it loads the native library:
To make detection harder, the first-stage native library is heavily obfuscated with the OLLVM obfuscator. Its main goal is to decrypt and load the second stage, named “loader“. After unpacking, decrypting and loading into memory the second-stage DEX file, the code calls the method dex_load and executes the second stage. In this method, the second-stage native library path is added to the class loader, and the second-stage main activity and service start. The application then shows a notification that asks for permission to draw overlays.
When the main service starts, the second-stage native library libopencv_java3.so is loaded, and the certificate for C2 communications, which is placed in the second-stage assets folder, is decrypted. The treat actors used an IP address for C2 communications, and if the connection could not be established, the malware tried to connect to more domains. After successfully connecting, the app sends information about the device, including the installed applications, mobile network, IP address and unique device ID, to the C2. If the threat actors find their target relevant on the strength of that data, they respond with a command to download and run the “core” component of Mandrake. The app then downloads, decrypts and executes the third stage (core), which contains the main malware functionality.
Second-stage commands:
Command | Description |
start | Start activity |
cup | Set wakelock, enable Wi-Fi, and start main parent service |
cdn | Start main service |
stat | Collect information about connectivity status, battery optimization, “draw overlays” permission, adb state, external IP, Google Play version |
apps | Report installed applications |
accounts | Report user accounts |
battery | Report battery percentage |
home | Start launcher app |
hide | Hide launcher icon |
unload | Restore launcher icon |
core | Start core loading |
clean | Remove downloaded core |
over | Request “draw overlays” permission |
opt | Grant the app permission to run in the background |
Third stage commands:
Command | Description |
start | Start activity |
duid | Change UID |
cup | Set wakelock, enable Wi-Fi, and start main parent service |
cdn | Start main service |
stat | Collect information about connectivity status, battery optimization, “draw overlays” permission, adb state, external IP, Google Play version |
apps | Report installed applications |
accounts | Report user accounts |
battery | Report battery percentage |
home | Start launcher app |
hide | Hide launcher icon |
unload | Restore launcher icon |
restart | Restart application |
apk | Show application install notification |
start_v | Load an interactive webview overlay with a custom implementation of screen sharing with remote access, commonly referred to by the malware developers “VNC” |
start_a | Load webview overlay with automation |
stop_v | Unload webview overlay |
start_i, start_d | Load webview overlay with screen record |
stop_i | Stop webview overlay |
upload_i, upload_d | Upload screen record |
over | Request “draw overlays” permission |
opt | Grant the app permission to run in the background |
When Mandrake receives a start_v command, the service starts and loads the specified URL in an application-owned webview with a custom JavaScript interface, which the application uses to manipulate the web page it loads.
While the page is loading, the application establishes a websocket connection and starts taking screenshots of the page at regular intervals, while encoding them to base64 strings and sending these to the C2 server. The attackers can use additional commands to adjust the frame rate and quality. The threat actors call this “vnc_stream”. At the same time, the C2 server can send back control commands that make application execute actions, such as swipe to a given coordinate, change the webview size and resolution, switch between the desktop and mobile page display modes, enable or disable JavaScript execution, change the User Agent, import or export cookies, go back and forward, refresh the loaded page, zoom the loaded page and so on.
When Mandrake receives a start_i command, it loads a URL in a webview, but instead of initiating a “VNC” stream, the C2 server starts recording the screen and saving the record to a file. The recording process is similar to the “VNC” scenario, but screenshots are saved to a video file. Also in this mode, the application waits until the user enters their credentials on the web page and then collects cookies from the webview.
The start_a command allows running automated actions in the context of the current page, such as swipe, click, etc. If this is the case, Mandrake downloads automation scenarios from the URL specified in the command options. In this mode, the screen is also recorded.
Screen recordings can be uploaded to the C2 with the upload_i or upload_d commands.
The main goals of Mandrake are to steal the user’s credentials, and download and execute next-stage malicious applications.
Data decryption methods
Data encryption and decryption logic is similar across different Mandrake stages. In this section, we will describe the second-stage data decryption methods.
The second-stage native library libopencv_java3.so contains AES-encrypted C2 domains, and keys for configuration data and payload decryption. Encrypted strings are mixed with plain text strings.
To get the length of the string, Mandrake XORs the first three bytes of the encrypted array, then uses the first two bytes of the array as keys for custom XOR encoding.
The key and IV for decrypting AES-encrypted data are encoded in the same way, with part of the data additionally XORed with constants.
Mandrake uses the OpenSSL library for AES decryption, albeit in quite a strange way. The encrypted file is divided into 16-byte blocks, each of these decrypted with AES-CFB128.
The encrypted certificate for C2 communication is located in the assets/raw folder of the second stage as a file named cart.raw, which is decrypted using the same algorithm.
Installing next-stage applications
When Mandrake gets an apk command from the C2, it downloads a new separate APK file with an additional module and shows the user a notification that looks like something they would receive from Google Play. The user clicking the notification initiates the installation process.
Android 13 introduced the “Restricted Settings” feature, which prohibits sideloaded applications from directly requesting dangerous permissions. To bypass this feature, Mandrake processes the installation with a “session-based” package installer.
Sandbox evasion techniques and environment checks
While the main goal of Mandrake remains unchanged from past campaigns, the code complexity and quantity of the emulation checks have significantly increased in recent versions to prevent the code from being executed in environments operated by malware analysts. However, we were able to bypass these restrictions and discovered the changes described below.
The versions of the malware discovered earlier contained only a basic emulation check routine.
In the new version, we discovered more checks.
To start with, the threat actors added Frida detection. When the application starts, it loads the first-stage native library libopencv_dnn.so. The init_array section of this library contains the Frida detector function call. The threat actors used the DetectFrida method. First, it computes the CRC of all libraries, then it starts a Frida detect thread. Every five seconds, it checks that libraries in memory have not been changed. Additionally, it checks for Frida presence by looking for specific thread and pipe names used by Frida. So, when an analyst tries to use Frida against the application, execution is terminated. Even if you use a custom build of Frida and try to hook a function in the native library, the app detects the code change and terminates.
Next, after collecting device information to make a request for the next stage, the application checks the environment to find out if the device is rooted and if there are analyst tools installed. Unlike some other threat actors who seek to take advantage of root access, Mandrake developers consider a rooted device dangerous, as average users, their targets, do not typically root their phones. First, Mandrake tries to find a su binary, a SuperUser.apk, Busybox or Xposed framework, and Magisk and Saurik Substrate files. Then it checks if the system partition is mounted as read-only. Next, it checks if development settings and ADB are enabled. And finally, it checks for the presence of a Google account and Google Play application on the device.
C2 communication
All C2 communications are maintained via the native part of the applications, using an OpenSSL static compiled library.
To prevent network traffic sniffing, Mandrake uses an encrypted certificate, decrypted from the assets/raw folder, to secure C2 communications. The client needs to be verified by this certificate, so an attempt to capture SSL traffic results in a handshake failure and a breakdown in communications. Still, any packets sent to the C2 are saved locally for additional AES encryption, so we are able to look at message content. Mandrake uses a custom JSON-like serialization format, the same as in previous campaigns.
Example of a C2 request:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
node #1 { uid "a1c445f10336076b"; request "1000"; data_1 "32|3.1.1|HWLYO-L6735|26202|de||ricinus_airfs_3.4.0.9|0|0|0||0|0|0|0|Europe/Berlin||180|2|1|41|115|0|0|0|0|loader|0|0|secure_environment||0|0|1|0||0|85.214.132.126|0|1|38.6.10-21 [0] [PR] 585796312|0|0|0|0|0|"; data_2 "loader"; dt 1715178379; next #2; } node #2 { uid "a1c445f10336076b"; request "1010"; data_1 "ricinus_airfs_3.4.0.9"; data_2 ""; dt 1715178377; next #3; } node #3 { uid "a1c445f10336076b"; request "1003"; data_1 "com.airft.ftrnsfr\n\ncom.android.calendar\n\[redacted]\ncom.android.stk\n\n"; data_2 ""; dt 1715178378; next NULL; } |
Example of a C2 response:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
node #1 { response "a1c445f10336076b"; command "1035"; data_1 ""; data_2 ""; dt "0"; next #2; } node #2 { response "a1c445f10336076b"; command "1022"; data_1 "20"; data_2 "1"; dt "0"; next #3; } node #3 { response "a1c445f10336076b"; command "1027"; data_1 "1"; data_2 ""; dt "0"; next #4; } node #4 { response "a1c445f10336076b"; command "1010"; data_1 "ricinus_dropper_core_airfs_3.4.1.9.apk"; data_2 "60"; dt "0"; next NULL; } |
Mandrake uses opcodes from 1000 to 1058. The same opcode can represent different actions depending on whether it is used for a request or a response. See below for examples of this.
- Request opcode 1000: send device information;
- Request opcode 1003: send list of installed applications;
- Request opcode 1010: send information about the component;
- Response opcode 1002: set contact rate (client-server communication);
- Response opcode 1010: install next-stage APK;
- Response opcode 1011: abort next-stage install;
- Response opcode 1022: request user to allow app to run in background;
- Response opcode 1023: abort request to allow app to run in background;
- Response opcode 1027: change application icon to default or Wi-Fi service icon.
Attribution
Considering the similarities between the current campaign and the previous one, and the fact that the C2 domains are registered in Russia, we assume with high confidence that the threat actor is the same as stated in the Bitdefender’s report.
Victims
The malicious applications on Google Play were available in a wide range of countries. Most of the downloads were from Canada, Germany, Italy, Mexico, Spain, Peru and the UK.
Conclusions
The Mandrake spyware is evolving dynamically, improving its methods of concealment, sandbox evasion and bypassing new defense mechanisms. After the applications of the first campaign stayed undetected for four years, the current campaign lurked in the shadows for two years, while still available for download on Google Play. This highlights the threat actors’ formidable skills, and also that stricter controls for applications before being published in the markets only translate into more sophisticated, harder-to-detect threats sneaking into official app marketplaces.
Indicators of Compromise
File Hashes
141f09c5d8a7af85dde2b7bfe2c89477
1b579842077e0ec75346685ffd689d6e
202b5c0591e1ae09f9021e6aaf5e8a8b
31ae39a7abeea3901a681f847199ed88
33fdfbb1acdc226eb177eb42f3d22db4
3837a06039682ced414a9a7bec7de1ef
3c2c9c6ca906ea6c6d993efd0f2dc40e
494687795592106574edfcdcef27729e
5d77f2f59aade2d1656eb7506bd02cc9
79f8be1e5c050446927d4e4facff279c
7f1805ec0187ddb54a55eabe3e2396f5
8523262a411e4d8db2079ddac8424a98
8dcbed733f5abf9bc5a574de71a3ad53
95d3e26071506c6695a3760b97c91d75
984b336454282e7a0fb62d55edfb890a
a18a0457d0d4833add2dc6eac1b0b323
b4acfaeada60f41f6925628c824bb35e
cb302167c8458e395337771c81d5be62
da1108674eb3f77df2fee10d116cc685
e165cda25ef49c02ed94ab524fafa938
eb595fbcf24f94c329ac0e6ba63fe984
f0ae0c43aca3a474098bd5ca403c3fca
Domains and IPs
45.142.122[.]12
ricinus[.]ru
ricinus-ca[.]ru
ricinus-cb[.]ru
ricinus-cc[.]ru
ricinus[.]su
toxicodendron[.]ru
Mandrake spyware sneaks onto Google Play again, flying under the radar for two years
Michael Lesanangi
Waiting to see how it works
Securelist
Hi Michael!
This is a full report, for the detailed description of Mandrake’s methods, please see the “Infection chain” part (https://securelist.com/mandrake-apps-return-to-google-play/113147/#infection-chain).