目录
SGX SDK补丁——0001-reconfigure-AEP-TCS-ebase.patch(不太重要,为了显示Enclave相关信息)
将管理Enclave的汇编函数进行包装,放到urts.cpp
攻击者配置运行时【attacker_config_runtime】
获取受害者信息【register_enclave_info】
配置页表【attacker_config_page_table】
将真实的秘密值(64字节)从Enclave拷贝出来,并与偷到的值进行比对
SGX SDK补丁——0001-reconfigure-AEP-TCS-ebase.patch(不太重要,为了显示Enclave相关信息)
攻击者在URTS端安插管理Encalve的函数声明
攻击者在sgx_urts.h(urts就是untrusted runtimes,是APP的不可信部分)插入几个获取和设置Enclave AEP、TCS、Enclave加载基址(虚拟地址)等信息的函数声明。
diff --git a/common/inc/sgx_urts.h b/common/inc/sgx_urts.h
index 6c9d8a2..7253142 100644
--- a/common/inc/sgx_urts.h
+++ b/common/inc/sgx_urts.h
@@ -76,6 +76,12 @@ typedef struct _sgx_kss_config_t
extern "C" {
#endif
+//XXX
+void* SGXAPI sgx_get_aep(void);
+void SGXAPI sgx_set_aep(void *aep);
+void* SGXAPI sgx_get_tcs(void);
+void SGXAPI sgx_set_load_ptr(void *load_ptr);
+
typedef uint8_t sgx_launch_token_t[1024];
/* Convenient macro to be passed to sgx_create_enclave(). */
攻击者在uRTS端自定义设置Enclave基址
攻击者在/psw/urts/linux/enclave_creator_hw.cpp安插sgx_load_ptr指针和设置该指针的函数。该指针的目的是在创建Enclave时候设置一个(可选的)Enclave基址偏好。(原本提供的enclave_create的第一个参数默认置为NULL,常用的sgx_create_enclave没有向开发者暴露用于设置基址的参数,作者想设置这个Enclave基址偏好)。
diff --git a/psw/urts/linux/enclave_creator_hw.cpp b/psw/urts/linux/enclave_creator_hw.cpp
index f1d914d..d3879b7 100644
--- a/psw/urts/linux/enclave_creator_hw.cpp
+++ b/psw/urts/linux/enclave_creator_hw.cpp
@@ -54,6 +54,8 @@
static EnclaveCreatorHW g_enclave_creator_hw;
+void* sgx_load_ptr = NULL;
+
EnclaveCreator* g_enclave_creator = &g_enclave_creator_hw;
static uint64_t g_eid = 0x1;
@@ -166,7 +168,12 @@ int EnclaveCreatorHW::error_api2urts(uint32_t api_error)
return ret;
}
-
+
+void sgx_set_load_ptr(void* load_ptr)
+{
+ sgx_load_ptr = load_ptr;
+}
+
int EnclaveCreatorHW::create_enclave(secs_t *secs, sgx_enclave_id_t *enclave_id, void **start_addr, bool ae)
{
assert(secs != NULL && enclave_id != NULL && start_addr != NULL);
@@ -177,7 +184,7 @@ int EnclaveCreatorHW::create_enclave(secs_t *secs, sgx_enclave_id_t *enclave_id,
return SGX_ERROR_UNEXPECTED;
uint32_t enclave_error = ENCLAVE_ERROR_SUCCESS;
- void* enclave_base = enclave_create(NULL, (size_t)secs->size, 0, ENCLAVE_TYPE_SGX2, &enclave_create_sgx, sizeof(enclave_create_sgx_t), &enclave_error);
+ void* enclave_base = enclave_create(sgx_load_ptr, (size_t)secs->size, 0, ENCLAVE_TYPE_SGX2, &enclave_create_sgx, sizeof(enclave_create_sgx_t), &enclave_error);
if (enclave_error)
return error_api2urts(enclave_error);
攻击者在uRTS端汇编层面安插管理Encalve的函数定义
攻击者在每次EENTER前,将TCS记录到g_tcs,将AEP记录到g_aep_pointer,并允许攻击者修改AEP。作者的目的就是在比如AEX结束返回到tRTS前,能够添加一些自定义的操作流程。
diff --git a/psw/urts/linux/enter_enclave.S b/psw/urts/linux/enter_enclave.S
index fcc5da8..ff992b6 100644
--- a/psw/urts/linux/enter_enclave.S
+++ b/psw/urts/linux/enter_enclave.S
@@ -32,6 +32,29 @@
#include "enter_enclave.h"
+/* XXX runtime reconfigurable indirect Asynchronous Exit Pointer (AEP)
+ * (ld complains when initializing __default_async_exit_pointer here, so we have
+ * to do it at runtime, when EENTERing, below in .Ldo_eenter.
+ */
+ .data
+g_aep_pointer:
+ .word 0x0
+ .word 0x0
+ .word 0x0
+ .word 0x0
+
+/* XXX HACK: SGX stores TCS address in rbx on interrupt, but this value is
+ * somehow not properly stored in Linux's pt_regs struct available to our
+ * driver's interrupt handler. We therefore store TCS address here in the
+ * untrusted runtime, so as to be able to explicitly communicate TCS to our
+ * driver...
+ */
+ .data
+g_tcs:
+ .word 0x0
+ .word 0x0
+ .word 0x0
+ .word 0x0
/* int __morestack(const tcs_t *tcs, const int fn, const void *ocall_table, const void *ms, CTrustThread *trust_thread); */
.file "enter_enclave.S"
@@ -72,9 +95,17 @@ EENTER_PROLOG
je 1f
vzeroupper
1:
- mov frame_arg0, %xbx /* tcs addr */
- lea_pic .Lasync_exit_pointer, %xcx /* aep addr */
- mov $SE_EENTER, %xax /* EENTER leaf */
+ mov frame_arg0, %xbx /* tcs addr */
+ lea_pic g_tcs, %xax
+ mov %xbx, (%xax)
+ /* fetch AEP; init when NULL */
+ lea_pic g_aep_pointer, %xax
+ mov (%xax), %xcx /* aep addr */
+ cmp $0x0, %xcx
+ jnz 1f
+ lea_pic __default_async_exit_pointer, %xcx
+ mov %xcx, (%xax)
+1: mov $SE_EENTER, %xax /* EENTER leaf */
.Leenter_inst:
ENCLU
@@ -132,14 +163,26 @@ EENTER_PROLOG
.Loret:
EENTER_EPILOG
-.Lasync_exit_pointer:
+__default_async_exit_pointer:
ENCLU
.size __morestack, .-__morestack
-DECLARE_GLOBAL_FUNC get_aep
- lea_pic .Lasync_exit_pointer, %xax
+ DECLARE_GLOBAL_FUNC get_aep
+ lea_pic g_aep_pointer, %xax
+ mov (%xax), %xax
+ ret
+
+DECLARE_GLOBAL_FUNC set_aep
+ lea_pic g_aep_pointer, %xax
+ mov naked_arg0, %xbx
+ mov %xbx, (%xax)
+ ret
+
+DECLARE_GLOBAL_FUNC get_tcs
+ lea_pic g_tcs, %xax
+ mov (%xax), %xax
ret
DECLARE_GLOBAL_FUNC get_eenterp
将管理Enclave的汇编函数进行包装,放到urts.cpp
diff --git a/psw/urts/linux/urts.cpp b/psw/urts/linux/urts.cpp
index d22dddb..5abf27d 100644
--- a/psw/urts/linux/urts.cpp
+++ b/psw/urts/linux/urts.cpp
@@ -40,6 +40,26 @@
#include "urts_com.h"
+//XXX
+extern "C" void *get_aep();
+extern "C" void set_aep(void *aep);
+extern "C" void *get_tcs();
+
+extern "C" void* sgx_get_aep(void)
+{
+ return get_aep();
+}
+
+extern "C" void* sgx_get_tcs(void)
+{
+ return get_tcs();
+}
+
+extern "C" void sgx_set_aep(void *aep)
+{
+ set_aep(aep);
+}
+
static bool inline _check_ex_params_(const uint32_t ex_features, const void* ex_features_p[32])
{
//update last feature index if it fails here
lds文件中新增uRTS暴露的接口
diff --git a/psw/urts/linux/urts.lds b/psw/urts/linux/urts.lds
index 5ba4bd1..baf8197 100644
--- a/psw/urts/linux/urts.lds
+++ b/psw/urts/linux/urts.lds
@@ -1,5 +1,9 @@
{
global:
+ sgx_get_aep;
+ sgx_set_aep;
+ sgx_get_tcs;
+ sgx_set_load_ptr;
sgx_create_enclave;
sgx_create_enclave_ex;
sgx_destroy_enclave;
给SGX Simulation模式添加函数定义
diff --git a/sdk/simulation/uinst/u_instructions.cpp b/sdk/simulation/uinst/u_instructions.cpp
index 0b0486f..42d1146 100644
--- a/sdk/simulation/uinst/u_instructions.cpp
+++ b/sdk/simulation/uinst/u_instructions.cpp
@@ -57,6 +57,13 @@ static uintptr_t _ECREATE (page_info_t* pi);
static uintptr_t _EADD (page_info_t* pi, void* epc_lin_addr);
static uintptr_t _EREMOVE(const void* epc_lin_addr);
+void* sgx_load_ptr = NULL;
+
+void sgx_set_load_ptr(void* load_ptr)
+{
+ sgx_load_ptr = load_ptr;
+}
+
#define __GP__() exit(EXIT_FAILURE)
@@ -154,7 +161,7 @@ uintptr_t _ECREATE(page_info_t* pi)
// `ce' is not checked against NULL, since it is not
// allocated with new(std::no_throw).
- addr = se_virtual_alloc(NULL, (size_t)secs->size, MEM_COMMIT);
+ addr = se_virtual_alloc(sgx_load_ptr, (size_t)secs->size, MEM_COMMIT);
if (addr == NULL) {
delete ce;
return 0;
diff --git a/sdk/simulation/urtssim/urts_deploy.c b/sdk/simulation/urtssim/urts_deploy.c
index a383efd..1f116ee 100644
--- a/sdk/simulation/urtssim/urts_deploy.c
+++ b/sdk/simulation/urtssim/urts_deploy.c
@@ -57,6 +57,23 @@ sgx_status_t sgx_create_encrypted_enclave()
}
+void *sgx_get_aep(void)
+{
+ printf("Please use the correct uRTS library from PSW package.\n");
+ return NULL;
+}
+
+void sgx_set_aep(void* p)
+{
+ printf("Please use the correct uRTS library from PSW package.\n");
+}
+
+void *sgx_get_tcs(void)
+{
+ printf("Please use the correct uRTS library from PSW package.\n");
+ return NULL;
+}
+
void sgx_debug_load_state_add_element(){};
void sgx_debug_unload_state_remove_element(){};
void sgx_destroy_enclave(){};
Foreshadow攻击
攻击者创建Enclave
攻击者配置运行时【attacker_config_runtime】
调用【sched_setaffinity】指定受害者CPU逻辑核
通过对目录(/sys/devices/system/cpu/intel_pstate/)下的文件内容进行操作,设置PSTATE最小值(19->100)、最大值(100->100),来保证处理器始终处于百分百功率,关闭Turbo(开启Turbo会关闭部分核心,让电力集中到某个核心,但整个CPU的总功率TDP不变)。
使用【signal】函数设置【SIGSEGV】的回调函数为【fault_handler】函数。
【SIGSEGV】的回调函数【fault_handler】
- MARK_PRESENT,将【pte_alias】这个PTE里指向的地址重新设置为PRESENT。
- 使用mprotect将alias_ptr起始的0x1000长度的虚拟地址空间恢复读写权限(PROT_READ | PROT_WRITE)
- 将【alias_ptr】重新标记为RW。
- (这样就能绕过页面中止语义了)
获取受害者信息【register_enclave_info】
- 使用前面PSW补丁插桩的函数获取受害者Enclave的AEP和TCS信息。
- 【step_open】打开sgx-step驱动
- 通过驱动调用【find_vma】获取Enclave的基址和大小。
- 将这些打印出来
配置页表【attacker_config_page_table】
在Enclave内产生一个随机数,随机数的线性地址【secret_ptr】(tRTS内的数据)会交给(不可信的uRTS端的)APP,将【secret_ptr】的页号保存到【secret_page】,APP会将【secret_ptr】PAGE级别(页内数据的物理地址)地重映射到【alias_ptr】。再将【alias_ptr】PTE级别(PTE项的物理地址)地重映射到【pte_alias】,并将【pte_alias】里包含的地址(指向PAGE)的P位清除。将【alias_ptr】用mprotect撤销页权限【PROT_NONE】。
重映射的做法是:先(通过sgx-step驱动)将给定虚拟地址的各级页表项的物理地址记录到【address_mapping_t】结构,后续根据所需的Level来确定某级页表项(某级页表+该级页表内偏移)的物理地址,然后将【/dev/mem+物理地址】的内容给映射到虚拟地址中。
(这么做也就是在原来的虚拟地址转物理地址映射表机制外新开了一个别名映射表,用来设置PTE,具体见论文“错误压制”部分)。
Foreshadow初始化
测试普通整数缓存未命中加载时长【t1】和缓存命中加载时长【t2】,确定判断数据是否在缓存的阈值【t1-t2-60】。
把连续256个页的Oracle内存加载进缓存。
Enclave重加载秘密【enclave_reload】
进入Enclave,在Enclave内将【secret_ptr】内容加载到EAX,由于处理器会一次将包含【secret_ptr】所值内容在内的64个字节都加载进缓存,因此64个字节的秘密都被加载到了同一个Cache Line。
针对64字节秘密中的每一个字节,缓存侧信道判断该字节是什么
对于每个目标秘密字节,需要用到前面准备的Oracle内存。由于一个字节表示范围是256,所以Oracle槽数也需要是256个,才能探测一个字节所表示的所有可能值。
为了判断目标秘密字节最终会落在哪个槽上,需要从第0个到第255个槽遍历查看是否有缓存泄露。使用TSX和不使用TSX的做法不一样:
使用TSX
对于每一个槽(槽与槽之间相隔4K也就是一个页的大小),具体做法是先刷新这个槽,然后【transient_access】会用到【alias_ptr】(【secret_ptr】的别名)将某个槽提到缓存中,然后重加载这个槽,如果重加载时Cache命中(如果低于阈值),说明当前目标秘密字节就是这个槽的编号。
【transient_access】执行过程中,会触发页错误(NOT PRESENT)进入瞬态执行来提取槽。因为使用了TSX,可以抑制页错误(TSX会回滚)进而避免错误处理句柄的执行对Cache Line的影响。
不使用TSX
先将所有槽清了,然后执行一次【transient_access】,然后检查槽判断哪个Cache Line被访问了。
由于不使用TSX,【transient_access】执行过程中的页错误无法抑制,会进入一开始准备的【fault_handle】错误句柄,其中错误句柄会将P位恢复,将【alias_ptr】恢复。但就光这一次的错误句柄执行就十分影响Cache Line。
而上一种情况可以一个槽一个槽地执行多次瞬态执行,因为有TSX抑制错误,几乎不会对Cache Line产生影响。
此外,由于错误句柄中将别名页和PTE权限恢复了,那么每偷完一个目标秘密字节,都需要重新将别名页和PTE权限去除。
稍微提一句
从上面可以看到,页错误时会进入错误句柄或者被抑制,不过总归不会触发SGX的页面中止语义。
将真实的秘密值(64字节)从Enclave拷贝出来,并与偷到的值进行比对
DUMP SSA
不使用TSX时,获取SSA是在SIGSEGV处理句柄中使用foreshadow来获取,SIGSEGV处理过程中触发的SIGSEGV,似乎不再能被SIGSEGV处理句柄给捕获。