metasploit-framework/external/source/exploits/CVE-2018-4237/libspc/serialization.c

470 lines
14 KiB
C

#include "serialization.h"
#include "array.h"
#include "dictionary.h"
#include "utils.h"
#include <stdlib.h>
#include <stddef.h>
#include <stdio.h>
typedef struct {
unsigned char* start;
unsigned char* end;
unsigned char* ptr;
size_t num_ports;
spc_port_t* ports;
} writer_t;
typedef struct {
unsigned char* end;
unsigned char* ptr;
size_t next_port;
size_t num_ports;
spc_port_t* ports;
} reader_t;
char last_header[8] = "CPX@\x05\x00\x00\x00";
void spc_ensure_space(writer_t* writer, size_t num_bytes)
{
if (writer->ptr + num_bytes > writer->end) {
size_t new_size = (writer->end - writer->start) + num_bytes;
void* buf = realloc(writer->start, new_size);
writer->ptr = buf + (writer->ptr - writer->start);
writer->start = buf;
writer->end = buf + new_size;
}
}
size_t spc_write(writer_t* writer, void* buf, size_t len)
{
spc_ensure_space(writer, len);
memcpy(writer->ptr, buf, len);
writer->ptr += len;
return len;
}
size_t spc_write_padded(writer_t* writer, const void* buf, size_t len)
{
size_t remainder = (4 - (len % 4)) % 4;
spc_ensure_space(writer, len + remainder);
memcpy(writer->ptr, buf, len);
writer->ptr += len;
memset(writer->ptr, 0, remainder);
writer->ptr += remainder;
return len + remainder;
}
size_t spc_write_str(writer_t* writer, const char* str)
{
return spc_write_padded(writer, str, strlen(str) + 1);
}
size_t spc_write_uint32(writer_t* writer, uint32_t val)
{
// TODO replace with
// spc_write(writer, &val, 4);
spc_ensure_space(writer, 4);
memcpy(writer->ptr, &val, 4);
writer->ptr += 4;
return 4;
}
size_t spc_write_uint64(writer_t* writer, uint64_t val)
{
spc_ensure_space(writer, 8);
memcpy(writer->ptr, &val, 8);
writer->ptr += 8;
return 8;
}
size_t spc_write_int64(writer_t* writer, uint64_t val)
{
spc_ensure_space(writer, 8);
memcpy(writer->ptr, &val, 8);
writer->ptr += 8;
return 8;
}
size_t spc_write_double(writer_t* writer, double val)
{
spc_ensure_space(writer, 8);
memcpy(writer->ptr, &val, 8);
writer->ptr += 8;
return 8;
}
void spc_write_port(writer_t* writer, spc_port_t port)
{
writer->ports = realloc(writer->ports, (writer->num_ports + 1) * sizeof(spc_port_t));
writer->ports[writer->num_ports] = port;
writer->num_ports += 1;
}
size_t spc_write_array(writer_t* writer, spc_array_t* array);
size_t spc_write_dict(writer_t* writer, spc_dictionary_t* dict);
size_t spc_serialize_value(writer_t* writer, spc_value_t value)
{
size_t bytes_written = 0;
bytes_written += spc_write_uint32(writer, value.type);
switch (value.type) {
case SPC_TYPE_NULL:
break;
case SPC_TYPE_BOOL:
bytes_written += spc_write_uint32(writer, value.value.u64);
break;
case SPC_TYPE_UINT64:
bytes_written += spc_write_uint64(writer, value.value.u64);
break;
case SPC_TYPE_INT64:
bytes_written += spc_write_int64(writer, value.value.i64);
break;
case SPC_TYPE_DOUBLE:
bytes_written += spc_write_double(writer, value.value.dbl);
break;
case SPC_TYPE_STRING:
bytes_written += spc_write_uint32(writer, strlen(value.value.str) + 1);
bytes_written += spc_write_str(writer, value.value.str);
break;
case SPC_TYPE_ARRAY:
bytes_written += spc_write_array(writer, value.value.array);
break;
case SPC_TYPE_DICT:
bytes_written += spc_write_dict(writer, value.value.dict);
break;
case SPC_TYPE_FD:
case SPC_TYPE_SEND_PORT:
case SPC_TYPE_RECV_PORT:
spc_write_port(writer, value.value.port);
break;
case SPC_TYPE_UUID:
bytes_written += spc_write(writer, value.value.ptr, 0x10);
break;
case SPC_TYPE_DATA:
bytes_written += spc_write_uint32(writer, value.value.data.size);
bytes_written += spc_write_padded(writer, value.value.data.ptr, value.value.data.size);
break;
default:
printf("Unsupported value type: 0x%x\n", value.type);
}
return bytes_written;
}
size_t spc_write_array(writer_t* writer, spc_array_t* array)
{
size_t bytes_written = 0;
ptrdiff_t bytesize_offset = writer->ptr - writer->start;
spc_write_uint32(writer, 0); // placeholder for byte size
bytes_written += spc_write_uint32(writer, array->length);
for (size_t i = 0; i < array->length; i++) {
bytes_written += spc_serialize_value(writer, array->values[i]);
}
// Fill in correct byte size
*(uint32_t*)(writer->start + bytesize_offset) = bytes_written;
return bytes_written + 4;
}
size_t spc_write_dict(writer_t* writer, spc_dictionary_t* dict)
{
size_t bytes_written = 0;
ptrdiff_t bytesize_offset = writer->ptr - writer->start;
spc_write_uint32(writer, 0); // placeholder for byte size
bytes_written += spc_write_uint32(writer, dict->num_items);
for (spc_dictionary_item_t* item = dict->items; item != NULL; item = item->next) {
bytes_written += spc_write_str(writer, item->key);
bytes_written += spc_serialize_value(writer, item->value);
}
// Fill in correct byte size
*(uint32_t*)(writer->start + bytesize_offset) = bytes_written;
return bytes_written + 4;
}
spc_mach_message_t* spc_serialize(spc_message_t* msg)
{
spc_mach_message_t* mach_msg;
size_t actual_size, content_size, initial_size = msg->content->num_items * 32; // heuristic
writer_t writer;
writer.start = malloc(initial_size);
writer.end = writer.start + initial_size;
writer.ptr = writer.start;
writer.ports = NULL;
writer.num_ports = 0;
spc_write(&writer, last_header, 8);
spc_write_uint32(&writer, SPC_TYPE_DICT);
spc_write_dict(&writer, msg->content);
content_size = writer.ptr - writer.start;
char* ptr;
if (writer.num_ports != 0) {
// Must create a complex messge
actual_size = sizeof(mach_msg_header_t) + sizeof(mach_msg_body_t) + writer.num_ports * sizeof(mach_msg_port_descriptor_t) + content_size;
mach_msg = malloc(actual_size);
mach_msg->header.msgh_bits = MACH_MSGH_BITS_COMPLEX;
ptr = (char*)mach_msg + sizeof(mach_msg_header_t);
mach_msg_body_t* body = (mach_msg_body_t*)ptr;
body->msgh_descriptor_count = writer.num_ports;
ptr += sizeof(mach_msg_body_t);
for (size_t i = 0; i < writer.num_ports; i++) {
mach_msg_port_descriptor_t* descriptor = (mach_msg_port_descriptor_t*)ptr;
descriptor->type = MACH_MSG_PORT_DESCRIPTOR;
descriptor->name = writer.ports[i].name;
descriptor->disposition = writer.ports[i].type;
ptr += sizeof(mach_msg_port_descriptor_t);
}
} else {
actual_size = sizeof(mach_msg_header_t) + content_size;
mach_msg = malloc(actual_size);
mach_msg->header.msgh_bits = 0;
ptr = (char*)mach_msg->buf;
}
// Fill in the mach message
mach_msg->header.msgh_remote_port = msg->remote_port.name;
mach_msg->header.msgh_local_port = msg->local_port.name;
mach_msg->header.msgh_id = msg->id;
mach_msg->header.msgh_size = actual_size;
mach_msg->header.msgh_bits |= MACH_MSGH_BITS(msg->remote_port.type, msg->local_port.type);
memcpy(ptr, writer.start, content_size);
free(writer.start);
return mach_msg;
}
void* spc_read(reader_t* reader, size_t len)
{
// Let it crash if the received message is invalid...
if (reader->ptr + len > reader->end) {
printf("OOB read in spc_read\n");
abort();
}
void* res = reader->ptr;
reader->ptr += len;
return res;
}
uint64_t spc_read_uint64(reader_t* reader)
{
return *(uint64_t*)spc_read(reader, 8);
}
int64_t spc_read_int64(reader_t* reader)
{
return *(int64_t*)spc_read(reader, 8);
}
double spc_read_double(reader_t* reader)
{
return *(double*)spc_read(reader, 8);
}
uint32_t spc_read_uint32(reader_t* reader)
{
return *(uint32_t*)spc_read(reader, 4);
}
void* spc_read_padded(reader_t* reader, size_t size)
{
size_t remainder = (4 - (size % 4)) % 4;
return spc_read(reader, size + remainder);
}
char* spc_read_str(reader_t* reader)
{
unsigned char* end = memchr(reader->ptr, 0, reader->end - reader->ptr);
if (!end)
return NULL;
return spc_read_padded(reader, end - reader->ptr + 1);
}
spc_port_t spc_reader_next_port(reader_t* reader)
{
if (reader->next_port >= reader->num_ports)
return SPC_NULL_PORT;
reader->next_port++;
return reader->ports[reader->next_port - 1];
}
spc_value_t spc_deserialize_value(reader_t* reader);
spc_array_t* spc_deserialize_array(reader_t* reader)
{
spc_array_t* array = spc_array_create();
spc_read_uint32(reader);
size_t length = spc_read_uint32(reader);
for (uint32_t i = 0; i < length; i++) {
spc_value_t value = spc_deserialize_value(reader);
spc_array_set_value(array, i, value);
}
return array;
}
spc_dictionary_t* spc_deserialize_dict(reader_t* reader)
{
spc_dictionary_t* dict = spc_dictionary_create();
spc_read_uint32(reader);
dict->num_items = spc_read_uint32(reader);
for (uint32_t i = 0; i < dict->num_items; i++) {
spc_dictionary_item_t* item = malloc(sizeof(spc_dictionary_item_t));
item->key = strdup(spc_read_str(reader));
item->value = spc_deserialize_value(reader);
item->next = dict->items;
dict->items = item;
}
return dict;
}
spc_value_t spc_deserialize_value(reader_t* reader)
{
spc_value_t value;
value.type = spc_read_uint32(reader);
switch (value.type) {
case SPC_TYPE_NULL:
break;
case SPC_TYPE_BOOL:
value.value.u64 = spc_read_uint32(reader);
break;
case SPC_TYPE_UINT64:
value.value.u64 = spc_read_uint64(reader);
break;
case SPC_TYPE_INT64:
value.value.i64 = spc_read_int64(reader);
break;
case SPC_TYPE_DOUBLE:
value.value.dbl = spc_read_double(reader);
break;
case SPC_TYPE_STRING:
spc_read_uint32(reader);
value.value.str = strdup(spc_read_str(reader));
break;
case SPC_TYPE_ARRAY:
value.value.array = spc_deserialize_array(reader);
break;
case SPC_TYPE_DICT:
value.value.dict = spc_deserialize_dict(reader);
break;
case SPC_TYPE_SEND_PORT:
value.value.port = spc_reader_next_port(reader);
break;
case SPC_TYPE_RECV_PORT:
value.value.port = spc_reader_next_port(reader);
break;
case SPC_TYPE_UUID:
value.value.ptr = malloc(0x10);
memcpy(value.value.ptr, spc_read(reader, 0x10), 0x10);
break;
case SPC_TYPE_DATA:
value.value.data.size = spc_read_uint32(reader);
value.value.data.ptr = malloc(value.value.data.size);
memcpy(value.value.data.ptr, spc_read_padded(reader, value.value.data.size), value.value.data.size);
break;
default:
printf("Unsupported value type: 0x%x\n", value.type);
exit(-1);
}
return value;
}
#define MSGID_CONNECTION_INTERRUPTED 71
spc_message_t* spc_deserialize(spc_mach_message_t* mach_msg)
{
reader_t reader;
reader.next_port = 0;
reader.num_ports = 0;
reader.ports = NULL;
reader.end = (unsigned char*)mach_msg + mach_msg->header.msgh_size;
reader.ptr = mach_msg->buf;
// Handle well-known message IDs
if (mach_msg->header.msgh_id == MSGID_CONNECTION_INTERRUPTED) {
spc_dictionary_t* dict = spc_dictionary_create();
spc_dictionary_set_string(dict, "error", "Connection interrupted");
printf("Connection interrupted\n");
// TODO
exit(-1);
}
if (mach_msg->header.msgh_bits & MACH_MSGH_BITS_COMPLEX) {
mach_msg_body_t* body = (mach_msg_body_t*)spc_read(&reader, sizeof(mach_msg_body_t));
for (int i = 0; i < body->msgh_descriptor_count; i++) {
mach_msg_descriptor_type_t type = ((mach_msg_type_descriptor_t*)reader.ptr)->type;
switch (type) {
case MACH_MSG_PORT_DESCRIPTOR: {
reader.ports = realloc(reader.ports, (reader.num_ports + 1) * sizeof(spc_port_t));
mach_msg_port_descriptor_t* descriptor = (mach_msg_port_descriptor_t*)spc_read(&reader, sizeof(mach_msg_port_descriptor_t));
reader.ports[reader.num_ports].name = descriptor->name;
reader.ports[reader.num_ports].type = descriptor->disposition;
reader.num_ports += 1;
break;
}
case MACH_MSG_OOL_DESCRIPTOR:
spc_read(&reader, sizeof(mach_msg_ool_descriptor_t));
printf("Warning: ignoring OOL descriptor\n");
break;
case MACH_MSG_OOL_PORTS_DESCRIPTOR:
spc_read(&reader, sizeof(mach_msg_ool_ports_descriptor_t));
printf("Warning: ignoring OOL ports descriptor\n");
break;
default:
printf("Unsupported mach message descriptor type: %d\n", type);
exit(-1);
}
}
}
void* header = spc_read(&reader, 8);
memcpy(last_header, header, 8);
spc_value_t value = spc_deserialize_value(&reader);
if (value.type != SPC_TYPE_DICT) {
spc_value_destroy(value);
puts("Invalid XPC message type");
return NULL;
}
spc_message_t* msg = malloc(sizeof(spc_message_t));
msg->remote_port.name = mach_msg->header.msgh_remote_port;
msg->remote_port.type = MACH_MSGH_BITS_REMOTE(mach_msg->header.msgh_bits);
msg->local_port.name = mach_msg->header.msgh_remote_port;
msg->local_port.type = MACH_MSGH_BITS_LOCAL(mach_msg->header.msgh_bits);
msg->id = mach_msg->header.msgh_id;
msg->content = value.value.dict;
return msg;
}