protobuf脱壳二进制漏洞总结
字数 1500 2025-08-22 12:22:15
Protocol Buffers (Protobuf) 二进制漏洞分析与利用
1. Protobuf 基础
1.1 简介
Protocol Buffers (简称 Protobuf) 是由 Google 开发的一种语言中立、平台中立的可扩展序列化结构数据的方法。它是一种高效的协议,常用于网络通信、数据存储和应用程序间的数据传输。
1.2 环境搭建
sudo apt-get update
sudo apt-get install -y protobuf-compiler libprotobuf-dev
sudo apt-get install libprotobuf-c-dev protobuf-c-compiler
编译为C代码:
protoc --c_out=. user.proto
生成文件:
user.pb-c.h:头文件,定义消息类型及其相关函数user.pb-c.c:源文件,实现序列化和反序列化等操作
编译为Python代码:
protoc --python_out=. demo.proto
生成 demo_pb2.py 文件
1.3 基本语法
// demo.proto
syntax = "proto3";
package tutorial;
message Person {
string name = 1;
int32 id = 2;
string email = 3;
enum PhoneType {
PHONE_TYPE_UNSPECIFIED = 0;
PHONE_TYPE_MOBILE = 1;
PHONE_TYPE_HOME = 2;
PHONE_TYPE_WORK = 3;
}
message PhoneNumber {
string number = 1;
PhoneType type = 2;
}
repeated PhoneNumber phones = 4;
}
message AddressBook {
repeated Person people = 1;
}
2. Protobuf 逆向分析
2.1 关键逆向点
在生成的 demo-pb-c.c 文件中,反序列化函数:
Tutorial__AddressBook * tutorial__address_book__unpack (
ProtobufCAllocator *allocator,
size_t len,
const uint8_t *data)
{
return (Tutorial__AddressBook *)
protobuf_c_message_unpack(&tutorial__address_book__descriptor,
allocator, len, data);
}
2.2 Descriptor 结构体
struct ProtobufCMessageDescriptor {
uint32_t magic; /**< 1 */
const char *name; /**< 2 */
const char *short_name; /**< 3 */
const char *c_name; /**< 4 */
const char *package_name; /**< 5 */
size_t sizeof_message; /**< 6 */
unsigned n_fields; /**< 7 */
const ProtobufCFieldDescriptor *fields; /**< 8 */
const unsigned *fields_sorted_by_name; /**< 9 */
unsigned n_field_ranges; /**< 10 */
const ProtobufCIntRange *field_ranges; /**< 11 */
ProtobufCMessageInit message_init; /**< 12 */
void *reserved1; /**< 13 */
void *reserved2; /**< 14 */
void *reserved3; /**< 15 */
};
关键字段:
magic:通常为0x28AAEEF9n_fields:结构体中的字段数量fields:指向字段描述符结构体
2.3 ProtobufCFieldDescriptor 结构体
struct ProtobufCFieldDescriptor {
const char *name; /**< 1 */
uint32_t id; /**< 2 */
ProtobufCLabel label; /**< 3 */
ProtobufCType type; /**< 4 */
unsigned quantifier_offset; /**< 5 */
unsigned offset; /**< 6 */
const void *descriptor; /**< 7 */
const void *default_value; /**< 8 */
uint32_t flags; /**< 9 */
unsigned reserved_flags; /**< 10 */
void *reserved2; /**< 11 */
void *reserved3; /**< 12 */
};
字段说明:
name:字段名id:唯一字段编号label:修饰符(required/optional/repeated)type:数据类型offset:字段在结构体中的偏移量
2.4 ProtobufCLabel 枚举
typedef enum {
PROTOBUF_C_LABEL_REQUIRED, //1
PROTOBUF_C_LABEL_OPTIONAL, //2
PROTOBUF_C_LABEL_REPEATED, //3
PROTOBUF_C_LABEL_NONE, //4
} ProtobufCLabel;
2.5 ProtobufCType 枚举
typedef enum {
PROTOBUF_C_TYPE_INT32, /**< int32 */ // 0
PROTOBUF_C_TYPE_SINT32, /**< 有符号 int32 */ // 1
PROTOBUF_C_TYPE_SFIXED32, /**< 有符号 int32(4 字节) */ // 2
PROTOBUF_C_TYPE_INT64, /**< int64 */ // 3
PROTOBUF_C_TYPE_SINT64, /**< 有符号 int64 */ // 4
PROTOBUF_C_TYPE_SFIXED64, /**< 有符号 int64(8 字节) */ // 5
PROTOBUF_C_TYPE_UINT32, /**< 无符号 int32 */ // 6
PROTOBUF_C_TYPE_FIXED32, /**< 无符号 int32(4 字节) */ // 7
PROTOBUF_C_TYPE_UINT64, /**< 无符号 int64 */ // 8
PROTOBUF_C_TYPE_FIXED64, /**< 无符号 int64(8 字节) */ // 9
PROTOBUF_C_TYPE_FLOAT, /**< 浮点数 */ // 10
PROTOBUF_C_TYPE_DOUBLE, /**< 双精度浮点数 */ // 11
PROTOBUF_C_TYPE_BOOL, /**< 布尔类型 */ // 12
PROTOBUF_C_TYPE_ENUM, /**< 枚举类型 */ // 13
PROTOBUF_C_TYPE_STRING, /**< UTF-8 或ASCII 字符串 */ // 14
PROTOBUF_C_TYPE_BYTES, /**< 任意字节序列 */ // 15
PROTOBUF_C_TYPE_MESSAGE, /**< 嵌套消息 */ // 16
} ProtobufCType;
3. Protobuf 脱壳技术
3.1 工具分析
使用 pbtk 工具:
sudo apt install python3-pip git openjdk-11-jre libqt5x11extras5 python3-pyqt5.qtwebengine python3-pyqt5
sudo pip3 install protobuf pyqt5 pyqtwebengine requests websocket-client
git clone https://github.com/marin-m/pbtk
cd pbtk
./gui.py
3.2 手动分析步骤
- 根据 magic 值
0x28AAEEF9定位 descriptor 结构体 - 按照字节逆向还原 descriptor 结构体
- 根据
fields指针定位ProtobufCFieldDescriptor结构体 - 逐个分析字段描述符
- 根据参数个数判断是 proto2 还是 proto3(proto3 删除了预留值)
4. 漏洞案例分析
4.1 2023ciscn-talkbot 漏洞分析
逆向结构体
syntax = "proto2";
message devicemsg {
required sint64 actionid = 1;
required sint64 msgidx = 2;
required sint64 msgsize = 3;
required bytes msgcontent = 4;
}
漏洞点
- free 函数置零位置错误,导致 UAF 漏洞
- 利用 mprotect + setcontext_61 + magic_addr 进行攻击
- 2.31 版本 setcontext 变为 rdx,需用 magic_addr 转换
利用思路
- 泄露 libc 和堆地址
- 构造 ROP 链
- 利用 UAF 修改 free_hook
- 执行 shellcode 获取 flag
4.2 ciscn2024-ezbuf 漏洞分析
逆向结构体
syntax = "proto2";
message devicemsg {
required bytes whatcon = 1;
required sint64 whattodo = 2;
required sint64 whatidx = 3;
required sint64 whatsize = 4;
required uint32 whatsthis = 5;
}
漏洞点
- free 没有置零,存在 UAF
- strtok 使用 ABS_got(libc 的 got 表)
- 劫持 strtok 跳转到 system 函数
利用思路
- 泄露 libc 地址
- 构造 fastbin attack
- 修改 libc got 表
- 劫持程序流执行 system("/bin/sh")
5. 防御建议
- 确保 free 操作正确置零指针
- 检查 Protobuf 反序列化边界
- 使用最新版本的 Protobuf 库
- 启用堆保护机制(如 ASLR、NX 等)
- 对输入数据进行严格验证
6. 总结
Protobuf 相关的二进制漏洞主要集中在:
- 反序列化过程中的内存管理问题
- 指针处理不当导致的 UAF
- 结构体解析错误
- 版本差异导致的兼容性问题
通过逆向 descriptor 结构体可以还原原始 protobuf 定义,进而分析漏洞点并构造利用链。在实际漏洞利用中,需要结合堆管理和程序流劫持技术实现攻击。