目录

Protobuf in Pwn

傻逼国内pwn, 不会出可以不出, 套个 protobuf 断网不给环境打你 🐴

sh

emerge dev-libs/protobuf dev-libs/protobuf-c dev-python/protobuf

数据段有出现 .proto 字符串

marin-m/pbtk: A toolset for reverse engineering and fuzzing Protobuf-based apps

text

ggbound/docker/bin via 🐍 v3.13.5
❯ ./pbtk/extractors/from_binary.py pwn out

[+] Wrote 10 .proto files to "out".


ggbound/docker/bin via 🐍 v3.13.5
❯ t out
 out
├──  github.com
│   └──  golang
│       └──  protobuf
│           └──  ptypes
│               ├──  any
│               │   └──  any.proto
│               ├──  duration
│               │   └──  duration.proto
│               └──  timestamp
│                   └──  timestamp.proto
├──  google
│   ├──  protobuf
│   │   ├──  any.proto
│   │   ├──  descriptor.proto
│   │   ├──  duration.proto
│   │   └──  timestamp.proto
│   └──  rpc
│       └──  status.proto
├──  grpc
│   └──  binlog
│       └──  v1
│           └──  binarylog.proto
└──  ggbond.proto

ggbound/docker/bin via 🐍 v3.13.5
❯ head out/ggbond.proto
syntax = "proto3";

package GGBond;

option go_package = "./;ggbond";

service GGBondServer {
    rpc Handler(Request) returns (Response);
}

傻逼 ccb 不给 protobuf-c.so

ida 导入一下 /usr/include/protobuf-c/protobuf-c.h, 里面头文件注释掉, 否则会递归到一些宏然后报错.

在 rodata 里搜 message 的魔术 0x28AAEEF9, 这个地方就是 ProtobufCMessageDescriptor.

对着相应的地方是 ProtobufCFieldDescriptor

结构体示例
结构体示例

对着字段写 proto:

proto

syntax = "proto3";

package ${package_name};

message ${short_name} {
  ${fields}
}

其中 ${fields} 是各个 ProtobufCFieldDescriptor, 只用看前四个字段, 写成这样的:

text

${LABEL} ${TYPE} ${name} = ${id};

LABEL 和 TYPE 都是常量后面改成小写就行, LABEL_NONE 直接忽略.

此外, 0x14159BC3 是 ProtobufCServiceDescriptor 的魔术, 也对着恢复结构体.

sh

protoc --python_out=. name.proto

能够生成 name_pb2.py, import 这个包, 可以用 ${short_name}() 构造消息类, 直接实例 .name 就可以修改 fields 了. 之后 SerializeToString 序列化后拿去交互即可. 比如:

py

msg = giao_pb2.Msgiao()
msg.giaoid = 0x114514
msg.giaosize = 0x415411
msg.giaocontent = shellcode
msg.giaotoken = b"87dd78e1-9025-4d57-9c2e-418608b3bbea"
sa("your giao: ",msg.SerializeToString())

这文档是 Protocol Buffers (.proto) 文件的完整基础定义参考,适用于 proto2 和 proto3(默认推荐 proto3)。


proto

syntax = "proto3"; //  "proto2"

proto

package my.package.name;

proto

import "other.proto";

proto

option java_package = "com.example";
option go_package = "github.com/example/project/proto";

proto

message Person {
  string name = 1;
  int32 id = 2;
  bool is_verified = 3;
}

proto

repeated string tags = 4;

proto

map<string, int32> scores = 5;

proto

oneof contact_info {
  string email = 6;
  string phone = 7;
}

proto

reserved 10, 11, 12;
reserved "old_field", "legacy";

proto

enum Status {
  UNKNOWN = 0;
  ACTIVE = 1;
  INACTIVE = 2;
  BANNED = 3;
}

proto

service UserService {
  rpc GetUser (UserRequest) returns (UserResponse);
  rpc CreateUser (User) returns (UserResponse);
}

message UserRequest {
  int32 id = 1;
}

message UserResponse {
  bool success = 1;
  string message = 2;
}

proto

import "google/protobuf/any.proto";

message Wrapper {
  google.protobuf.Any payload = 1;
}

proto

message Outer {
  message Inner {
    int32 value = 1;
  }

  enum Level {
    LOW = 0;
    HIGH = 1;
  }

  Inner inner = 1;
  Level level = 2;
}

proto

message Foo {
  extensions 100 to 199;
}

结构类型 定义字段 魔数值 (16进制)
ProtobufCMessageDescriptor PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC 0x28aaeef9
ProtobufCServiceDescriptor PROTOBUF_C__SERVICE_DESCRIPTOR_MAGIC 0x14159bc3

proto

syntax = "proto3";

package example;

message User {
  int32 id = 1;
  string name = 2;
  repeated string roles = 3;
  Status status = 4;
  map<string, string> meta = 5;

  oneof contact {
    string email = 6;
    string phone = 7;
  }
}

enum Status {
  UNKNOWN = 0;
  ACTIVE = 1;
  BANNED = 2;
}

service UserService {
  rpc GetUser (UserRequest) returns (User);
  rpc CreateUser (User) returns (UserResponse);
}

message UserRequest {
  int32 id = 1;
}

message UserResponse {
  bool success = 1;
  string message = 2;
}