Monday, November 4, 2019

NFT

Threat hunting is the use case for EDR
(Nate Fick, CEO Endgame.)

Более чем 3 года назад, в 2016, мы впервые заговорили о Threat hunting на русском языке, позже появились и другие публикации, сейчас об этом говорят уже все, а прошлые SOC-и называют поставщиками сервисов MDR. Уже ни для кого не новость MITRE ATT&CK и многие производители, пытающиеся систематически подходить к тому самому Threat hunting, (далее - "активный поиск угроз" или "хантинг") уже прошли тест, и многие еще пройдут.

Однако, несмотря на эпиграф к этой статье и достаточно хорошее покрытие задачи со стороны EPP-EDR, надо все же стремиться к более комплексной истории, и заниматься активным поиском угроз не только на эндпоинте, но и на сети, о чем и поговорим в этой заметке.

Почему активный поиск угроз на сети важен? На слайде 27 я приводил тактики, с указанием эффективности сенсоров. Нетрудно сообразить, что сетевой активный поиск угроз будет здорово работать на:
и абсолютно незаменим на:
Более того, с точки зрения результативности, разумным подходом является использование принципа "кругового детекта", когда одна угроза обкладывается множеством различных детектирующих логик, работающих по разным принципам, таким образом, выпустив множество детектов - по сэмплу, по поведению, по похожести, по сетевому трафику и т.п. - мы увеличиваем вероятность успешного обнаружения (или предотвращения, если это технически возможно). Поэтому наличие сетевых IoA, как технологически абсолютно иных в сравнение с хостовыми IoA, в значительной степени повысят совокупный detection rate.

Если совсем кратко, то реализованные ранее EDR-ные подходы могут быть полностью перенесены на сеть: нам также нужен инструмент, поставляющий телеметрию, которую мы могли бы предварительно обогащать доступным ThreatIntel-ом, и поверх которой мы смогли бы делать TTP-based детекты, именуемые нами внутри "хантами" или публично индикаторами атаки (IoA). Некоторые примеры EDR-ной телеметрии приведены на слайде 9, а на слайде 11 приведен укрупненный пайплайн ее обработки: обогащение, далее - TTP-based разметка "хантами"\IoA, далее можно еще покоррелировать и поразмечать, в завершении - валидация живым аналитиком (или машиннообучаемым Автоаналитиком, может, как-нибудь напишу об этом). Приведу здесь картинку из презентации, которую показывал на одном из мероприятий:


В качестве сенсора для сетевого активного поиска угроз прекрасно подходит Network Forensics Tools. NFT - старый термин, стары и инструменты, но, если упрощенно определить активный поиск угроз - это форенсика, реализованная как операционный процесс, сейчас, находясь на этапе, когда атаки нельзя не только предотвратить, но даже и обнаружить полностью автоматически, активный поиск угроз - все что нам остается. При этом, на сети работают все те же подходы: пытаемся предотвратить (NGFW, IPS), не получилось - хотя бы обнаружить (IDS), не получилось и обнаружить автоматически - должна быть телеметрия для ручного хантинга. И здесь как раз нам поможет NFT.

Приведу пример на Bro/Zeek. Телеметрией для активного поиска угроз служат его логи, которые полностью расширяемые как по полям (примером может служить hassh от ребят из Salesforce), так и по типам логов - ничего не мешает написать парсер под любой загадочный протокол, журналлируемый в отдельный лог. Логи Zeek являются прекрасным компромиссом между сбором всего трафика, и сбором только детектов NGFW/IDS/IPS, но, вместе с тем, достаточным для сетевого хантинга. В этом плане их допустимо сравнить с сырыми нотификациями EDR, например, от sysmon.

Сырые логи Zeek можно также собрать в ELK, например так (правда, я бы сделал проще: настроил бы логгирование Bro в JSON, и тогда filter в конфиге Logstash был бы тривиален:
if "brologs" in [tags] {
   json {
      source => "message"
      remove_field => "message"
   }
}
), а уже запросами в Kibana удобно хантить.

Поверх телеметрии Zeek можно уже класть "ханты", автоматизирующие запросы к Elastic-у, аналогично как это делается у нас для телеметрии EPP-EDR. Для понимания приведу примеры хантов на телеметрии Zeek:
  • прохождение неизвестного трафика через стандартный порт (техника - T1436)
  • использование подозрительных методов RPC (T1175)
  • подозрительный User-Agent - Mozilla/5.0 (Windows NT 10.0; Microsoft Windows 10.0.15063; en-US) PowerShell/6.0.0 (T1089)  
Или что-нибудь более продвинутое, может даже с машобучем :)

Будучи немного погруженным в инжениринг детектов EDR могу поделиться проблемой, что далеко не любую телеметрию можно достать по причинам стабильности и производительности, а полагаться полностью на ETW не кажется надежным. В случае NFT таких проблем нет, поскольку трафик собирается пассивно, и анализируется не на целевой системе, и, следовательно, не влияет на ее производительность (плата за это очевидна - меньшая visibility, но, вместе с тем, - NFT непереоценимое дополнение к EPP-EDR)

Думаю, что уже очевидна полная аналогия, но все же приведу табличку:


ПерспективаEPP-EDRNFT
Prevent - Detect - HuntEPP - EDR - активный поиск угроз по телеметрииNGFW/IPS - IDS - активный поиск угроз по телеметрии
Телеметриязапуск процессов, загрузка библиотек, детект EPP, событие ОС, создание файла, инжект, ... логи соединений, обнаруженное в сети ПО, пролетающие по сети файлы, логи протоколов прикладного уровня, ...
Ханты\Детекторы\IoAРелевантные телеметрии

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

В заключении хочется дополнить слова мистера Фика, указанные в эпиграфе: "Threat hunting is the use case for combination of EDR and NFT", что надеюсь, мне удалось доказать в этой заметке на примере Zeek.


Некоторые, возможно полезные, ссылки





Tuesday, October 29, 2019

myapp @Safe-10.10.10.147@HTB BO DEP (ROP в локальный system@main)

Продолжая серию своих черновых заметок о бинарщине, расскажу о переполнении буфера в машинке Safe все на том же HTB. Интересно здесь может быть то, что необходимый "стандартный" гаджет "pop rdi; ret" здесь не находится, поэтому использовать pwntools полностью автоматизированно, как мы это сделали в Ellinson, не получится.



Запускаем приложение, оно показывает вывод команды uptime, затем спрашивает вопрос, и выводит пользовательский ввод.


Сгенерим 500 символов и пошлем в приложение, оно упадет в Segmentation fault, из чего следует, что приложение уязвимо к переполнению.

Запустим в дибаггере и посмотрим, что творится в памяти на момент падения. Для верности пошлем 2000 байт.

Замечаем, что на момент падения буфер записался в регистры rsp, rsi, r9 и r13. Это означает, что их содержимое мы можем контролировать при переполнении.
rdi - используется для передачи значений аргументов в функцию в х64 (а у нас именно такой случай) - пустой.

Определим какие места нашего буфера попали в какие регистры.
rsp: 120. По смещению 120 в нашем буфере мы попадаем в стек.

rsi: 1024

r9: 1072

r13: 336

Технически нам надо получить шелл, т.е. как-то прыгнуть на функцию system с аргументом “/bin/bash”.

Первое, что приходит в голову - реализовать переход на libc, где есть и system и строка “/bin/sh”, как мы это делали ранее. Но, во-первых, машинка простая (20 очков, вместо 40 за Ellingson), т.е. решение должно быть проще, а во-вторых, внутри приложения уже есть вызов system, которым надо бы воспользоваться…

Полезная статья по теме: 
 
Посмотрим, какие у нас есть функции, обратим внимание на наличие странной функции test по по смещению 0x401152:

Посмотрим main (по смещению 0x40115F), убедимся в наличии там вызова system. Командочка, которая нам в этом поможет objdump -D myapp.

Важно: Скриншоты из objdump -D будут в AT&T синтаксисе, где операнды команд следуют наоборот, чем в более привычном Intel, но, надеюсь, это не будет большим неудобством. 

Посмотрим на test (0x401152):

test - очень загадочная функция.
Особенно любопытен прыжок на исполнение r13 (по адресу 0x401159). Трудно себе представить исходный код, скомпилированный в такую инструкцию… Но запомним этот момент, - из него следует, что то, что нам нужно выполнить мы можем записать в r13 и подстроить так, чтобы исполнение программы попало на смещение 0x401159.

Но вернемся к нашем регистрам. При отправке 2000 байт, по смещению 120 мы попадаем в RSP, по смещению 336 - попадаем в R13, по смещению 1024 - попадаем в RSI, по смещению 1072 - попадаем в R9.
Наиболее интересно попадание в стек (в RSP). Попадая в стек мы его контролируем, т.е. можем туда записать то, что нам нужно и выполнить это. Аргументы для исполняемой функции в х64 берутся из регистра rdi, а чтобы положить содержимое стека в rdi нам надо найти гаджет типа “pop rdi; ret”, который положит текущее содержимое стека в rdi и вернется обратно.

Гаджета “pop rdi; ret” в исполняемой (у нас DEP) части кода не нашлось :(. Но зато нашелся полностью эквивалентный, “mov rdi, rsp” (по смещению  0x401156, как отмечено выше, в AT&T синтаксисе на скриншоте он выглядит как "mov %rsp, %rdi”) и через некоторое количество инструкций - “ret”, - в той самой загадочной функции “test” (см. картинку выше).

Получается, что если прыгнуть на 0x401156, то согласно коду test, выполнится, как минимум, следующее:
  • текущее содержимое стека (из регистра rsp) положится в регистр rdi (это хорошо, так как после переполнения мы контролируем стек и можем записать туда то, что нам нужно).
  • вызовется инструкция, располагающаяся в r13 ("jmp r13” по смещению 0x401159)
"jmp r13” - крайне странная инструкция, что создает впечатление подстроенности и усиливает веру в то, что мы на правильном пути. Т.е., если в r13 нам удастся положить адрес system из main, то system выполнится, а параметр для system будет взят из rdi, куда его надо заблаговременно записать из стека (инструкция "mov rdi, rsp", или аналог), который мы контролируем.

Таким образом, кусочек нашего пейлоада переполнения в стеке может выглядеть так: p64(0x401156) + '/bin/bash\x00’, где p64(0x401156) - вызовет инструкцию копирования текущего указателя стека в rdi, а ‘/bin/bash\x00’- куда будет показывать указатель стека (т.е. что фактически будет положено в rdi), после отработки 0x401156. С параметрами для system, вроде, разобрались, теперь надо подумать как вызвать system.

Если положить адрес system в r13, то инструкция “jmp r13”, расположенная по смещению 0x401159 (в test), его выполнит. 
Положить из стека в r13 можно инструкциями “pop r13; ret”, поищем такой гаджет. 
В нашем дизассемблере, полученном с помощью “objdump -D myapp”, можно найти вот такую последовательность: 

По смещению 0x401206 идет последовательность таких инструкций:
  • положить текущий стек в r13
  • положить текущий стек в r14
  • положить текущий стек в r15
  • выйти.
Т.е. практически то, что нам нужно - “pop r13; ret”, но с добавлением лишних pop r14 и pop r15, что не является проблемой, ибо мы полностью контролируем стек и можем положить туда нужное количество мусора для успешного его размещения в регистрах r14 и r15.

Таким образом, еще один кусочек нашего пейлоуда должен выглядеть так: p64(0x401206) + p64(0x40116e), где p64(0x401206) - положит содержимое стека в r13, а p64(0x40116e) - что будет лежать в стеке. 

Из картинки с кодом main выше видно, что по адресу 0x40116e располагается инструкция вызова system из main: 

Но надо еще дойти до ret, от которого нас отделяют инструкции “pop r14; pop r15”, расположенные по смещениями 0x401208 и 0x40120a, которые положат содержимое стека в регистры r14 и к15 соответственно . Нужно обеспечить наличие чего-то в стеке для этих инструкций. Это сделать несложно, так как в стек мы класть умеем.

Итак, собираем наш пейлоад: payload = junk + p64(0x401206) + p64(0x40116E) + '\x42’*8 + ‘\x43’*8 + p64(0x401156) + '/bin/bash\x00’, где junk = ‘\x41’*120
Поясним, что здесь происходит:
  • junk - в момент переполнения доведет нас до текущего указателя стека (регистр rsp). Вспомним, что на момент переполнения мы находимся на выходе из main (0x4011ac, см выше скриншот EDB). После выхода из main инструкция для исполнения будет взята из стека (из rsp).
  • p64(0x401206) подсовывает в текущее положение стека (в rsp) инструкцию по адресу 0x401206. Смотрим выше, 0x401206 - это "pop r13” - положить текущее содержимое стека в r13
  • следующее слагаемое нашего пейлоада, передаваемое в стек - p64(0x40116e). Выше указано, что по адресу 0x40116e располагается вызов system из main - это и есть то, что будет положено в r13 описанной выше инструкцией.
  • далее следует 2 мусора по 8 байт: “BBBB BBBB” и “CCCC CCCC”. Помним, что после выполнения кода по смещению 0x401206 ("pop r13”), будет выполняться код по смещению 0x401208 (“pop r14”), код по смещению 0x40120a (“pop r15”) и затем - по смещению 0x40120c - “ret”, см картинку с загадочной функцией test выше. В результате этой последовательности инструкций - “pop r14; pop r15; ret” - записанный нами мусор в стек - “BBBB BBBB” и “CCCC CCCC” будет положен в регистры r14 и r15 соответственно, и после этого будет реализован выход на адрес из стека.
  • далее в нашем стеке следует p64(0x401156) + ‘/bin/bash\x00’. Как отмечалось выше, по смещению 0x401156 располагается инструкция “mov rdi, rsp”, размещающая в RDI текущее содержимое стека, а строка ‘/bin/bash\x00’ - то самое, нужное нам, содержимое стека, которое будет положено в RDI.
  • посмотрим выше на код чудесной функции test. За только что описанной инструкцией по смещению 0x401156 (mov rdi, rsp) следует инструкция по смещению 0x401159 (jmp r13). В результате этого jmp исполнение кода перейдет на адрес в r13, а как мы ранее отмечали, на тот момент в r13 будет лежать вызов system из main, который возьмет аргумент из rdi, где лежит строка “/bin/bash”. 

В результате, мы получаем RCE в виде запуска /bin/bash с помощью system, взятого из main, без перехода на libc. В целом, вместо "/bin/bash" может быть любая другая команда. 

Для того, чтобы стало совсем понятно что происходит, отладим наш эксплоит в gdb с замечательным палгином gef.

Код нагрузки изменим на запуск /bin/id, чтобы не связываться с интерактивностью (как было бы в случае bash).
Пересылку нагрузки организуем через файл, подготовленный следующим образом: 

Посмотрим нашу нагрузку:

Запустим наше приложение в отладчике и поставим breakpoint на выход из main (смещение: 0x4011ac, скриншот с кодом main см. выше), пейлоад передадим на stdin.
 
Окно отладчика сразу после запуска, приложение остановилось на созданной breakpoint:

Из картинки видно, что мы находимся на выходе из main: “
ret”, rip = 0x4011ac, наша breakpoint. При выходе из main следующая инструкция берется из стека. 
Следующая инструкция, которая будет выполнена - pop r13, что видно из rsp = 0x401206, в результате ее исполнения содержимое стека будет положено в r13. r13 пока пустой, там содержится 0x1
Следующие 8 байт в стеке - инструкция по смещению 0x40116e - вызов system@plt из main (
call 0x401040 ).

Наберем nexti (следующая инструкция) и посмотрим, что произойдет дальше. 

Из картинки следует, что текущая выполняемая инструкция (смотрим регистр rip) - pop r13 - положить содержимое стека в r13.
Содержимое стека (см. регистр rsp) - смещение 0x40116e - это адрес инструкции вызова system из main (
call 0x401040 )

Вводим nexti, смотрим, что будет дальше. 

На картинке отчетливо видно, что в регистр r13 было положено смещение 0x40116e, по которому располагается вызов system из main (
call 0x401040 )
Текущая исполняемая инструкция - pop r14 - положит в r14 текущее содержимое стека.
Текущее содержимое стека (смотрим регистр rsp) - “BBBB BBBB” 

Набираем nexti, смотрим что будет дальше. 

В r14  теперь “BBBB BBBB
Следующая инструкция (см. регистр rip) - pop r15 - положит содержимое стека в регистр r15.
Текущее содержимое стека (регистр rsp) - “CCCC CCCC"

Набираем nexti, смотрим что происходит дальше. 

В регистр r15 записались символы “CCCC CCCC
Программа находится на инструкции ret по смещению 0x40120c, см. регистр rip. После выхода, адрес следующей инструкции будет взят из стека.
Стек (rsp) указывает на инструкцию mov rdi, rsp - положить содержимое стека в rdi - расположенную по смещению 0x401156 в функции test ( mov rdi, rsp)

Набираем nexti, смотрим, что происходит. 

Текущая исполняемая инструкция (см. регистр rip) расположена по смещению 0x401156 - mov rdi, rsp - положит в регистр rdi содержимое стека (регистр rsp)
Стек указывает на строку “/bin/id” - это то, что будет положено в rdi

Набираем nexti 

Наблюдаем полностью подготовленную нужную нам для запуcка system(“/bin/id”) ситуацию:
  • в rdi лежит строка “/bin/id”. Помним, что при запуске функции, аргумент будет браться из rdi.
  • следующая исполняемая инструкция расположена по смещению 0x401159 - jmp r13 - переход на исполнение инструкции по адресу в r13 (см. регистр rip)
  • В r13 лежит инструкция по смещению 0x40116e - вызов system из main (
    call 0x401040 ). При отработке этой инструкции будет выполнена функция system с аргументом из регистра rdi.

Набираем nexti . 

Текущая исполняемая инструкция (регистр rip) расположена по смещению 0x40116e - вызов system из main (
call 0x401040 )
Аргумент для system будет браться из rdi, в котором сейчас содержится строка “/bin/id

Набираем nexti и видим, что system(“/bin/id”) выполнилась. 


Код эксплоита и предложение прикладываю.

Хочется выразить огромнейшую благодарность моему доброму приятелю и коллеге Артему, который познакомил меня с прекрасным плагином gef и пояснил многие моменты, в которых я бы точно сам не разобрался.

Всем добра!



PS: Один мой приятель спросил для кого я все это пишу на русском языке. Я немного смутился и ничего разумного ответить не смог, так куча вопросов на которые я не смог быстро сообразить ответ заняли все мои мысли:
- какие претензии к русскому языку? Почему вообще все статьи надо писать на английском (хотя, вполне вероятно, будущие заметки я буду писать на нем)?
- почему я не могу писать заметки для себя? Но писать их более-менее прилично, чтобы в них мог еще кто-то разобраться, может быть, это тоже будет кому-то полезно. В частности, шпаргалку по certutil я писал для себя в бытность админа УЦ, но сейчас это - самая популярная статья в этом блоге, причем я ее с момента написания не обновлял, а, вероятно, имело бы смысл добавить релевантный функционал декода бинарей в Base64 и скачивания файлов.
- в чем проблема пользоваться автоматическим переводом от того же Google?
- мне тупо проще писать по-русски, так как это мой родной язык
- ....