> For the complete documentation index, see [llms.txt](https://yutewiyof.gitbook.io/intro-rev-ida-pro/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://yutewiyof.gitbook.io/intro-rev-ida-pro/chast-57.md).

# Часть 57

[\[Используемые материалы\]](https://github.com/yutewiyof/intro-rev-ida-pro/tree/e1367e11cc661f3d69c02ae5f733b7dd168bc5ab/.gitbook/assets/files/57.zip)

Хорошо. Мы будем пытаться эксплуатировать переполнение стека из скрипта **PYTHON**. Обычно эксплуатация ядра происходит локально. Другими словами, скрипт уже эксплуатирует некоторую программу, которая не имеет привилегий системы и хочет повысить привилегии и быть с правами **SYSTEM**, что устраняет ограничения, которые имеет эксплуатируемый процесс, который имеет только ограниченные привилегии обычного пользователя на машине.

Поэтому, за исключением очень редких исключений, эксплойты ядра - это эксплоиты для повышения привилегий или **PRIVILEDGE ESCALATION**.

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

В любом случае, как код в **C**, так и код на **PYTHON** основаны на вызовах одних и тех же **WINDOWS API**, таких, как **CREATEFILE**, **DEVICEIOCONTROL** и т.д. Поэтому, то, что делается на одном языке, легко переносится на другой.

Давайте напомним нашу модель **PYTHON** предыдущего примера.

![](/files/-LjrT45cqLr5_Ry7TS8w)

Первым шагом будет изменение имени для драйвера поэтому, когда вы делаете **CREATEFILE**, нам возвратится его правильный дескриптор.

Напомним, что имя генерируется здесь

![](/files/-LjrT45m7p4Zbx42Yecj)

![](/files/-Ljtu5MTA_YQd912rfiF)

Здесь драйвер возвращает **SYMBOLICNAME**, который приходит из строки, которая находится в **DEVICENAME**.

![](/files/-LjrT466CLhGXSbjCZ_Y)

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

![](/files/-LjrT46DSLUtU6ZZFyq0)

Мы можем скопировать его на целевую машину и посмотреть, не даст ли он мне ошибку или даст действительный дескриптор, но я буду продолжать смотреть.

Этот драйвер запущен, и я запускаю скрипт.

![](/files/-Ljtu5OIZUMH_tUyTkSw)

Поскольку **SYMBOLICLINK** идет отсюда, имеет смысл использовать это имя.

![](/files/-Ljtu5OYNv6Blcx9R4su)

Давайте попробуем так.

![](/files/-Ljtu5Os1Ib4dVu9f7P2)

Мы видим, что всё работает.

![](/files/-LjrT46kQM44rhF8tUlQ)

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

![](/files/-LjrT46rNEIBa6vjnHtw)

На данный момент, мы удаляем **WHILE** и все остальное, что нас не интересует. Следующая вещь заключается в том, чтобы увидеть, какой именно **IOCTL** переносит нас в блок переполнение стека, чтобы отправить его.

Вопрос состоит в том, как добраться туда.

![](/files/-Ljtu5PxaamIe8jhqICU)

Мы видим, что драйвер идёт отсюда.

![](/files/-Ljtu5QJtKPvDwwtxpNY)

Регистр **EDX** имеет код **IOCTL**, который получается отсюда.

![](/files/-Ljtu5QfqIwjiz_FA5lZ)

Программа сделает вычитание из регистра значения **0x222003**, и если результатом будет нуль, мы перейдем к той части, которая нам нужна.

Давайте посмотрим, достигнет ли программа блока, где мы хотим присоединить **IDA** и устанавливаем **BP**.

![](/files/-LjrT47TrmJGqzag6R0j)

Я запускаю драйвер на целевой машине и попадаю в **BP**.

![](/files/-LjrT47d9FJutZe_vSwq)

Мы видим, что в регистр **EDX** драйвер считывает **IOCTL** код, который он передает.

![](/files/-LjrT47k7HOzx_t3_o2L)

![](/files/-Ljtu5SBlNouWrYL8T24)

Программа помещает результат в регистр **EAX**, вычитает из него значение **0x222003** и поскольку результатом будет нуль, драйвер переходит в уязвимый блок.

![](/files/-Ljtu5S_QtIMSxQOz6y_)

![](/files/-Ljtu5T8ueh3BkFWAP8t)

Программа читает длину, которая является нулем, так как я не передал ей аргументы за исключением **IOCTL**.

![](/files/-LjrT48JAZg_3tn7-8oY)

Поскольку аргумент равен нулю, программа пропускает функцию, в которой происходит переполнение стека.

![](/files/-Ljtu5URxo6T09wH0SiQ)

Поскольку **API** функция, вызывается из **WIN32FILE** в **PYTHON**, она имеет меньше аргументов, чем исходная функция, которая имеет больше.

![](/files/-Ljtu5UuJgpehs9ZeXaw)

![](/files/-LjrT48gJDO6V95YOxHJ)

Здесь находится определение **WIN32FILE PYTHON**.

![](/files/-Ljtu5Vg5R_JGZl9OW_b)

Мы увидим, что если мы достигли этого места, нам нужно сделать буфер, чтобы передать его на вход, так как поскольку нам нужен указатель на него, мы можем сделать это с помощью функции **ALLOCATEREADBUFFER**, которая выделяет нам в куче количество памяти, которое нам нужно передать входному буферу. (Другая опция **WIN32FILE** не дает нам).

![](/files/-LjrT48xJMG57jxLAVNS)

Поскольку библиотека **WIN32FILE** не имеет доступа ко всем **API**, она не позволяет копировать непосредственно в буфер как функция **MEMCPY** или что-то вроде этого, поэтому мы можем копировать его только с помощью функции **READFILE**, поэтому мы делаем файл **PEPE.BIN**, заполняя его **0x1000** буквами **A**. Мы передаем файл в функцию **CREATEFILE**, чтобы она открывала его и вернула нам дескриптор, и мы передали его в функцию **READFILE** с аргументом **BUF** буфера, который мы выделяем и копировать содержимое файла туда.

Очевидно, что если мы сделаем это на **C, C++** или языке, в котором мы можем использовать функцию **VIRTUALALLOC** и легко копировать с помощью функции **MEMCPY** или той которую мы хотим. Но здесь у нас есть определенные ограничения, и мы должны приспосабливаться к этому.

Нам нужно дополнительно знать, какую длину мы должны отправить. Давайте посмотрим на буфер назначения в **IDA**.

![](/files/-Ljtu5WQFXEdR4nN7c9J)

Мы замечаем, что с начала буфера до адреса возврата получается **520** десятичных байт \* **4** размер элемента.

Поэтому

```python
hex(520*4) = '0x820'
```

Поэтому мы должны отправить **0x820** + **RET**.

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

![](/files/-Ljtu5WvMTYERMTWbk0P)

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

![](/files/-Ljtu5XFyJxyMfxc3Ksw)

![](/files/-LjrT49HwmqbZXoLm4Qw)

Здесь cуществует адрес, который мы можем передать с помощью **STRUCT.PACK** после **0x820 A**. Досадно, что мы должны записать их в файл, который читается.

Следовательно, как только у меня есть адрес буфер.

![](/files/-LjrT49O9-hLVOh7fOje)

Я пишу в файл **ФРУКТЫ**, которые хочу отправить.

![](/files/-LjrT49UzI_xwB44GpdM)

Перед функцией **READFILE** скопируем их в буфер.

Давайте посмотрим, заработает ли это. В этом случае нет необходимости иметь файл, потому что он будет создан и заполнен, поэтому мы удалили предыдущий.

![](/files/-Ljtu5YFz6jw_pShccCO)

Для этого мы изменили аргумент на **CREATE ALWAYS**.

![](/files/-Ljtu5YTolNPEBHM3u6y)

Хорошо. Предположительно здесь - наш буфер с буквами **A**. **IDA** остановлена. Давайте посмотрим, что я передаю.

![](/files/-Ljtu5YfSYRzZP5ejT_P)

Мы видим, что входной буфер, который передается в регистр **EDX**, показывает нам то же самое значение. Давайте посмотрим, есть ли там **A**.

![](/files/-LjrT4A3q8R9OKoY5i7n)

![](/files/-LjrT4ABAXB5ziK_gkxk)

Он не заполнился буквами A. Давайте пропустим эксплуатацию и давайте исправим скрипт.

![](/files/-LjrT4AJwdiJJCW7uZuc)

Я меняю **EIP** здесь с помощью правой кнопки и **SET IP** и нажимаю **RUN**.

Я думаю, что проблема в том, что размер буфера должен соответствовать размеру файла.

![](/files/-Ljtu5Zo3DjC3VtuVyqX)

Давайте сделаем так.

Ах, уже упал.

![](/files/-Ljtu5_GC-vfF0dEi3Qu)

Скрипту не хватало установки указателя файла в начале файла. Он читал файл с конца, где я оставил **WRITEFILE**, поэтому я не читал **A**. Теперь всё работает.

![](/files/-LjrT4A_qKotoVtGmAEX)

Я трассирую до **MEMCPY**.

![](/files/-LjrT4AgnsfpOnsWIWsX)

Я добираюсь до **RET**.

![](/files/-LjrT4Apct3_KTYvuma9)

Я продолжаю с помощью **F7**.

![](/files/-LjrT4AxPZnvIxzevv3M)

Я вижу, что я попал в буфер. Проблема состоит в том, что, поскольку я не выделил его с помощью **VIRTUALALLOC**, а выделяю его с помощью **API ALLOCATEREADBUFFER**, **WIN32FILE** у него нет прав на выполнение. Поэтому мне придется найти способ выделить исполняемый код другим способом.

![](/files/-LjrT4B51nPtgtrvds38)

Я добавил библиотеку **CTYPES**, и у неё есть функция **VIRTUALPROTECT**, поэтому я дал ей права на выполнение, и теперь у нас нет проблем.

![](/files/-Ljtu5bKaoTxXT51URkV)

Программа переходит и выполняет без проблем. Я действительно вижу, что **CTYPES** более продвинутая, чем **WIN32API**, поэтому вы можете делать все, вызывает прямо функцию **VIRTUALALLOC**непосредственно из **CTYPES**.

Версия c **CTYPES** – это так

![](/files/-Ljtu5bXzwOe3tN3qeaw)

Библиотека использует напрямую функции **CREATEFILE**, **VIRTUALALLOC**, **RTLMOVENEMORY** и **DEVICEIOCONTROL**. Вам просто нужно быть осторожным с одним из типов, но она работает хорошо.

![](/files/-LjrT4BQpRgA-6R0snoc)

Здесь всё работает. Вопрос сейчас состоит в том, что мы выполняем код. Нам осталось бы делать шеллкод, потому что так у нас будет только красивый синий экран.

Мы проанализируем шеллкод и создадим его в следующей части.

Автор оригинального текста — Рикардо Нарваха.

Перевод и адаптация на русский язык — Яша Яшечкин.

Перевод специально для форума системного и низкоуровневого программирования - WASM.IN

03.11.2018

[**Источник: ricardonarvaja.info**](http://ricardonarvaja.info/WEB/IDA%20DESDE%20CERO/CURSO%20DE%20IDA%20TUTES/57-INTRODUCCION%20AL%20REVERSING%20CON%20IDA%20PRO%20DESDE%20CERO.docx)
