Бэкдор на Node.js: зачем, почему и как это работает

    Недавно коллеги из Яндекса поделились с нами сэмплом интересного троянца, о чем мы сообщили в этой новости. Такая малварь попадается не часто, поэтому мы решили подробнее ее разобрать, а заодно поговорить о том, почему мы так редко встречаем подобные сэмплы.

    Троянец представляет собой многокомпонентный бэкдор, написанный на JavaScript’е и использующий для запуска Node.js. Основные его элементы – worker и updater, которые загружаются и устанавливаются в систему загрузчиком. Полезная нагрузка может быть любой, но в данном случае троянец устанавливает майнер xmrig. На момент проведения исследования разработчик использовал майнера для добычи криптовалюты TurtleCoin.

    MonsterInstall распространяется через сайты с читами к популярным видеоиграм. Большинство таких ресурсов принадлежат разработчику троянца, но мы нашли еще несколько зараженных файлов на других подобных сайтах. Владелец одного из них регулярно следит за обновлениями конкурентов и пополняет свой ресурс свежим контентом. Для этого он использует скрипт parser.php, который через прокси ищет новые читы на cheathappens.com.

    Proxy parse done, total: 1
    Use sox 84.228.64.133:1080
    Error: CURL error(#52), attempts left: 10
    Use sox 84.228.64.133:1080
    Posts found: 30!
    [33mPage Satisfactory: Трейнер +8 vCL#96731 {CheatHappens.com} already in base[0m
    [33mPage Borderlands: The Pre-Sequel - Трейнер +28 v1.2019 {LinGon} already in base[0m
    [33mPage Borderlands - Game of the Year Enhanced: Трейнер +19 v1.0.1 {LinGon} already in base[0m
    [33mPage Star Wars: Battlefront 2 (2017): Трейнер +4 v01.04.2019 {MrAntiFun} already in base[0m
    [36mPage Far Cry 5: Трейнер +23 v1.012 (+LOST ON MARS/DEAD LIVING ZOMBIES) {CheatHappens.com} added 2019-Apr-09[0m
    [36mPage Fate/Extella Link: Трейнер +13 v04.09.2019 {CheatHappens.com} added 2019-Apr-09[0m
    [36mPage Superhot: Трейнер +3 v2.1.01p { MrAntiFun} added 2019-Apr-09[0m
    [36mPage Dawn of Man: Трейнер +7 v1.0.6 {CheatHappens.com} added 2019-Apr-08[0m
    [36mPage Borderlands 2: Трейнер +14 v06.04.2019 {MrAntiFun} added 2019-Apr-08[0m
    [36mPage Borderlands: The Pre-Sequel - Трейнер +17 v06.04.2019 {MrAntiFun} added 2019-Apr-08[0m
    [36mPage Tropico 6: Трейнер +9 v1.01 {MrAntiFun} added 2019-Apr-08[0m
    [36mPage Operencia: The Stolen Sun - Трейнер +20 v1.2.2 {CheatHappens.com} added 2019-Apr-08[0m
    [36mPage Enter the Gungeon: Трейнер +6 v2.1.3 {MrAntiFun} added 2019-Apr-07[0m
    [36mPage The Guild 3: Трейнер +2 v0.7.5 {MrAntiFun} added 2019-Apr-07[0m
    [36mPage Dead Effect 2: Трейнер +8 v190401 {MrAntiFun} added 2019-Apr-07[0m
    [36mPage Assassin's Creed: Odyssey - Трейнер +26 v1.2.0 {FLiNG} added 2019-Apr-07[0m
    [36mPage Assassin's Creed: Odyssey - Трейнер +12 v1.2.0 {MrAntiFun} added 2019-Apr-06[0m
    [36mPage Super Dragon Ball Heroes: World Mission - Трейнер +11 v1.0 {FLiNG} added 2019-Apr-05[0m
    [36mPage Tropico 6: Трейнер +7 v1.02 97490 {CheatHappens.com} added 2019-Apr-05[0m
    [36mPage Risk of Rain 2: Трейнер +10 Build 3703355 {CheatHappens.com} added 2019-Apr-05[0m
    [36mPage Sid Meier's Civilization 6 - Rise and Fall: Трейнер +12 v1.0.0.314 {MrAntiFun} added 2019-Apr-05[0m
    [36mPage Sid Meier's Civilization 6 - Gathering Storm: Трейнер +12 v1.0.0.314 {MrAntiFun} added 2019-Apr-05[0m
    [36mPage Sid Meier's Civilization 6: Трейнер +12 v1.0.0.314 {MrAntiFun} added 2019-Apr-05[0m
    [36mPage Borderlands GOTY Enhanced: Трейнер +16 v1.0 {CheatHappens.com} added 2019-Apr-05[0m
    [36mPage Borderlands Game of the Year Enhanced: Трейнер +13 v1.00 {MrAntiFun} added 2019-Apr-04[0m
    [36mPage Assassin's Creed: Odyssey: Трейнер +24 v1.2.0 (04.04.2019) {CheatHappens.com} added 2019-Apr-04[0m
    [36mPage Sekiro: Shadows Die Twice - Трейнер +24 v1.02 {FLiNG} added 2019-Apr-04[0m
    [36mPage Hearts of Iron 4: Трейнер +24 v1.6.2 {MrAntiFun} added 2019-Apr-04[0m
    [36mPage Wolcen: Lords of Mayhem - Трейнер +5 v1.0.2.1 {MrAntiFun} added 2019-Apr-04[0m
    [36mPage Devil May Cry 5: Трейнер +18 v1.0 (04.03.2019) {CheatHappens.com} added 2019-Apr-04[0m
    Parse done
    


    На сайтах разработчика – большой выбор читов, но по всем ссылкам будет отдаваться один и тот же архив. При попытке скачать любой из файлов с сайта малварщика пользователь получит Trojan.MonsterInstall. О некоторых параметрах троянца можно догадаться по ссылке на скачивание:

    https://<malicious_site>/fc/download.php?name=Borderlands%20GOTY%20Enhanced:%20%D0%A2%D1%80%D0%B5%D0%B9%D0%BD%D0%B5%D1%80%20+16%20v1.0%20{CheatHappens.com}&link=https://<malicious_site>//r.php?site=http://gtrainers.com/load/0-0-1-7081-20&password=<malicious_site>/&uid=101&sid1=1&sid2=1&charset=utf-8
    

    • name – имя архива и ехе в архиве;
    • link – ссылка на файл, который пользователь хотел скачать (зашивается в data.json);
    • password – пароль на архив.

    Допустим, мы выбрали нужный чит и скачали с сайта разработчика троянца защищенный паролем 7zip-архив с многообещающим названием “ExtrimHack.rar”. Внутри него – исполняемый файл, конфигурационный файл, библиотека 7zip, а также bin-архив с нативными библиотеками С++ и скриптами, запускаемыми с помощью бинаря Node.js.

    Пример содержимого архива:

    • 7z.dll;
    • data.bin;
    • data.json;
    • ESP чит для КС ГО.exe.


    При запуске исполняемого файла троянец установит все необходимые для его работы компоненты, а также скачает нужный пользователю чит, используя информацию из data.json-файла с параметрами.

    Пример содержимого data.json:
    {"source":[5,10,11,43],"dataVersion":[0,0,0,115],"link":"http:\/\/clearcheats.ru\/images\/dl\/csgo\/ESP_csgo.dll"}
    


    Чтобы исключить работу нескольких копий своего процесса, троянец создает мьютекс «cortelMoney-suncMutex» и устанавливается в директорию "%WINDIR%\WinKit\". Затем проверяет, есть ли он в реестре ([HKLM\\Software\\Microsoft\\Windows Node]). Если да, то читает свои параметры и сравнивает версию с той, что указана в data.json. Если версия актуальная, ничего дальше не делает и завершается.

    После этого троянец распаковывает содержимое data.bin в %WINDIR%\\WinKit\\ и устанавливает службу для запуска start.js.

    Содержимое data.bin:
    • Daemon;
    • node_modules;
    • 7z.dll;
    • msnode.exe;
    • start.js;
    • startDll.dll;
    • update.js;
    • updateDll.dll.

    При этом msnode.exe – это исполняемый файл Node.js с действительной цифровой подписью, а каталог node_modules содержит библиотеки «ffi», «node-windows» и «ref».

    В start.js подгружается библиотека startDll.dll и вызывается ее экспорт mymain, в котором она читает из реестра свои параметры, запускает "%WINDIR%\\WinKit\\msnode %WINDIR%\\WinKit\\update.js" и останавливает службу «Windows Node». Скрипт update.js, в свою очередь, подгружает библиотеку updateDll.dll и вызывает ее экспорт mymain. Ничего сложного.

    В updateDll.dll троянец начнет проверять подключение к интернету. Для этого он каждые 10 секунд будет отправлять запросы на google.com, yahoo.com, facebook.com, пока все трое не вернут 200 код. Затем отправит на сервер s44571fu[.]bget[.]ru/CortelMoney/enter.php POST запрос с конфигурационными данными:

    {"login":"NULL","mainId":"PPrn1DXeGvUtzXC7jna2oqdO2m?WUMzHAoM8hHQF","password":"NULL","source":[0,0,0,0],"updaterVersion":[0,0,0,0],"workerVersion":[0,0,0,0]}


    При этом для базовой авторизации используется пара «cortel:money», а User-Agent выставлен «USER AGENT». Для базовой авторизации последующих запросов будут использоваться login:password, которые сообщит сервер.

    Сервер отвечает json-ом следующего вида:

    {
        "login": "240797",
        "password": "tdzjIF?JgEG5NOofJO6YrEPQcw2TJ7y4xPxqcz?X",
        "updaterVersion": [0, 0, 0, 115],
        "updaterLink": "http:\/\/s44571fu.bget.ru\/CortelMoney\/version\/0-0-0-115-upd.7z",
        "workerVersion": [0, 0, 3, 0],
        "workerLink": "http:\/\/s44571fu.bget.ru\/CortelMoney\/version\/0-0-3-0-work.7z"
    }


    Как видим, в ответе сервера указаны версии основных элементов троянца. Если текущая версия updater’а на устройстве пользователя старше той, что сообщил сервер, троянец скачивает файл по указанной ссылке и распаковывает архив в директорию "%WINDIR%\\WinKit\\\\", где вместо будет указано значение параметра updaterVersion из ответа сервера.

    Троянец распаковывает worker файл в директорию %WINDIR%\\WinKit\\SystemNode\\, после чего запускает "%WINDIR%\\WinKit\\SystemNode\\sysnode %WINDIR%\\WinKit\\SystemNode\\main.js".

    Содержимое архива с worker’ом:
    • node_modules;
    • 7za.exe;
    • codex;
    • main.js;
    • sysnode.exe.

    Затем троянец удаляет службу «Windows Node Guard», после чего создает ее заново, заменив исполняемый файл на файл службы «Windows Node». Таким же образом он пересоздает службу «Windows Node» с заменой исполняемого файла на daemon\\service.exe.

    Рядом формирует service.xml с параметрами:
    <service><id>service.exe</id><executable>C:\Windows\\WinKit\0.0.0.115\msnode.exe</executable><arguments>"C:\Windows\\WinKit\0.0.0.115\start.js"</arguments></service>
    


    Updater устанавливается в директорию «C:\Windows\Reserve Service», прописывается службой и запускается бинарником Node.js. Он также состоит из нескольких js-скриптов и нативных библиотек на С++. Основным модулем является main.js.

    Содержимое архива с updater’а:
    • main.js;
    • start.js;
    • crypto.dll;
    • network.dll;
    • service.exe.

    Первым делом троянец узнает текущую дату, отправив запрос к google.com, yandex.ru или www.i.ua. Полученную информацию он использует чуть позже. Затем расшифровывает с помощью библиотеки crypto.dll содержимое файла bootList.json.

    Алгоритм дешифровки:
    key = '123'
    s = ''
    for i in range(len(d)):
        s += chr((ord(d[i]) - ord(key[i % len(key)])) & 0xff)
    


    Получает оттуда список управляющих серверов:
    [{"node":"http://cortel8x.beget.tech/reserve","weight":10},{"node":"http://reserve-system.ru/work","weight":10}]


    Затем троянец читает информацию из реестра:
    function getInfo()
    {
    var WindowsNodeInfo = new Object();
    WindowsNodeInfo.mainId = windowsLib.getStringRegKey("HLM\\SOFTWARE\\Microsoft\\Windows Node", "mainId");
    WindowsNodeInfo.login = windowsLib.getStringRegKey("HLM\\SOFTWARE\\Microsoft\\Windows Node", "log");
    WindowsNodeInfo.password = windowsLib.getStringRegKey("HLM\\SOFTWARE\\Microsoft\\Windows Node", "pass");
    WindowsNodeInfo.source = windowsLib.getStringRegKey("HLM\\SOFTWARE\\Microsoft\\Windows Node", "source");
    WindowsNodeInfo.updaterVersion = windowsLib.getStringRegKey("HLM\\SOFTWARE\\Microsoft\\Windows Node", "updaterVersion");
    WindowsNodeInfo.workerVersion = windowsLib.getStringRegKey("HLM\\SOFTWARE\\Microsoft\\Windows Node", "workerVersion");
    var ReserveSystemInfo = new Object();
    ReserveSystemInfo.workerVersion = windowsLib.getStringRegKey("HLM\\SOFTWARE\\Microsoft\\Reserve System", "updaterVersion");
    var myInfo = new Object();
    myInfo.windowsNode = WindowsNodeInfo;
    myInfo.reserveSystem = ReserveSystemInfo;
    return JSON.stringify(myInfo);
    }
    

    После чего добавляет HTTP-заголовок базовой авторизации, соответствующий паре «cortel:money», и отправляет ее POST-запросом на ранее расшифрованный управляющий сервер.
    В ответ от сервера получает:

    
    {
        "data": {
            "updaterVersion": [0, 0, 0, 1],
            "updaterLink": "/upd.7z",
            "updaterVerify": "£ñß(\u0012Ä\ti¾$ë5ž»\u001c²\u001c\fÙ=±÷ö‚´èUnŽÐÂBÔ\n\u001dW6?u½\u0005Œn\u000fp:üÍ\u0019\u0000\u000bSý«\u00137÷\u0013”’ì¥û§s7F\u0016ó\\\u000f%6ñê\"7î<ýo䃃0Æ%tñÅv­‚S¡\r\u001e•ÅÆ¡¿N)v\\f8\u0004F\fUS¯‰³§ oIõŒiÆîGݪ\u0017êH/8Ö1-°™[P5E7X‡Fø%SŠXÕ6Oþ=Vô‰…ˆ:.3Œ‚i\u000eÁù9Ã&¾ŒM\u001eÛªé$\u0006#IèÞÛ\u0018À\u001b^è,ÁòÑCTXb\u001d$ç\u0004„ð¶0UVÕ»e\u001f\b\u001e¡Ä¼è+Fjúÿoâz\r!çô3xØs—_\u000b\u0017\u001fY]\u0001¥j^û\\W",
            "dateTime": 1534868028000,
            "bootList": [{
                "node": "http://cortel8x.beget.tech/reserve/",
                "weight": 10
            }, {
                "node": "http://reserve-system.ru/work/",
                "weight": 10
            }]
        },
        "dataInfo": "I`ù@ÀP‘ÈcÊÛ´#ièÒ~\u0007<\u0001Ýìûl«ÔÆq\u0013àÛ\u0003\b\u0017ÑLÁ}ÿژDS']\u0003bf\u0003!¿Cð¸q¸ÖÜ’B¢CÄAMˆÀA¤d\u001c5¨d-\u0013‰\u0011ѼF‘|SB[¬°(ܹÈÒÜ £L\u00071¾:`\u001bŒìýKõ\"²Ÿ¸$´3™UºÅ¨J†¨cƒf¿}r;Öeì¶x‰ØKt¥‹„47a\u001e¸Ô‡ˆy\u0006•\u001b\u0004‚‹„„•ó\u001a\u0019\nu>¨)bkæ…'\u00127@é‹7µæy3ÈNrS’Mð‡\u0018\u0019¾òÓ[Žå5H‡ƒ·¦k‘¿ÉŠ&PÂÈîåÚ~M\u0010ðnáH擪xÃv cד\u0013…T…ïÑÝ\tœŽ\u0018†Æ\u00148$”Ôî"
    }
    


    И тут пригодится текущая дата, полученная ранее. Троянец сверяет ее с dataTime-параметром, переданным сервером. Если разница между датами больше недели (в пересчете на миллисекунды), троянец не станет выполнять команды. Также в параметре dataInfo содержится подпись данных (поле data), она проверяется с помощью зашитого в main.js публичного ключа. А в параметре «bootList» содержится список серверов, который троянец шифрует и сохраняет в «bootList.json».

    После этого троянец сверяет свою версию с той, что указана в параметре «updaterVersion». Если установленная версия не ниже последней актуальной, троянец запускает «upd\\upd.exe», передав параметром «main.js». Если версия из ответа сервера новее, то троянец скачивает по ссылке из параметра «updaterLink» upd.7z архив с обновлением, проверяет его подпись и распаковывает. Затем записывает в реестр версию обновления [HKLM\\SOFTWARE\\Microsoft\\Reserve System] 'updaterVersion', после чего опять же запускает «upd\\upd.exe», передав параметром «main.js».

    Worker троянца проверяет, что из компонентов уже установлено, и принимает решения о необходимости установки приложений. Первым делом он создает мьютекс «MoonTitleWorker», затем расшифровывает файл codeX XOR-ом со строкой «xor» и выполняет его. После этого формирует json с информацией:
    
    {"userId": id, "starter": [], "worker": [], "source": [], "osInfo": {"isX64": True, "osString": "Windows 7 Enterprise"}}
    

    Отправляет эту информацию POST-запросом на http://<malicious_site>[.]xyz:1001/getApps (ради приличия мы не указываем доменное имя, но его можно узнать здесь.) Ответ сервера может содержать информацию о приложениях, которые нужно установить.

    Пример ответа:
    
    {
        "body": {
            "apps": [{
                "hash": "452f8e156c5c3206b46ea0fe61a47b247251f24d60bdba31c41298cd5e8eba9a",
                "size": 8137466,
                "version": [2, 0, 0, 2],
                "link": "xmr-1-64.7z",
                "path": "%pf%\\Microsoft JDX\\64",
                "runComand": "%path%\\moonlight.exe start.js",
                "name": "xmr64"
            }]
        },
        "head": "O~¨^Óå+ßzIçsG¬üS„ʶ$êL–LùθZ\f\u0019ÐÐ\u000e\u0004\u001cÀU¯\u0011š)áUÚ\u001flß²A\u001fôÝÔ숱y%\"DP»^¯«FUâ\u001cÔû\u001dµ´Jï#¬ÌȹÎÚª?\r—]Yj·÷õ³—\u001e°ÖÒ\\鉤d’BT\u0019·¦FõVQ°Aç’)\u001cõªµ¦ýûHlb͸þ}éŒ\u0000jvÔ%S;Ã×þA\u0011ߑI[´\u0004ýÚ\u0007Z:ZÂ\n–ñz#ÈBö›²2\u0007ÎJw±èTVoŸå\bÖR3½ù;ƒó\u0011É̀ÅÖàð06ÓeÕþ­ˆ”7ٚ\u0011•»”˜¢5µgôÛc˜&L\u000fê.?!Çæ}¨\u001e՗J#A¼_Ì\u0015càñb"
    }
    

    Если на устройстве нет такого приложения, троянец скачивает его, отправив POST-запрос на URL http://<malicious_site>[.]xyz:1001/, где вместо будет указано значение параметра «link» для соответствующего приложения из ответа сервера. Если такое приложение есть, но более старой версии, оно обновляется до актуальной.

    Троянец сверяет размер и хэш скачанного файла с информацией, указанной сервером в параметрах «hash» и «size». Если данные сходятся, он перемещает файл по пути из параметра «path» и запускает команду, указанную в поле «runCommand». Информация о скачанном приложении сохраняется в реестре [HKLM\\SOFTWARE\\Microsoft\\MoonTitle\\apps\\].

    На данный момент с помощью бэкдора ставится майнер xmrig. В зависимости от разрядности системы троянец скачивает xmr-1.7z или xmr-1-64.7z архив. В start.js подгружает библиотеку xmrig.dll и вызывает экспорт mymain, где она разворачивает свои переменные окружения и убивает процессы:
    • %sys32_86%\\xmr;
    • %sys32_86%\\xmr64;
    • %pf_86%\\Microsoft JDX\\32\\windows-update.exe;
    • %pf_86%\\Microsoft JDX\\64\\windows-update.exe.

    Если рядом находится файл xmrig.exe, троянец загружает его в память текущего процесса, стирает сигнатуру MZ, расшифровывает его с использованием XOR с 0x39, после чего сохраняет его дамп в файле «dump ». Если троянец находит в той же директории файл «dump», то расшифровывает его таким же образом, запускает windows-update.exe и встраивает в него расшифрованную полезную нагрузку.

    Троянец собирает и отправляет POST-запросом на URL: cherry-pot[.]top/RemoteApps/xmr/main.php следующую информацию о системе: {«action»:«enter»,«architecture»:«INTEL»,«cpuAES»:true,«cpuCache»:8192,«cpuSpeed»:3392.0,«cpuThreads»:2,«cpuVendorString»:" Intel® Core(TM) i5-4690S CPU @ 3.20GHz\u0000",«hightPages»:false,«login»:«null»,«password»:«null»,«ramPhysicalSize»:3071,«xmrigVersion»:[2,10,0]}

    В ответ сервер присылает конфигурацию майнера:
    {"maxCpuLoad":1000,"minCpuLoad ":0,"algo":"cryptonight-pico/trtl","av":0,"background":false,"donate-level":1,"max-cpu-usage":75,"retries":5,"retry-pause":5,"cpu-priority":1,"pools":[{"url":"185.224.133.91:5511","keepalive":true,"nicehash":true}]}

    После того как троянец сохранит конфигурацию в config.json, он автоматически запустится и начнет майнить.

    У MonsterInstall есть и другие модификации. К примеру, кроме читов для игр разработчик малвари распространял его под видом установщика браузера Chrome и программы для проверки файлов. В более поздних версиях троянца разработчик задумался о безопасности и добавил шифрование строк, а также необходимость ввода пароля для некоторых файлов. Кроме того, в загрузчике одной из версий троянца даже есть ссылка на лицензионное соглашение, размещенное на домене разработчика троянца.

    (Вопросы о юридической силе подобных соглашений, к сожалению, выходят за рамки этой статьи, но если вам было бы интересно почитать материал на эту тему, сообщите нам в комментариях).

    Выводы

    Node.js – не самое практичное решение для вирусописателей. Если размер такого троянца и может быть небольшим, то обвязка Node.js (исполняемый файл и библиотеки) будет значительно «тяжелее» стандартной малвари. Чем же продиктован такой выбор? Как правило, разработчики выбирают инструменты, с которыми они хорошо знакомы. Поэтому даже в случае с малварщиками выбор технологии – в большей степени вопрос личных предпочтений. Тем не менее, у Node.js есть свои плюсы, одним из которых является валидная подпись. В системе такой процесс будет подписан как Node.js, что редко вызывает подозрения.

    Подводя итоги, можно отметить, что, несмотря на интересный выбор инструментов, это не дало разработчику бэкдора какого-то значительного преимущества. Маловероятно, что в будущем мы увидим больше вредоносных программ, использующих Node.js.

    Как обычно, делимся индикаторами компрометации.
    Доктор Веб
    77,94
    Компания
    Поделиться публикацией

    Комментарии 3

      +1

      "Node.js – не самое практичное решение для вирусописателей. Если размер такого троянца и может быть небольшим, то обвязка Node.js (исполняемый файл и библиотеки) будет значительно «тяжелее» стандартной малвари."
      А обязательно прям все с собой нести? Если люди у которых стоит Nodejs целевая аудитория. То у них уже и библиотеки должны быть установлены.

        0

        А если вдруг вирь окажется привязан к некоей версии одной из использованных либ, тут разраб обновится и вирь всплывет? Нелогично, если уж таскать с собой ноду, то всю какую надо.
        Кроме того, майнеру пофиг на ЦА, ему нужны мощности процессора.

        +1
        Вообще, было бы интересно почитать о лицензионных соглашениях на подобное. Хотя бы мельком.

        Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

        Самое читаемое