> 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)


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://yutewiyof.gitbook.io/intro-rev-ida-pro/chast-57.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
