We sometimes come across modified applications when analyzing suspicious files. These are created in response to user requests for more customization options within the app or for new features that the official versions don’t have. Unfortunately, it’s not uncommon for popular mods to contain malware. This often happens because they’re distributed on unofficial websites that don’t have any moderation. For example, last year we found popular WhatsApp mods infected with CanesSpy and distributed this way. Before that, we found ads for WhatsApp mods infected with the Triada Trojan dropper in the popular Snaptube application. However, even official app stores can be infiltrated by infected apps. In 2019, we discovered the Necro dropper hidden within CamScanner, a widely used document scanning and processing app available on Google Play. At the time of the malware discovery, this app had been downloaded to more than 100 million devices worldwide. Sadly, history has repeated itself, and this time the Trojan authors exploited both distribution vectors: the new version of the multi-stage Necro loader infected both apps in Google Play and modified versions of Spotify, Minecraft, and other popular applications in unofficial sources.
Our conclusions in a nutshell:
In late August 2024, our attention was drawn to a Spotify mod called Spotify Plus, version 18.9.40.5. At the time of writing this, the mod could be downloaded from spotiplus[.]xyz and several related sites that linked to it. The original website claimed that the mod was certified, safe, and contained numerous additional features not found in the official app. We decided to verify the claims about the application’s safety by downloading the latest version from this website (acb7a06803e6de85986ac49e9c9f69f1) and analyzing it.
The mod implements a custom Application subclass that initializes an SDK named adsrun in its onCreate method. This SDK is intended for integrating several advertising modules into the application: among other things, it initializes a module named Coral SDK. Upon activation, Coral SDK transmits a POST request to a designated command-and-control server. This request contains encrypted JSON data, specifically detailing the compromised device and the application hosting the module. The encryption method employed is a substitution cipher, where the substitution values are generated using a standard Java pseudo-random number generator seeded with a predefined constant. See an example of data sent by the module below.
{ "appId": "REDACTED", "channelId": "com.spoti.plus", "androidId": "REDACTED", "isAdb": false, "isProxy": false, "isSimulator": false, "isDebug": false, "localShellVer": 0, "sdkVer": 116, "appVersion": "1020000005", "appVersionName": "18.9.40.5" } |
The C2 server returns a JSON response with an error code, encrypted with the same method. A value of 0 indicates successful execution. In this case, the response from the C2 will also contain an array of one object with a link to download the image in PNG format and associated metadata: name, MD5, version, and so on. Intriguingly, the downloaded file is termed “shellP”, suggesting it might be a condensed form of “shellPlugin”.
{ "code": 0, "result": [{ "md5": "F338384C5B4BC7D55681A3532273B4EB", "name": "shellP", "sdkver": 100, "url": "hxxps://adoss.spinsok[.]com/plugin/shellP_100.png.png" } ] } |
Next, the module verifies the integrity of the downloaded image by calculating its MD5 hash and comparing it to the value received from the server. A payload is hidden in this image using steganography, which the module must extract and execute in the next step.
Coral SDK uses a very simple steganographic algorithm. If the MD5 check is successful, it extracts the contents of the PNG file — the pixel values in the ARGB channels — using standard Android tools. Then the getPixel method returns a value whose least significant byte contains the blue channel of the image, and processing begins in the code.
If we consider the blue channel of the image as a byte array of dimension 1, then the first four bytes of the image are the size of the encoded payload in Little Endian format (from the least significant byte to the most significant). Next, the payload of the specified size is recorded: this is a JAR file encoded with Base64, which is loaded after decoding via DexClassLoader. Coral SDK loads the sdk.fkgh.mvp.SdkEntry class in a JAR file using the native library libcoral.so. This library has been obfuscated using the OLLVM tool. The starting point, or entry point, for execution within the loaded class is the run method.
Therefore, the security claims made about the application on the mod website can be considered false.
Having searched for the loader in our telemetry, we found other apps infected with Necro, including those available in Google Play at the time of writing this report. Their combined audience numbered more than 11 million Android devices.
Our first find is the Wuta Camera app. Judging by its page in Google Play, it was downloaded at least 10 million times. According to our data, the Necro loader has been embedded in it starting from version 6.3.2.148. The latest version of the app at the time of collecting information, 6.3.6.148 (1cab7668817f6401eb094a6c8488a90c), which was available on Google Play, also had the Necro loader. We reported the presence of malicious code to Google Play, after which the loader was removed from the app in version 6.3.7.138.
The second infected app we found was Max Browser.
This browser, according to Google Play, has been installed more than a million times and, starting with version 1.2.0, also contained the Necro loader. After we reported it, Google took down the infected app from their store.
We also found WhatsApp mods containing the Necro loader (0898d1a6232699c7ee03dd5e58727ede) in unofficial sources. The infected application is distributed under the package name com.leapzip.animatedstickers.maker.android. Interestingly, there’s a legitimate app on Google Play with the exact same package name that isn’t a WhatsApp mod, but instead offers a collection of stickers for the messaging app.
The loader contained within the ad module in these applications functions somewhat differently from the sample described above. For instance, the code isn’t obfuscated at all but is protected by the SecAPK code protector. Additionally, the application uses Google’s Firebase Remote Config cloud service as a C2, storing information about files that need to be downloaded and executed.
While examining this loader, we discovered an interesting quirk: the malicious code within it has an 84% or 90% chance of execution. Initially, a random number between 0 and 99 is generated. Subsequently, based on the application package name, a threshold for malware execution is selected: the generated number must exceed either 9 or 15 for the loader to launch. If the number meets this criterion, a corresponding flag inhibiting loader operation is set to false, and the malicious functionality is executed.
Intermediate payloads downloaded by this loader are not pre-encoded. The Trojan receives both the entry point information for the downloaded file and the download link from its C2 server. According to our data, one of the payloads (37404ff6ac229486a1de4b526dd9d9b6) bore resemblance to a loader found in a modified version of Spotify, albeit with minor variations.
This is not an exhaustive list of our findings. In addition to Spotify and WhatsApp mods, as well as apps in Google Play, we found infected game mods, including the following:
Given that various apps from multiple sources, including official ones, were found to be infected, we believe that the developers used an untrusted solution for ad integration. This led to a malicious loader appearing in the apps. Our security solutions detect it with the following verdicts:
During our research, we managed to obtain several samples of payloads that the loader subsequently executes. This particular payload (fa217ca023cda4f063399107f20bd123) exhibits several interesting characteristics that allow us to classify it as belonging to the Necro family:
Based on this, we assert that both the examined payload and the original loader belong to the Necro family, which is familiar to us.
Now let’s move on to analyzing the payload. The second stage of the launch process reads a JSON-formatted configuration embedded within the code. An example of the configuration is provided below.
{ "hs":{ "server":"https://oad1.azhituo.com:9190", "default":"https://oad1.azhituo.com:9190", "dataevent":"https://oad1.azhituo.com:9190", "PluginServer":"https://oad1.azhituo.com:9190" }, "ps":{ "web":"canna", "dsp":"hatch" }, "mp":{ "PMask":"159" }, "rp":{} } |
The rp switch might contain malicious services to be launched, but it was empty in the samples we analyzed.
The mp configuration switch holds parameters for the second-stage loader. It’s likely an abbreviation for “module parameters”.
The malicious functionality of Necro is implemented in additional modules that are downloaded from the C2 server. The malware authors frequently refer to these as “plugins” in the code. The ps configuration field (likely an abbreviation for “plugin stop list”, meaning a list of prohibited plugins) is necessary to block these modules. The switches in this object are the names of plugins that are forbidden to load, and the values are alternative plugins that can be executed instead of the blocked ones if they were loaded. The download ban will be applied if the mp field has the PluginControl flag set to true. However, in the samples we were able to obtain, the restrictions did not apply. Additionally, the mp field may contain the PluginUpdateFeature flag, which controls plugin updates. If this flag is not present, plugins will be updated by default.
The hs switch in the configuration stores a list of C2 addresses which the Trojan will talk to. Note that the malware logic does not require all addresses to match, although in the sample we examined, they were identical. The Trojan needs each address to perform the following tasks:
In response, the server sends a list of plugins to download. These are downloaded asynchronously. To do this, the malware registers a broadcast receiver, and a separate thread, which is started for the download, sends a broadcast message when a plugin is ready to be downloaded. The plugins are differentiated by their name, which is also provided by the server.
The plugin loading code supports, among other things, the ability to decrypt plugins using various methods. Additionally, payloads can be extracted beforehand using the steganographic algorithm described above if a file with a .png extension was downloaded. The decryption method is specified in the file URL. The following options are available:
If no encryption method is specified, the plugin will be decrypted using a substitution cipher. The initial seed for this cipher will be the PMask parameter (short for plugin mask), which is defined in the mp object within the loader configuration. Once decoded, plugins can be loaded in various ways.
To better understand the attackers’ goals, we decided to thoroughly examine the payloads downloaded by the Trojan and, after analyzing telemetry data, found several Necro modules.
ed6c6924201bc779d45f35ccf2e463bb – Trojan.AndroidOS.Necro.g
This is a Necro module named “NProxy”. Its purpose is to create a tunnel through the victim’s device. When launched, the module connects to a server defined in the code.
This server acts as a C2 server that the Trojan talks to via an unidentified protocol implemented over TCP sockets. The C2 sends commands, which the Trojan processes. After processing, the Trojan forwards traffic from one endpoint to another through the victim’s device.
b3ba3749237793d2c06eaaf5263533f2 – Trojan.AndroidOS.Necro.i
We named this plugin “island”. When launched, the plugin generates a pseudo-random number, which it uses as an interval (in milliseconds) between displays of intrusive ads.
ccde06a19ef586e0124b120db9bf802e – Trojan.AndroidOS.Necro.d
This plugin is named “web”, and it is one of the most popular Necro plugins, judging by our telemetry data. Its code contains a configuration similar in structure to the shellPlugin payload configuration in the previous stage. It’s interesting that the code for this plugin contains artifacts of older versions of Necro.
Depending on the value of the CheckAbnormal flag, the plugin checks for the presence of a debugger in the execution environment and if a phone is connected via USB using ADB. If either condition is met, the Trojan clears the Logcat log to hide traces of its activity. Additionally, the plugin verifies if it has the permission to display windows on top of other applications. After all these checks, it launches a malicious task that runs once every two hours. When the malware starts, it sends a POST request containing details about the infected device to the server server. This is done to get the address of another server, named main URL, which the Trojan will communicate with frequently. If there’s an error when getting this address, the malware will fall back to using a server named default.
The received main URL serves as the C2 server: it sends a list of pages to the Trojan, which the malware later opens in the background before processing the interactive elements contained on them. This functionality has a couple of interesting features. First, the Trojan code contains some artifacts that indicate it might be running with elevated privileges. However, Android processes with elevated privileges do not allow WebView by default. Privilege checks occur directly when creating an instance of the WebView factory: in privileged processes, it won’t be created. To circumvent this restriction, the Trojan creates an instance of the factory directly using reflection, thus bypassing all checks of the current process.
Secondly, the Trojan can download and run other executables, which are then used to replace links loaded with WebView. Combined with the functionality described above, this theoretically allows to do things like adding any additional information to the URL parameters of a replaced link, such as confirmation codes for paid subscriptions, as well as executing other arbitrary code when loading specific links.
36ab434c54cce25d301f2a6f55241205 – Trojan-Downloader.AndroidOS.Necro.b
This module is named “Happy SDK”. Its code partially combines the NProxy and web modules logic, as well as the functionality of the previous stage of the loader with a few minor differences:
We believe this is a different variant of Necro where the developers have opted for a non-modular architecture in the malicious SDK. This suggests that Necro is highly adaptable and can download different iterations of itself, perhaps to introduce new features.
874418d3d1a761875ebc0f60f9573746 – Trojan.AndroidOS.Necro.j
We dubbed this plugin “Cube SDK”. It’s pretty simple and acts as a helper: its only job is to load other plugins to handle ads in the background.
522d2e2adedc3eb11eb9c4b864ca0c7f – Trojan.AndroidOS.Necro.l
This plugin, in addition to NProxy’s functionality, has an entry point for another plugin we’ve named “Tap”. Judging by its code, the latter is still under development: it contains a lot of unused functionality for interacting with ad pages. Tap downloads arbitrary JavaScript code and a WebView interface from the C2 server, which are responsible for viewing ads in the background. Among other things, the plugin includes com.leapzip.animatedstickers.maker.android as the package name of the infected app. This confirms that the WhatsApp mod loader described earlier, which uses Firebase Remote Config as a C2, also belongs to the Necro family.
These are all the payloads we were able to find during our research. For simplicity, we’ve combined all the processes described above into a single diagram illustrating all stages of the Necro Trojan.
It’s worth noting that the creators of Necro may regularly release new plugins and distribute them among infected devices, selectively or otherwise, for example, depending on the information about the infected application.
According to Google Play data, the infected applications could have been downloaded over 11 million times. However, the actual number of infected devices might be much higher, considering that the Trojan also infiltrated modified versions of popular apps distributed through unofficial sources.
KSN data shows that our security solutions blocked over ten thousand Necro attacks worldwide between August 26th and September 15th. Russia, Brazil, and Vietnam experienced the highest number of attacks. The chart below illustrates the distribution of Necro attacks across countries and territories where users most frequently encountered the Trojan.
Necro attacks by country and territory, August 26 through September 15, 2024 (download)
The Necro Trojan has once again managed to attack tens of thousands of devices worldwide. This new version is a multi-stage loader that used steganography to hide the second-stage payload, a very rare technique for mobile malware, as well as obfuscation to evade detection. The modular architecture gives the Trojan’s creators a wide range of options for both mass and targeted delivery of loader updates or new malicious modules depending on the infected application. To avoid being infected with this malware:
Applications infected with the loader
Application | Version | MD5 |
Wuta Camera | 6.3.6.148 | 1cab7668817f6401eb094a6c8488a90c |
6.3.5.148 | 30d69aae0bdda56d426759125a59ec23 | |
6.3.4.148 | 4c2bdfcc0791080d51ca82630213444d | |
6.3.2.148 | 4e9bf3e8173a6f3301ae97a3b728f6f1 | |
Max Browser | 1.2.4 | 28b8d997d268588125a1be32c91e2b92 |
1.2.3 | 52a2841c95cfc26887c5c06a29304c84 | |
1.2.2 | 247a0c5ca630b960d51e4524efb16051 | |
1.2.0 | b69a83a7857e57ba521b1499a0132336 | |
Spotify Plus (spotiplus[.]xyz) | 18.9.40.5 | acb7a06803e6de85986ac49e9c9f69f1 |
GBWhatsApp | 2.22.63.16 | 0898d1a6232699c7ee03dd5e58727ede |
FMWhatsApp | 20.65.08 | 1590d5d62a4d97f0b12b5899b9147aea |
Loader C2 server
oad1.bearsplay[.]com
shellPlugin versions
Second-stage payload
37404ff6ac229486a1de4b526dd9d9b6
Second-stage payload C2 server
oad1.azhituo[.]com
Plugins (third stage)
Plugin name | MD5 | Verdict |
NProxy | ed6c6924201bc779d45f35ccf2e463bb | Trojan.AndroidOS.Necro.g |
Cube | 874418d3d1a761875ebc0f60f9573746 cfa29649ae630a3564a20bf6fb47b928 |
Trojan.AndroidOS.Necro.j |
Island | b3ba3749237793d2c06eaaf5263533f2 | Trojan.AndroidOS.Necro.i |
Web/Lotus SDK | ccde06a19ef586e0124b120db9bf802e | Trojan.AndroidOS.Necro.d |
Happy SDK | 36ab434c54cce25d301f2a6f55241205 | Trojan-Downloader.AndroidOS.Necro.b |
Jar SDK | 1eaf43be379927e050126e5a7287eb98 | Trojan-Downloader.AndroidOS.Necro.b |
Tap | 522d2e2adedc3eb11eb9c4b864ca0c7f | Trojan.AndroidOS.Necro.l |
Plugin C2 servers
47.88.246[.]111
174.129.61[.]221
47.88.245[.]162
47.88.190[.]200
47.88.3[.]73
hsa.govsred[.]buzz
justbigso[.]com
bear-ad.oss-us-west-1.aliyuncs[.]com