[Информационная безопасность, Реверс-инжиниринг, Исследования и прогнозы в IT, IT-компании] Старый конь борозды не испортит: как стилер Pony крадет данные и где их потом искать

Автор Сообщение
news_bot ®

Стаж: 6 лет 9 месяцев
Сообщений: 27286

Создавать темы news_bot ® написал(а)
28-Янв-2021 14:32


Если помните, недавно у нас выходила статья про молодой, но уже подающий надежды data stealer Loki. Тогда мы подробно рассмотрели этот экземпляр (версия 1.8), получили представление о работе бота и освоили инструмент, облегчающий реагирование на события, связанные с этим ВПО. Для более полного понимания ситуации, давайте разберем еще одно шпионское ПО и сравним исследованных ботов. Сегодня мы обратим внимание на Pony — более старый, но не менее популярный образец data stealer’а. Никита Карпов, аналитик CERT-GIB, расскажет, как бот проникает на компьютер жертвы и как вычислить похищенные данные, когда заражение уже произошло. Разбор функциональности ботаВпервые Pony был замечен в 2011 году и все еще продолжает использоваться. Как и в ситуации с Loki, популярность этого ВПО обусловлена тем, что несколько версий бота вместе с панелью администратора можно без проблем найти в сети. Например, здесь.Экземпляр Pony, который мы будем изучать, защищен тем же самым упаковщиком, что и Loki, рассмотренный в предыдущей статье. По этой причине не будем еще раз останавливаться на процессе получения чистого ВПО и перейдем сразу к более интересным моментам. Единственное, что следует упомянуть перед разбором ВПО, — ссылка на сервер, по которой мы определяем нужный PE-файл, оканчивается на gate.php, и это один из индикаторов Pony.
При исследовании дизассемблированного кода Pony обратим внимание на участок, содержащий главные функции. Интерес представляют две из них — Initialize_Application и CnC_Func (названия функций переименованы в соответствии с их содержанием). 
Ниже представлена функция Initialize_Application. Она отвечает за инициализацию необходимых элементов (библиотеки, привилегии и т.д.) и за похищение данных. В процессе работы ВПО несколько раз использует значение 7227 — пароль к данному экземпляру бота. В Initialize_Application это значение используется для шифрования буфера, содержащего данные приложений, алгоритмом RC4.
Далее перейдем к декомпилированному коду функции CnC_Func и разберем ее алгоритм:
  • Буфер, полученный в результате работы функции Initialize_Application, передается в функцию BuildPacket, где собирается пакет данных для передачи на сервер.
  • По каждому URI из списка бот отправляет данные и ожидает подтверждения со стороны сервера. Если сервер не ответил 3 раза — бот идет дальше.
  • После завершения первого списка CnC бот пытается загрузить и запустить дополнительное ВПО.

В общем доступе находится готовый билдер, который подтверждает функционал, полученный в процессе статического анализа декомпилированного кода. Пользователю предлагается ввести список URI, куда будут выгружаться похищенные данные, и список, откуда будет выгружаться дополнительное ВПО. Также пользователь может изменить пароль бота и имя дополнительного ВПО.
Pony атакует более сотни приложений, и, хотя у него есть функционал загрузчика, в основном Pony используется именно для похищения пользовательских данных. В таблице ниже перечислены все приложения, из которых бот может похитить данные.ID ПриложениеID ПриложениеID Приложение0System Info45FTPGetter90Becky!1FAR Manager46ALFTP91Pocomail2Total Commander47Internet Explorer92IncrediMail3WS_FTP48Dreamweaver93The Bat!4CuteFTP49DeluxeFTP94Outlook5FlashFXP50Google Chrome95Thunderbird6FileZilla51Chromium / SRWare Iron96FastTrackFTP7FTP Commander52ChromePlus97Bitcoin8BulletProof FTP53Bromium (Yandex Chrome)98Electrum9SmartFTP54Nichrome99MultiBit10TurboFTP55Comodo Dragon100FTP Disk11FFFTP56RockMelt101Litecoin12CoffeeCup FTP / Sitemapper57K-Meleon102Namecoin13CoreFTP58Epic103Terracoin14FTP Explorer59Staff-FTP104Bitcoin Armory15Frigate3 FTP60AceFTP105PPCoin (Peercoin)16SecureFX61Global Downloader106Primecoin17UltraFXP62FreshFTP107Feathercoin18FTPRush63BlazeFTP108NovaCoin19WebSitePublisher64NETFile109Freicoin20BitKinex65GoFTP110Devcoin21ExpanDrive663D-FTP111Frankocoin22ClassicFTP67Easy FTP112ProtoShares23Fling68Xftp113MegaCoin24SoftX69RDP114Quarkcoin25Directory Opus70FTP Now115Worldcoin26FreeFTP / DirectFTP71Robo-FTP116Infinitecoin27LeapFTP72Certificate117Ixcoin28WinSCP73LinasFTP118Anoncoin2932bit FTP74Cyberduck119BBQcoin30NetDrive75Putty120Digitalcoin31WebDrive76Notepad++121Mincoin32FTP Control77CoffeeCup Visual Site Designer122Goldcoin33Opera78FTPShell123Yacoin34WiseFTP79FTPInfo124Zetacoin35FTP Voyager80NexusFile125Fastcoin36Firefox81FastStone Browser126I0coin37FireFTP82CoolNovo127Tagcoin38SeaMonkey83WinZip128Bytecoin39Flock84Yandex.Internet / Ya.Browser129Florincoin40Mozilla85MyFTP130Phoenixcoin41LeechFTP86sherrod FTP131Luckycoin42Odin Secure FTP Expert87NovaFTP132Craftcoin43WinFTP88Windows Mail133Junkcoin44FTP Surfer89Windows Live MailВзаимодействие с серверомРассмотрим подробнее сетевое взаимодействие Pony. Как мы уже говорили, Pony сначала выгружает похищенные данные приложений на удаленный сервер, и индикатором такой коммуникации служит gate.php. После этого Pony просматривает второй список ссылок, откуда он пытается загрузить дополнительное ВПО на зараженный компьютер.
Для подтверждения того, что сервер получил и прочитал данные, бот должен получить в ответ строку STATUS-IMPORT-OK, иначе бот считает, что сервер не получил данные.
Данные, передаваемые на сервер, надежно защищаются шифрованием и компрессией. Защиту данных определяет заголовок, который идет перед ними. Стандартная защита пакета выглядит так:
  • Данные в чистом виде с заголовком PWDFILE0.
  • Сжатые данные с заголовком PKDFILE0. Для сжатия используется библиотека aPLib, работа которой основана на алгоритме компрессии LZW.
  • Зашифрованные данные с заголовком CRYPTED0 и ключом в виде пароля, например, 7227 или PA$$. Для шифрования используется алгоритм RC4.
  • Зашифрованные алгоритмом RC4 данные, ключ указан в первых 4 байтах.
РазмерЗначениеОписание0x4rc_4keyКлюч для верхнего уровня шифрования0x12REPORT_HEADER(PWDFILE0/ PKDFILE0/ CRYPTED0)Заголовок отчета о похищенных данных(normal/packed/crypted)8 байт — заголовок, и 4 байта — контрольная сумма CRC320x4Версия отчетаВерсия отчета о похищенных данных(константное значение 01.0)0x4Размер модуляЗаголовок модуля, присутствует у каждого модуля  0x8ID заголовка модуля(chr(2).chr(0)."MODU".chr(1).chr(1))2 байта, ключевое слово MODU, 1 байт, 1 байт0x2ID модуля0x2Версия модуля-Название системы пользователяМодуль “module_systeminfo” (module id = 0x00000000)Содержит информацию о системе пользователя0x2Система x32 или x64 -Страна пользователя-Язык системы пользователя0x2Является ли пользователь администратором-Значение MachineGuid из приложения WinRAR-Список модулей всех приложенийПо аналогии с модулем “module_systeminfo” записаны данные всех приложенийПарсер сетевых коммуникацийКак и для Loki, напишем парсер на Python, используя следующие библиотеки:
  • Dpkt для поиска пакетов, принадлежащих Pony, и работы с ними.
  • aPLib для декомпрессии данных.
  • Hexdump для представления данных пакета в хексе.
  • JSON для записи найденной информации в удобном виде.
Рассмотрим основные части алгоритма работы скрипта:
for ts, buf in pcap:
    eth = dpkt.ethernet.Ethernet(buf)
    if not isinstance(eth.data, dpkt.ip.IP):
        ip = dpkt.ip.IP(buf)
    else:
        ip = eth.data
    if isinstance(ip.data, dpkt.tcp.TCP):
        tcp = ip.data
        try:
            if tcp.dport == 80 and len(tcp.data) > 0:  # HTTP REQUEST
                if str(tcp.data).find('POST') != -1:
                    http += 1
                    httpheader = tcp.data
                    continue
                else:
                    if httpheader != "":
                        pkt = httpheader + tcp.data
                        req += 1
                        request = dpkt.http.Request(pkt)
                        parsed_payload['Network'].update({'Request method': request.method})
                        uri = request.headers['host'] + request.uri
                        parsed_payload['Network'].update({'CnC': uri})
                        parsed_payload['Network'].update({'User-agent': request.headers['user-agent']})
                        if uri.find("gate.php") != -1:
                            parsed_payload['Network'].update({'Traffic Purpose': "Exfiltrate Stolen Data"})
                            parse(tcp.data, debug)
                        elif uri.find(".exe") != -1:
                            parsed_payload['Network'].update({'Traffic Purpose': "Download additional malware"})
                        print(json.dumps(parsed_payload, ensure_ascii=False, sort_keys=False, indent=4))
                        parsed_payload['Network'].clear()
                        parsed_payload['Malware Artifacts/IOCs'].clear()
                        parsed_payload['Compromised Host/User Data'].clear()
                        parsed_payload['Applications'].clear()
                        print("----------------------")
            if tcp.sport == 80 and len(tcp.data) > 0:  # HTTP RESPONCE
                resp += 1
                response = dpkt.http.Response(tcp.data)
                if response.body.find(b'STATUS-IMPORT-OK') != -1:
                    AdMalw = True
                    print('Data imported successfully')
                else:
                    print('C2 did not receive data')
                print("----------------------")
        except(dpkt.dpkt.NeedData, dpkt.dpkt.UnpackError):
            continue
print("Requests: " + str(req))
print("Responces: " + str(resp))
Поиск пакетов, связанных с Pony, аналогичен поиску пакетов Loki. Ищем все HTTP-пакеты. Парсим запросы, в которых находится информация бота. Остальные запросы фиксируются, но данные в них не обрабатываются. Если в ответ на запрос получена строка STATUS-IMPORY-OK — отмечаем успешную выгрузку данных. Во всех других случаях считаем, что сервер не получил данные. Если после выгрузки данных найдены HTTP-запросы с URI, оканчивающимся на .exe — отмечаем загрузку дополнительного ВПО.Рассмотрим функцию, отвечающую за снятие всей защиты с данных и импорт модулей:
def process_report_data(data, debug):
    index = 0
    if len(str(data)) == 0:
        return False
    elif len(str(data)) < 12:
        return False
    elif len(str(data)) > REPORT_LEN_LIMIT:
        return False
    elif len(str(data)) == 12:
        return True
    if verify_new_file_header(data):
        rand_decrypt(data)
    report_id = read_strlen(data, index, 8)
    index += 8
    if report_id == REPORT_CRYPTED_HEADER:
        parsed_payload['Malware Artifacts/IOCs'].update({'Crypted': report_id.decode('utf-8')})
        decrypted_data = rc4DecryptText(report_password, data[index:len(str(data))])
        data = decrypted_data
        index = 0
        report_id = read_strlen(data, index, 8)
        index += 8
    if report_id == REPORT_PACKED_HEADER:
        parsed_payload['Malware Artifacts/IOCs'].update({'Packed': report_id.decode('utf-8')})
        unpacked_len = read_dword(data, index)
        index += 4
        leng = read_dword(data, index)
        index += 4
        if leng < 0:
            return False
        if not leng:
            return ""
        if index + leng > len(str(data)):
            return False
        packed_data = data[index:index + leng]
        index += leng
        if unpacked_len > REPORT_LEN_LIMIT or len(str(packed_data)) > REPORT_LEN_LIMIT:
            return False
        if not len(str(packed_data)):
            return False
        if len(str(packed_data)):
            data = unpack_stream(packed_data, unpacked_len)
        if not len(str(data)):
            return False
        if len(str(data)) > REPORT_LEN_LIMIT:
            return False
        index = 0
        report_id = read_strlen(data, index, 8)
        index += 8
    if report_id != REPORT_HEADER:
        print("No header")
        return False
    version_id = read_strlen(data, index, 3)
    index += 8
    if version_id != REPORT_VERSION:
        return False
    parsed_payload['Malware Artifacts/IOCs'].update({'Data version': version_id.decode('utf-8')})
    hexdump.hexdump(data)
    report_version_id = version_id
    parsed_payload['Applications'].update({'Quantity': 0})
    while index < len(data):
        index = import_module(data, index, debug)
    return data
После снятия обязательного шифрования, определяем метод снятия следующего уровня защиты — в зависимости от заголовка. Если присутствует дополнительное шифрование с заголовком CRYPTED0 — скрипт пытается подставить стандартный ключ, и при несоответствия ключа запрашивает файл ВПО, в котором находит используемый в этом боте пароль. Если заголовок данных PWDFILE0 — начинаем импорт модулей приложений.Для расшифровки мы использовали алгоритм RC4:
def rc4DecryptHex(key, pt):
    if key == '':
        return pt
    s = list(range(256))
    j = 0
    for i in range(256):
        j = (j + s[i] + key[i % len(key)]) % 256
        s[i], s[j] = s[j], s[i]
    i = j = 0
    ct = []
    for char in pt:
        i = (i + 1) % 256
        j = (j + s[i]) % 256
        s[i], s[j] = s[j], s[i]
        ct.append(chr(char ^ s[(s[i] + s[j]) % 256]))
    decrypted_text = ''.join(ct)
    data = decrypted_text.encode('raw_unicode_escape')
    return data
Результат работы парсера представлен ниже. Парсер успешно снял шифрование, произвел декомпрессию и нашел похищенные данные. Следует отметить, что у каждого модуля есть несколько типов представления данных, в зависимости от найденной ботом информации. В нашем примере бот похитил данные Outlook и записал их с типом 7. На первый запрос сервер ответил боту, а остальные коммуникации не несли полезной информации.
В заключение давайте сравним исследованные data stealer'ы Pony и Loki и подведем итог. Список атакуемых приложений и у Pony, и у Loki примерно одинаков, но функционал Loki, особенно в новых версиях, шире, чем у Pony. Pony защищает все передаваемые данные в несколько уровней, что не дает определить без специального инструмента, какие именно данные похитил бот. Loki, в свою очередь, передает все данные в открытом виде, но без знания структуры запросов разобрать эти данные тоже довольно сложно. Надеемся, эти две статьи помогли разобраться, какую опасность несут данные data stealer’ы и как можно упростить реагирование на инциденты с помощью реализованных нами инструментов.
===========
Источник:
habr.com
===========

Похожие новости: Теги для поиска: #_informatsionnaja_bezopasnost (Информационная безопасность), #_reversinzhiniring (Реверс-инжиниринг), #_issledovanija_i_prognozy_v_it (Исследования и прогнозы в IT), #_itkompanii (IT-компании), #_data_stealer, #_stiler (стилер), #_vpo (впо), #_reversinzhiniring (реверс-инжиниринг), #_informatsionnaja_bezopasnost (информационная безопасность), #_cybersecurity, #_cybercrime, #_kompjuternaja_bezopasnost (компьютерная безопасность), #_kompjuternaja_kriminalistika (компьютерная криминалистика), #_analiz_vredonosov (анализ вредоносов), #_blog_kompanii_groupib (
Блог компании Group-IB
)
, #_informatsionnaja_bezopasnost (
Информационная безопасность
)
, #_reversinzhiniring (
Реверс-инжиниринг
)
, #_issledovanija_i_prognozy_v_it (
Исследования и прогнозы в IT
)
, #_itkompanii (
IT-компании
)
Профиль  ЛС 
Показать сообщения:     

Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы

Текущее время: 22-Ноя 18:18
Часовой пояс: UTC + 5