直播视频公布 | 窥探有方——调试Released SGX Enclave

 

直播主题

Intel SGX窥探之旅

 

内容简介

SGX是Intel基于CPU扩展的一种革命性的安全技术,旨在提供具有最小攻击面的硬件辅助的可信执行环境。实际应用时主要用来保护使用中的数据的机密性和完整性。本议题主要是探索SGX的工作原理和安全优势,同时分享我们发现的一个SGX安全漏洞一一可将Released Enclave转换成debug模式以窥视其内部数据。
除此之外,本议题也会探索SGX在数据安全领域的一些应用。

 

讲师简介

苏小智

360安全工程院冰刃实验室,安全开发专家。主要研究方向为Linux内核、主动防御、虚拟化及其安全、loT、SGX, 设计并研发了多款终端硬件产品,终端安全和云安全软件产品。

直播视频:


作者:Suezi@360安全工程院冰刃实验室

Intel Software Guard Extensions (Intel SGX)是基于CPU扩展的一种革命性的安全技术,旨在提供具有最小攻击面的硬件辅助的可信执行环境。它允许应用程序或应用程序的一部分运行在一个称为Enclave的安全容器中,任何应用程序,包括OS、Hypervisor、BIOS均无法访问其内容。Enclave使用的页面和数据结构由CPU内部的MEE加密存储在EPC中,负责映射Enclave页面的页表由OS管理,但OS无法获取其内容,仅Enclave可访问。然而攻击者总是想方设法以直接或间接的方式来获取数据,比如隐私数据,加密密钥,或者篡改代码的执行流。分析SGX的工作模型,设法将Release版本的Enclave转换成Debug版本,再借助SGX开发套件中的sgx-gdb工具,可实现对SGX Enclave的动态调试,之后便可为所欲为。

 

1 引言

当程序运行在某个计算平台,特别是在公有云时,其依赖的运行环境可能存在着安全漏洞甚至系统已被攻破,代码和数据的安全性均无法得到保障。此时建立一个可信计算环境(TEE)是十分必要的安全手段和需求,如ARM平台的Trust Zone,Intel的SGX。SGX自Intel的第5代Xeon E3系列CPU开始可用,Intel在CPU内部提供一个具备最小攻击面的的执行环境——SGX,不管是应用程序还是具备root权限的操作系统还是VMM或者BIOS,都无法直接读取或修改Enclave的内容。

与普通的应用程序开发流程类似,Enclave程序在开发过程分为Debug version、PreRelease version、Release Version,不同的版本在构建时使用的密钥类型不一样,Debug版本可采用自生成的密钥,Release版本必须采用Intel签发的,其中Debug版本支持 sgx-gdb调试。sgx-gdb基于普通gdb添加针对SGX的ptrace,除了不支持少数的gdb指令(info sharedlibrary、charset、gcore)和受限支持next/step、call/print 外,使用和功能上与普通gdb无异。

本文以SGX的工作模型为切入点,主要研究如何将Release模式的Enclave转换成Debug,之后借助sgx-gdb工具完成对正常Enclave的窥探。本文提出并实现静态转换和动态转换的两种Released Enclave to Debug的方法。

 

2 SGX概述

根本上,SGX是Intel指令集架构(ISA)的扩展,设计了一套专用的指令集用于创建一个可信执行环境Enclave,可被应用程序用来保存/运行数据或代码。SGX利用CPU提供的指令在内存中划分一部分区域供Enclave使用,这部分内存称为EPC(Enclave Page Cache),并且限定每个EPC页面只能分配给一个Enclave,Enclave使用的页面和数据结构由CPU内部的MEE(Memory Encryption Engine)加密存储在EPC中。EPC的访问控制由跟CPU体系相关的硬件EPCM(Enclave Page Cache Map)负责,任何Non-Enclave都不能访问EPC的内容,任何软件均不可访问EPCM。SGX扩展出一个新的CPU模式,叫做Enclave模式,当CPU要访问Enclave中的数据时,需要切换到Enclave模式。Enclave和Non-Enclave通过EENTER和EEXIT指令进行切换,当Enclave运行过程被中断或异常打断时,Enclave通过AEX(Asynchronous Enclave Exit)机制退出到Non-Enclave模式。在模式切换时,其运行状态会保存到SSA(State Save Area)中,并清除TLB。

相比其他TEE,SGX的TCB(Trust Computing Base)更小,仅局限于Enclave 软件本身和CPU及其固件。从攻击面上对比,普通应用程序和SGX应用程序分别如图1和图2[1]所示。

图1 普通应用程序攻击面

图2 SGX应用程序的攻击面

 

3 SGX 详述

3.1 SGX术语

Enclave:不同的语境下有不同的含义,可以指SGX可信执行环境,也可以指应用程序里将要在可信执行环境里运行的那部分代码。

EPC:Enclave Page Cache,一个加密的内存区域。实现上可以通过BIOS预留计算机中DRAM的一部分做专用内存,再利用CPU的MEE对专用内存进行高效加解密。可有效防止内存嗅探攻击。

MEE:Memory Encryption Engine, CPU中的加密引擎。

EPCM: Enclave Page Cache Map,为了对每个EPC页进行访问控制,在CPU特定的数据结构中维持EPC entry的有效性和Enclave的访问权限(R/W/X)以及EPC页的类型(PT_SECS/PT_TCS/PT_REG/PT_VA/PT_TRIM)。

SSA:State Save Area, 当Enclave切换到Non-Enclave模式时,如Enclave运行中遇到CPU中断,会发生上下文切换,保存Enclave的上下文到EPC中保留区域,这部分区域叫做状态保留区域,在resume时再恢复。

SECS:SGX Enclave Control Structure,每个Enclave都对应着一个SECS,它是用于表示Enclave属性的数据结构,存储在EPC内存页,包含了Enclave所需的元数据信息,如存储Enclave构建时的度量值MRENCLAVE,是硬件保护Enclave的关键要素。文中探究的Enclave Debug属性也是包含在该数据结构中的ATTRIBUTES域里。整个数据结构不可被Non-Enclave 软件访问。

TCS:Thread Control Structure,每个运行着的Enclave拥有一个或者多个TCS。TCS是硬件进入/退出Enclave时用来存储/恢复线程相关信息的数据结构,保存在EPC内存页。TCS的FLAGS域可被Non-Enclave软件访问,TCS.FLAGS.DBGOPTIN 用于使能Enclave的debug属性(如TF, breakpoints),Debug模式的Enclave可通过调试器修改该位。

MRSIGNER:对Enclave签名密钥对中公钥的SHA-256 值。

SIGSTRUCT:Enclave Signature Structure,用于表示Enclave签名相关信息的数据结构。同时也保存着含有Debug 属性的Enclave ATTRIBUTES 域。

EINITTOKEN:EINT Token Structure,Enclave EINIT 时用来检验是否允许该Enclave初始化的令牌。也称为Launch token。令牌数据结构中同时也保存着含有Debug 属性的Enclave ATTRIBUTES 域。

MRENCLAVE:Enclave的唯一识别码,用于记录Enclave从创建到初始化完成整个过程的256 bit的摘要,代表着Enclave本身的TCB。在ECREATE时初始化,EADD/EEXTEND时更新,EINIT后锁定。

AEX:Asynchronous Enclave Exits,Enclave运行时遇到CPU中断等异常事件,退出执行。

3.2 SGX 指令

SGX指令包括特权指令(ENCLS)和用户指令(ENCLU)两大部分,通过在EAX寄存器指定输入值来和ENCLS/ENCLC编码形成不同功能的子函数(指令),其输入/输出参数通过RBX/RCX/RDX寄存器进行指定。ENCLS相关的指令助记符如表1所示。ENCLU相关的指令助记符如表2所示。

表1 ENCLS 指令

指令 EAX RBX RCX RDX 功能简介
ECREATE 00H

(In)

PAGEINFO

(In, EA)

EPCPAGE

(In, EA)

在EPC中创建一个SECS页
EADD 01H

(In)

PAGEINFO

(In, EA)

EPCPAGE

(In, EA)

增加一个新页到未初始化完成的Enclave
EINIT 02H

(In)

SIGSTRUCT

(In, EA)

SECS

(In, EA)

EINITTOKEN

(In, EA)

初始化一个可执行Enclave
EREMOVE 03H

(In)

EPCPAGE

(In, EA)

从EPC删除一页
EDBGRD 04H

(In)

ResultData

(Out)

EPCPAGE

(In, EA)

从Debug Enclave 读取数据
EDBGWR 05H

(In)

SourceData

(In)

EPCPAGE

(In, EA)

向Debug Enclave写入数据
EEXTEND 06H

(In)

EPCPAGE

(In, EA)

扩展未初始化完成的Enclave的度量值
ELDB 07H

(In)

PAGEINFO

(In, EA)

EPCPAGE

(In, EA)

VERSION

(In, EA)

加载一个EPC页并将其状态标记为blocked
ELDU 08H

(In)

PAGEINFO

(In, EA)

EPCPAGE

(In, EA)

VERSION

(In, EA)

加载一个EPC页并将其状态标记为unblocked
EBLOCK 09H

(In)

EPCPAGE

(In, EA)

将一个EPC页标记为blocked
EPA 0AH

(In)

PT_VA(In) EPCPAGE

(In, EA)

在EPC中增加Version Array
EWB 0BH

(In)

PAGEINFO

(In, EA)

EPCPAGE

(In, EA)

VERSION

(In, EA)

使一个EPC页面无效并写回到主内存(DRAM)
ETRACK 0CH

(In)

EPCPAGE

(In, EA)

激活EBLOCK的检查
EAUG 0DH

(In)

PAGEINFO

(In, EA)

EPCPAGE

(In, EA)

LINADDR 为已初始化的Enclave增加一个EPC页
EMODPR 0EH

(In)

SECINFO

(In, EA)

EPCPAGE

(In, EA)

为已初始化的Enclave修改EPC页的访问权限
EMODT 0FH

(In)

SECINFO

(In, EA)

EPCPAGE

(In, EA)

改变EPC页的类型
EA:Effective Address

In:Input parameter

Out:Output parameter

表2 ENCLU指令

指令 EAX RBX RCX RDX 功能简介
EREPORT 00H

(In)

TARGETINOF

(In,EA)

REPORTDATA

(In,EA)

OUTPUTDATA

(In, EA)

创建Enclave的加密报告
EGETKEY 01H

(In)

KEYREQUEST

(In,EA)

KEY

(In,EA)

检索一个加密密钥
EENTER 02H

(In)

TCS

(In,EA)

AEP

(In,EA)

进入Enclave模式执行
RBX.CSSA

(Out)

Return

(Out,EA)

ERESUME 03H

(In)

TCS

(In,EA)

AEP

(In, EA)

重进入Enclave模式执行
EEXIT 04H

(In)

Target

(In,EA)

Current

AEP

(Out)

退出Enclave模式
EACCEPT 05H

(In)

SECINFO

(In,EA)

EPCPAGE

(In,EA)

接受对EPC页面的更改
EMODPE 06H

(In)

SECINFO

(In,EA)

EPCPAGE

(In,EA)

扩展EPC页面的权限
EACCEPTCOPY 07H

(In)

SECINFO

(In,EA)

EPCPAGE

(In,EA)

EPCPAGE

(In,EA)

将现有EPC页面的内容拷贝到未初始化的EPC页面

3.3 SGX 技术实现

SGX技术实现如图3所示,可总结成如下几点:

图3 SGX 技术实现

SGX应用程序切分成Untrusted和Trusted两部分,Trust部分运行在Enclave中;

由Untrusted部分的应用程序通过ioctl系统调用的方式调用ENCLS指令创建出Enclave,并把Trust部分的代码加载到Enclave里执行;

Untrusted部分的应用通过特殊的调用接口Ecalls调用Enclave里函数的执行;

当Enclave里的函数被调用后,仅仅Enclave里的代码可访问其数据,外部的程序——不管是普通的应用程序,还是具备特权的OS、VMM、Bios都无法访问。当Enclave里的函数返回后,其数据仍保留在内存保护区;

Enclave拥有自己的代码和数据区,SGX保证代码和数据的完整和保密性。Enclave的入口点是在编译阶段预定义好,支持多线程,可访问整个Application的内存空间。下一小节详述Enclave.

EPC是SGX的内存管理核心,属于Enclave的相关数据结构和代码以及数据的存放处,是经过MEE加密存储,外部程序无法获取其实际内容,逻辑上如图4所示。

图4 EPC内部逻辑

3.4 Enclave安全机制

形式上,Intel SGX 允许以明文形式发布应用程序的受保护部分,也就是说在Enclave建立之前,Enclave的code和data都是可以自由地进行分析检查的,当这部分受保护的程序加载进Enclave时,它的code和data将会被度量,加载完成后,度量值存放于EPC的SECS,不可更改,来自Enclave外的访问将被拒绝。

应用上,Enclave可以向远方证明自己的身份,并且提供必要的构建块(MRENCLAVE)以安全地提供密钥和凭证。应用程序也可以请求特定于Enclave或平台的密钥,这样就支持保护那些希望存储在Enclave之外的密钥和数据。

原理上,当CPU访问Enclave中数据时,首先切换到Enclave模式,Enclave模式会强制对每个内存访问进行额外的基于硬件的安全检查,由于数据是存放在EPC中,而EPC的内存内容都经过MEE加密,只有当EPC的内存内容进入CPU package时才会被解密,一旦返回EPC后立即被加密。因此即使通过各种内存攻击手段,典型的如内存嗅探,获取到EPC的内容,也是无法获知实际内容的密文。Enclave Enter/Exit的流程如图5所示,进入Enclave模式时,首先通过应

图5 Enclave Entry/Exit 流程

用程序调用ENCLC指令EENTER通知CPU切换Enclave模式,之后将应用程序的上下文保存到TCS数据结构,这样CPU就切换到Enclave模式执行。当Enclave主动退出时,Enclave里的程序调用ENCLC指令EEXIT,切换回Non-Enclave模式。EENTER指令将CPU控制权从应用程序转移到Enclave里的预定位置,它会首先检查TCS是否可用,清空TLB条目,然后切换入Enclave模式,并保存好RSP、RBP和XCR0寄存器内容,最后禁用PEBS(Precise Event Based Sampling),使Enclave执行时像一条巨大的指令。EEXIT指令将进程返回其原始模式,并清除Enclave地址的TLB条目,释放TCS数据结构,另外,Enclave退出前会清空CPU寄存器以防止数据泄露。

当Enclave运行过程被中断或异常打断时,CPU通过AEX机制退回到Non-Enclave模式,在模式切换时,其运行状态会保存到EPC的SSA中,并清除TLB,处理完中断或异常利用ERESUME重进入Enclave并从SSA加载数据恢复先前状态.Enclave AEX 流程如图6所示,AEP(Asynchronous Exit Pointer)指向位于应用程序

图6 Enclave AEX流程

内部的处理程序,在中断服务例程(ISR)处理异常后,该处理程序将继续执行,并决定是否调用ERESUME指令来恢复Enclave的执行。流程上,在Enclave运行过程中,CPU收到了中断/异常,Enclave首先保存其程序上下文后恢复应用程序的上下文,然后操作系统调用ISR处理中断并返回到AEP,若AEP决定要恢复Enclave的执行,它将调用ERESUME指令,Enclave退出前保存的上下文内容将被恢复,最后Enclave从原先退出的地方继续执行。

SGX的内存访问控制流程如图7所示。线性地址转物理地址流程跟传统一样由OS负责,当访问地址指向EPC时,CPU首先检查是否属于Enclave发起的请求,若是再自动到EPCM里检查访问权限是否符合。EPCM检查项包括:

图7 SGX内存访问控制

该内存页是否有效;

该内存页的类型(PT_SECS/PT_TCS/PT_REG,etc.)是否正确;

该内存页是否属于当前的Enclave;

R/W/X访问权限是否匹配;

线性地址是否正确。

Enclave具备如下安全特性:

  1. 不管当前特权级别还是CPU模式(Ring3/用户模式,Ring0/内核模式,SMM,VMM,或是其他Enclave),Enclave安全区内存都无法从外部读取或写入;
  2. 在构建Enclave的时候可以设置debug属性,进行debug签名,再借助sgx-gdb 调试器可以像普通调试器调试普通软件一样调试Enclave。产品级(Released)Enclave不允许通过软件或硬件的形式进行调试(这正是本文要攻破的地方),若强行设置debug属性,将会导致Enclave创建失败,表现在EINIT时异常退出;
  3. Enclave执行环境的唯一方式是通过EENTER/ERESUME这样的SGX新指令在进行一系列安全检查后进入,像传统的函数调用、跳转、寄存器操控、栈操控等方式均无法进入Enclave执行环境。当然,在Enclave内部进行传统的函数调用是没问题的。

3.5 Enclave 的创建

从上文描述可见,在外对Enclave内部不可访问,无法修改运行中Enclave的SECS,因此想通过直接修改SECS的debug域是没法实现的。山重水复疑无路,柳暗花明又一村,我们可以从创建Enclave的过程着手。本文重点探究的将Released Enclave改成可debug模式从此开始。创建Enclave可分为两大过程,首先是创建Enclave内容,即编程和编译,其次是创建其执行环境,即加载执行的过程。

3.5.1 Enclave 内容的创建

图8 SGX 软件开发模型

以Linux系统为例,SGX应用软件发布时在形态上分为App和Enclave.signed.so 两部分,App运行在Non-Enclave模式,属于非可信计算,Enclave.signed.so运行在Enclave模式下,属于可信计算,运行时App利用特定的Enclave接口ECalls调用Enclave.signed.so内函数的执行,同时Enclave.signed.so也可利用特定的接口OCalls来调用App内的函数,典型的如系统调用。SGX软件开发的模型如图8所示,Intel提供了SGX SDK工具包以支持用户快速开发,SDK包含了工具和代码库两大部分,工具包含:用于配置CPUSVN信息的sgx_config_cpusvn、用于解析Enclave Description

Language (EDL) file的sgx_edger8r、用于加密的sgx_encrypt、用于给Enclave.so签名的sgx_sign以及debugger工具sgx-gdb;代码库根据是否属于可信计算分成trusted(如libsgx_trts.a)和untrusted(如libsgx_urts.so)两大部分以及两者间的接口。开发SGX应用时,用户自主决定哪部分代码需要运行在Enclave中,生成Enclave文件的整体操作流程是:

首先需要用户编写期望运行在Enclave中的c/c++代码,并声明在edl文件的”trusted”段中;

然后使用SGX SDK提供的 sgx_edger8r 工具解析edl文件生成Enclave_trust.c文件;

再将Enclave_trust.c和其他c文件编译成enclave.so;

最后使用SGX SDK提供的 sgx_sign 工具对enclave.so进行签名,生成enclave.signed.so。传入sgx_sign工具的文件还包括enclave_private.pem私钥文件和定义有Enclave metadata信息的xml文件,假如要生成产品级Release的Enclave,需要传入的私钥文件是经过Intel进行签发;若是debug版本的可以使用随SGX SDK一起的Samplecode里的Enclave_private.pem。 sgx_sign将所有的metadata数据保存在enclave.signed.so ELF文件里的”.note.sgxmeta”段里。

metadata里保存着Enclave的所有属性,在运行时生成的SECS、TCS关键信息大部分来自于它,包括EINIT所需的EINITTOKEN也是基于它来生成,因此修改metadata里相关信息后可将Enclave由release版本变成可调试的debug版本。下文将重点介绍该部分。

3.5.2 Enclave 执行环境的创建

Enclave的创建由普通应用程序发起,先申请Enclave页,再通过ioctl系统调用执行ENCLS指令,如ECREATE、EADD、EEXTEND、EINIT等,其细节流程如图9所示。首先,应用程序请求加载它的Enclave

图9 Enclave 的创建细节

部分到内存中;接着借助ioctl调用ECREATE指令创建文件和SECS数据结构;然后通过EADD指令将Enclave的代码加载进Enclave;每个EPC页的添加到Enclave时要使用EEXTEND指令将其度量值添加到SECS;Enclave代码加载完成后调用EINIT指令完成Enclave的初始化,固化SECS。该过程跟SECS密切相关的有ECREATE和EINIT,即本文探究的将Released Enclave改成可debug的关键所在。ECREATE指令负责创建一个独一无二的Enclave的实例,建立起线性地址的布局,以及设置Enclave的属性。这属性包括debug属性,信息保存在SECS数据结构中。而 SECS 的固化是在EINIT过程中完成,若简单的在ECREATE时修改SECS,将导致EINIT时对SECS的校验失败,从而导致Enclave创建失败。因此,下节重点介绍EINIT。

3.5.3 Enclave Init

顾名思义,EINIT指令负责Enclave init,它是创建Enclave时最后需要执行的一个ENCLS指令,执行完EINIT后,Enclave的度量值MRENCLAVE也完成了,此后应用程序可以通过EENTER指令进入Enclave运行。EINIT指令执行如图10所示,需传入3个参数:SECS、SIGSTRUCT、EINITTOKEN,其中SECS仅Enclave本身可访问,另两个

图10 EINIT过程

普通程序也可访问。大体执行步骤如下:

验证是否使用随附的公钥对SIGSTRUCT进行了签名;

检查SECS.MRENCLAVE是否等于SIGSTRUCT.HASHENCLAVE;

检查是否SIGSTRUCT.ATTRIBUTES中非保留位设置为1,而SIGSTRUCT.ATTRIBUTESMASK中非保留位设置为设置为0;

检查SIGSTRUCT.ATTRIBUTES中是否未设置仅Intel位,除非SIGSTRUCT由Intel签名;

检查SIGSTRUCT.ATTRIBUTES是否等于对SIGSTRUCT.ATTRIBUTEMASK与SECS.ATTRIBUTES进行逻辑与运算后的结果;

如果EINITTOKEN.VALID为0,则检查SIGSTRUCT是否由Intel签名;

如果EINITTOKEN.VALID为1,则检查EINITTOKEN的有效性;

如果EINITTOKEN.VALID为1,则检查EINITTOKEN.MRENCLAVE是否等于SECS.MRENCLAVE;

如果EINITTOKEN.VALID为1并且EINITTOKEN.ATTRIBUTES.DEBUG为1,则SECS.ATTRIBUTES.DEBUG必须为1;

从SIGSTRUCT取出签名的公钥进行SHA-256的Hash后产生MRSIGNER,将此MRSIGNER与EINITTOKEN.MRSINGER进行检验,通过后将MRSIGNER拷贝到SECS.MRSIGNER。同时基于SIGSTRUCT填写SECS.ISVSVN、SECS.ISVPRODID,完成SECS的初始化,固化SECS。

由上可见,若要调试Enclave,SECS.ATTRIBUTES.DEBUG必须为1,同时EINITTOKEN.ATTRIBUTES.DEBUG和SIGSTRUCT.ATTRIBUTES.DEBUG也必须为1,另外还要保证签名校验的成功。下面分别介绍SECS、SIGSTRUCT、EINITTOKEN的数据结构。

SECS在EPC中是4kB对齐的,数据结构如表3所示。我们比较关心的ATTRIBUTE域如表4所示。

表3 SECS数据结构

Filed Offset

(Bytes)

Size

(Bytes)

简介
SIZE 0 8 Enclave的大小
BASEADDR 8 8 Enclave线性地址的基址
SSAFRAMESIZE 16 4 SSA帧的大小
MISCSELECT 20 4 位向量,用于指定在AEX时将哪些扩展特征保存到SSA.MISC
RESERVED 24 24 预留
ATTRIBUTES 48 16 Enclave的属性
MRENCLAVE 64 32 Enclave的度量值
RESERVED 96 32 预留
MRSIGNER 128 32 签名密钥对应公钥的SHA-256
RESERVED 160 96 预留
ISVPRODID 256 2 Enclave的产品ID
ISVSVN 258 2 Enclave的安全版本号(SVN)
EID 用户设定 8 Enclave ID
PADDING 用户设定 352 填充
RESERVED 260 3836 预留

表4 ATTRIBUTES数据结构

Field Bit Posttion 简介
RESERVED 0 预留
DEBUG 1 置1后,enclave允许调试器读写enclave的数据
MODE64BIT 2 Enclave运行在64位模式
RESERVED 3 必须置0
PROVISIONKEY 4 EGETKEY可获取Provisioning Key
EINITTOKENKEY 5 EGETKEY可获取EINIT token key
RESERVED 63:6 预留
XFRM XSAVE mask

SIGSTRUCT包含了Enclave的签名信息,SHA-256摘要的ENCLAVEHASH,3072bit长度的MODULUS、SIGNATURE、Q1、Q2,也是必须4kB对齐的。Q1、Q2的算法如下:

Q1 = floor(Signature^2 / Modulus);

Q2 = floor((Signature^3 – q1 * Signature * Modulus) / Modulus);

SIGSTRUCT数据结构如表5所示,表中的“Y”代表该域的数据需要纳入需签名的数据里。

表5 SIGSTRUCT数据结构

Field OFFSET

(Bytes)

Size

(Bytes)

简介 Signed
HEADER 0 16 必须是06000000E10000000000010000000000H Y
VENDOR 16 4 Intel Enclave: 00008086H

Non-Intel Enclave: 00000000H

Y
DATE 20 4 编译日期:yyyymmdd Y
HEADER2 24 16 必须是01010000600000006000000001000000H Y
SWDEFINED 40 4 供软件使用 Y
RESERVED 44 84 必须是0 Y
MODULUS 128 384 公钥 N
EXPONENT 512 4 RSA Exponent=3 N
SIGNATURE 516 384 SIGSTRUCT本身的签名 N
MISCSELECT 900 4 用于指定SSA帧扩展特征 Y
MISCMASK 904 4 MISCSELECT的mask Y
RESERVED 908 20 必须是0 Y
ATTRIBUTES 928 16 Enclave的属性 Y
ATTRIBUTEMASK 944 16 ATTRIBUTES的mask Y
ENCLAVEHASH 960 32 本数据结构产生的MRENCLAVE Y
RESERVED 992 32 必须是0 Y
ISVPRODID 1024 2 Enclave产品ID Y
ISVSVN 1026 2 Enclave安全版本号 Y
RESERVED 1028 12 必须是0 N
Q1 1040 384 RSA签名校验值1 N
Q2 1424 384 RSA签名校验值2 N

EINITTOKEN 又称Launch Token,用来检验该Enclave是否允许启动,512 Bytes对齐,其数据结构如表6所示。EINITTOKEN是由Launch Enclave,简称LE生成,LE属于Architectural Enclave之一,由Intel编写并签名后随SGX SDK一起分发。EINITTOKEN里含有ATTRIBUTES字段,并且采用CPU内部的Launch key进行MAC,这样防止它被其他程序改变EINITTOKEN的值。这是本文需要攻破的另一个点,详见下文。

表6 EINITTOKEN数据结构

Field OFFSET

(Bytes)

Size

(Bytes)

MACed 简介
VALID 0 4 Y Bits 0:1 Valid 0:Debug
RESERVED 4 44 Y 必须是0
ATTRIBUTES 48 16 Y Enclave的属性
MRENCLAVE 64 32 Y Enclave的MRENCLAVE
RESERVED 96 32 Y 预留
MRSIGNER 128 32 Y Enclave的MRSIGNER
RESERVED 160 32 Y 预留
CPUSVNLE 192 16 N Launch Enclave的CPUSVN
ISVPRODIDLE 208 2 N Launch Enclave的ISVPRODID
ISVSVNLE 210 2 N Launch Enclave的ISVSVN
RESERVED 212 24 N 预留
MASKEDMISCSELECTLE 236 4 N Launch Enclave的MASKEDMISCSELECT
MASKEDATTRIBUTELE 240 16 N Launch Enclave的MASKEDATTRIBUTE
KEYID 256 32 N Key的保护值
MAC 288 16 N 采用Launch key对EINITTOKEN的MAC

 

4 Released Enclave to Debug静态转换法

上文3.5.1节提到在修改metadata里相关信息后可将Enclave由release版本变成可调试的debug版本,本节将详细探究,这种转换是修改Enclave ELF文件,是在Enclave运行前完成,因此称之为静态转换法。

如上文所述,Enclave的签名工具sgx_sign将所有的metadata数据保存在enclave.signed.so ELF文件里的”.note.sgxmeta”段里。metadata里保存着Enclave的所有属性,在运行时生成的SECS、TCS关键信息大部分来自于它,包括EINIT所需的EINITTOKEN也是基于它来生成,因此修改metadata里相关信息后可将Enclave由release版本变成可调试的debug版本。metadata的数据结构如表7所示,跟当前研究相关的域包括 version、attributes、enclave_css。

表7 metadata数据结构

typedef struct _metadata_t 
{
    uint64_t            magic_num;  /* The magic number identifying the file as a signed enclave image */
    uint64_t            version;               /* The metadata version */
    uint32_t            size;                  /* The size of this structure */
    uint32_t            tcs_policy;            /* TCS management policy */
    uint32_t            ssa_frame_size;        /* The size of SSA frame in page */
    uint32_t            max_save_buffer_size;  /* Max buffer size is 2632 */
    uint32_t            desired_misc_select;
    uint32_t            tcs_min_pool;          /* TCS min pool*/         
    uint64_t            enclave_size;          /* enclave virtual size */
    sgx_attributes_t    attributes;            /* XFeatureMask to be set in SECS. */
    enclave_css_t       enclave_css;           /* The enclave signature */
    data_directory_t    dirs[DIR_NUM];
    uint8_t             data[18592];
}metadata_t;

version代表metadata的版本号,如:2.3/2.1/1.4,为了兼容所有的版本,一个enclave.signed.so里包含有三段metadata,据本人实测发现,这三段metadata仅version域不一样外,其他域完全一样,同时在Ubuntu16.04上安装测试版本的PSW_2.2.100.45311仅支持1.4版本,因此本文所述的sgx_repack_tool仅生成一段version为1.4的metadata。

attributes域的sgx_attributes_t数据结构如表8所示,我们需要将flags添加上SGX_FLAGS_DEBUG 属性,即debug位置1。

表8 sgx_attributes_t数据结构

typedef struct _attributes_t
{
    uint64_t      flags;  /* 包含有debug属性的旗标,各bit含义与Enclave Signature的一致 */
    uint64_t      xfrm;
} sgx_attributes_t;

/* Enclave Flags Bit Masks */
#define SGX_FLAGS_INITTED 0x0000000000000001ULL /* If set, then the enclave is initialized */
#define SGX_FLAGS_DEBUG 0x0000000000000002ULL /* If set, then the enclave is debug */
#define SGX_FLAGS_MODE64BIT 0x0000000000000004ULL /* If set, then the enclave is 64 bit */
#define SGX_FLAGS_PROVISION_KEY 0x0000000000000010ULL /* If set, then the enclave has access to provision key */
#define SGX_FLAGS_EINITTOKEN_KEY 0x0000000000000020ULL /* If set, then the enclave has access to EINITTOKEN key */
#define SGX_FLAGS_RESERVED       (~(SGX_FLAGS_INITTED | SGX_FLAGS_DEBUG | SGX_FLAGS_MODE64BIT | SGX_FLAGS_PROVISION_KEY | SGX_FLAGS_EINITTOKEN_KEY))

表9 enclave_css_t数据结构

typedef struct _enclave_css_t {        /* 1808 bytes */
    css_header_t    header;             /* (0) */
    css_key_t       key;                /* (128) */
    css_body_t      body;               /* (900) */
    css_buffer_t    buffer;             /* (1028) */
} enclave_css_t;

typedef struct _css_buffer_t {         /* 780 bytes */
    uint8_t  reserved[12];         /* (1028) Must be 0 */
    uint8_t  q1[SE_KEY_SIZE];     /* (1040) Q1 value for RSA Signature Verification */
    uint8_t  q2[SE_KEY_SIZE];    /* (1424) Q2 value for RSA Signature Verification */
} css_buffer_t;
其中,q1 = floor(Signature^2 / Modulus);
q2 = floor((Signature^3 - q1 * Signature * Modulus) / Modulus);

表9 enclave_css_t数据结构(续)

typedef struct _css_header_t {        /* 128 bytes */
    uint8_t  header[12];       /* (0) must be (06000000E100000000000100H) */
    uint32_t type;            /* (12) bit 31: 0 = prod, 1 = debug; Bit 30-0: Must be zero */
    uint32_t module_vendor;             /* (16) Intel=0x8086, ISV=0x0000 */
    uint32_t date;                      /* (20) build date as yyyymmdd */
    uint8_t  header2[16];   /* (24) must be (01010000600000006000000001000000H) */
    uint32_t hw_version;  /* (40) For Launch Enclaves: HWVERSION != 0. Others, HWVERSION = 0 */
    uint8_t  reserved[84];              /* (44) Must be 0 */
} css_header_t;

typedef struct _css_key_t {           /* 772 bytes */
    uint8_t modulus[SE_KEY_SIZE];    /* (128) Module Public Key (keylength=3072 bits) */
    uint8_t exponent[SE_EXPONENT_SIZE]; /* (512) RSA Exponent = 3 */
    uint8_t signature[SE_KEY_SIZE];     /* (516) Signature over Header and Body */
} css_key_t;

typedef struct _css_body_t {            /* 128 bytes */
    sgx_misc_select_t   misc_select;    /* (900) The MISCSELECT that must be set */
    sgx_misc_select_t   misc_mask;      /* (904) Mask of MISCSELECT to enforce */
    uint8_t             reserved[20];   /* (908) Reserved. Must be 0. */
    sgx_attributes_t    attributes;     /* (928) Enclave Attributes that must be set */
    sgx_attributes_t    attribute_mask; /* (944) Mask of Attributes to Enforce */
    sgx_measurement_t   enclave_hash;   /* (960) MRENCLAVE - (32 bytes) */
    uint8_t             reserved2[32];  /* (992) Must be 0 */
    uint16_t            isv_prod_id;    /* (1024) ISV assigned Product ID */
    uint16_t            isv_svn;        /* (1026) ISV assigned SVN */
} css_body_t;

enclave_css域代表的是Enclave Signature Structure,其代码形式的数据结构enclave_css_t如表9所示。该数据结构中必须修改body域中的attributes及对应的attribute_mask,将attributes的flags置上SGX_FLAGS_DEBUG,将attribute_mask的flags的 SGX_FLAGS_DEBUG清零;同时需要修改header域的type,将其第31位置1,代表需要debug;再将header域的module_vendor置成0,伪装成非Intel发布。因为修改header和body影响了key域的signature签名,所以须对Enclave Signature Structure进行重签名,操作时将key域的modulus置换成Enclave debug 私钥对应的公钥,再使用私钥对Enclave Signature Structure的header和body域进行签名。因为key域的改变,buffer域的q1和q2也需要根据公式

q1 = floor(Signature^2 / Modulus);

q2 = floor((Signature^3 – q1 * Signature * Modulus) / Modulus);

进行修正 。

至此,将Released Enclave转换成debug版本已经呼之欲出,我们通过编写一个sgx_repack_tool工具将上述的修改操作自动化完成,将enclave_release.signed.so的metadata的相应位域修改后生成enclave_debug.signed.so,这样通过SGX SDK发布的sgx-gdb工具可以对enclave_debug.signed.so进行调试。如图11所示,将随同 SGX SDK一起发布Samplecode的Enclave_private.pem和enclave_release.signed.so文件做为输入,经过sgx_repack_tool 工具转换后生成可debug的enclave_debug.signed.so。

图11 sgx_repack_tool

静态转换法所需的修改点,总结如表11所示。

表11 需要修改的metadata数据域

序号 数据域
1 Metadata.version
2 Metadata.attributes.flags
3 SIGSTRUCT.TYPE
4 SIGSTRUCT.VENDOR
5 SIGSTRUCT. ATTRIBUTES
6 SIGSTRUCT.ATTRIBUTEMASK
7 SIGSTRUCT. MODULUS
8 SIGSTRUCT.SIGNATURE
9 SIGSTRUCT.Q1
10 SIGSTRUCT.Q2

 

5 Released Enclave to Debug动态转换法

静态转换法需要事先拿到enclave_release.sign.so后转换,当运行Enclave的时候还需要在Ring0程序创建Enclave的API sgx_create_enclave将Debug_Flag 参数置1,实现上可通过HOOK Ring0的Application的sgx_create_enclave ,将其Debug_Flag参数置1。但这样使用起来存在一定的局限性,本节探究一种更高级的方法,不需要HOOK Ring0的Application,也不需要修改enclave_release.sign.so,就在用户正常使用SGX Application的时候动态修改,实现debug无感知。我们称这种方法为Released Enclave to Debug动态转换法。

动态转换法的关键是修改Enclave的SECS.ATTRIBUTES.DEBUG,但SECS对Enclave外所有程序不可见。因此从SECS的创建入手,即Enclave的ECREATE和EINIT。ECREATE负责创建SECS的EPC页,在其参数secs上直接修改两处:

  1. secs.attributes |= SGX_FLAGS_DEBUG
  2. secs.mrsigner 替换成我们自己的debug模式的公钥的sha256

即可。比较复杂的是EINIT,上文分析EINIT过程可见,EINIT所需的参数SIGSTRUCT、EINITTOKEN这两个数据结构对普通应用程序可见,SECS的初始化依赖SIGSTRUCT,如若成功在SIGSTRUCT、EINITTOKEN里修改了DEBUG属性,那么SECS.ATTRIBUTES.DEBUG也将成功被置位,这样Enclave就变得可被debug。SIGSTRUCT的修改轻而易举,修改后仅需注意使用debug 密钥重新生成新的签名即可,关键问题在于EINITTOKEN。EINITTOKEN由LE生成,生成后虽然对普通程序可见,但是其内容经过LE key签名,我们无法获取LE key,意味着修改EINITTOKEN后而没有重签名,在EINIT时对EINITTOKEN的校验将会失败。那么,能不能在EINITTOKEN生成的过程下手,将其内容篡改呢?答案是肯定的。下面分析EINITTOKEN的生成过程。

EINITTOKEN的生成逻辑如图12所示,在Linux版本的SGX中,LE 属于Architectural Enclave,随SDK一起分发,运行时受aesm(Architectural Enclave Service Manager)守护进程管理。需EINITTOKEN时,应用程序将Enclave的MRENCLAVE、MRSIGNER、ATTRIBUTES等以protobuf形式封包,通过socket方式向aesm发起请求,aesm调用LE生成EINITTOKEN,再以protobuf封装EINITTOKEN

图12 EINITTOKEN生成逻辑

通过socket方式返回给应用程序。如若在此过程中,将MRSIGNER改成我们自己debug密钥对中公钥的SHA-256,同时将ATTRIBUTES的DEBUG置位,那么将可得到具备debug功能的EINITTOKEN。另外,跟静态转换法一样,用同样的debug密钥修改SIGSTRUCT的ATTRIBUTES和相关域。动态转换法可简单的概述为:保持enclave_release.signed.so不变,在其Enclave创建的过程修改debug属性,并用自己可控的debug版本的密钥替换掉原有的released密钥信息来完成ECREATE和EINIT。实测发现,经此修改后仍出现问题,原因在于应用程序使用SGX SDK中的libsgx_urts.so做加载Enclave的初始化相关工作,如sgx_create_enclave/sgx_create_enclave_ex ,欲加载的Enclave是否采用debug模式运行以debug参数的形式传入sgx_create_enclave/sgx_create_enclave_ex  API,而后借助get_misc_attr函数对欲加载的Enclave的attribute进行校验,若Enclave的metadata信息标明该Enclave为release版本,此刻的EINITTOKEN却要使能attribute的debug位,将导致校验失败,从而退出Enclave的初始化加载。因此,还要设法绕过此检测,方法也比较简单,因为函数里仅能检测debug位是否匹配,无法检测EINITTOKEN的签名信息是否正确,所以操作上仅需将生成的带debug功能的EINITTOKEN的debug位清零后供get_misc_attr函数进行校验即可。

 

图13 ECREATE 篡改流程

图14 EINIT 篡改流程

综上,动态转换法实现时采取HOOK SGX Driver,同时在Ring3运行SGX Debug Helper的应用程序,SGX应用程序本身不做任何修改。其中HOOK SGX Driver负责捕获请求EINITTOKEN时的报文和转发到SGX Debug Helper,同时HOOK Enclave ECREATE 和 EINIT,修改其指令参数;SGX Debug Helper负责管理签名密钥和篡改来自HOOK SGX Driver的protobuf的报文。ECREATE的流程如图13所示,在SGX Driver中HOOK ECREATE的函数,当SGX应用程序通过ioctl调用ECREATE函数时,向SGX Debug Helper程序请求debug 公钥的MRSIGNER,获取到MRSIGNER后篡改ECREATE参数secs.mrsigner和secs.attributes 。EINIT的流程如图14所示,SGX Driver HOOK socket,当SGX应用程序通过SGX SDK中的GetLauchTokenRequest函数发出请求EINITTOKEN时,将其protobuf报文转发到SGX Debug Helper,由SGX Debug Helper修改报文里的attribute和mrsigner并重新生成新的protobuf报文给SGX HOOK Driver,之后SGX HOOK Driver将新生成的protobuf报文转给aesm,aesm调用LE获取到EINITTOKEN后通过SGX SDK函数GetLaunchTokenResponse将EINITTOKEN以protobuf形式返回,同样此响应的socket被SGX HOOK Driver拦获并转发给SGX Debuge Helper, 由SGX Debuge Helper篡改响应报文——生成real EINITTOKEN和fake EINITTOKEN(针对real EINITTOKEN修改其attribute为not debug),并将这两个token一并发送给SGX HOOK Driver,SGX HOOK Driver自己留住real EINITTOKEN,将fake EINITTOKEN 以protobuf形式发送给SGX 应用程序,此后SGX应用程序采用fake EINITTOKEN 通过get_misc_attr函数进行校验并成功,最后借助ioctl系统调用执行EINIT指令,此时SGX HOOK Drvier对EINIT参数EINITTOKEN修正为real EINITTOKEN,再篡改SIGSTRUCT参数,EINIT 成功执行,大功告成。

动态转换法的主要思想是在SGX SDK中libsgx_uae_service.so 的 oal_get_launch_token函数通过socket 以protobuf封装参数信息,向aesm请求产生EINITTOKEN前,将protobuf的信息进行篡改以产生支持debug的EINITTOKEN;同时在SGX 驱动中将ECREATE和EINIT的参数SECS和SIGSTRUCT进行修改。

请求生成EINITTOKEN的修改点:

  1. signature.key.modules 将其改成我们自己的debug模式的公钥;
  2. sgx_attributes_t.flags |= SGX_FLAGS_DEBUG.

ECREATE的修改:

将ECREATE所需参数secs进行如下修改

  1. secs.attributes |= SGX_FLAGS_DEBUG;
  2. secs.mrsigner 替换成我们自己的debug模式的公钥的sha256.

EINIT的修改:

将EINIT所需的SIGSTRUCT参数进行修改,如表12所示

表12  动态转换时SIGSTRUCT的修改处

序号 数据域
1 SIGSTRUCT.header.header1[1] |=0x1ULL <<63
2 SIGSTRUCT.header.vendor = 0
3 SIGSTRUCT.body.attributes |= SGX_FLAGS_DEBUG
4 SIGSTRUCT.body.attributemask[0] &= ~SGX_FLAGS_DEBUG
5 SIGSTRUCT.modulus
6 SIGSTRUCT.signature
7 SIGSTRUCT.q1
8 SIGSTRUCT.q2

 

6 总结

本文从SGX的工作模型入手,研究了SGX的工作原理,特别是Enclave的保护机制和它的创建过程,从Enclave的创建过程得到启发,实现了静态转换和动态转换不同的两种方法对产品级的Enclave实现了debug调试。研究是在2018年进行,基于Linux操作系统,当时SGX的最新版本是2.2,测试验证工作也是基于2.2版本进行。现在已进入20年代,SGX新版本也到了2.6,在新版本上测试验证工作留给感兴趣的读者自行完成。

参考文献

[1]Frank McKeen.Intel® Software Guard Extensions(Intel® SGX) [EB/OL].http://web.stanford.edu/class/ee380/Abstracts/150415-slides.pdf

[2]Intel.Intel® Software Guard Extensions Programming

Reference. OCTOBER 2014

[3] Alexandre Adamski. Overview of Intel SGX – Part 1, SGX Internals [EB/OL]. https://blog.quarkslab.com/overview-of-intel-sgx-part-1-sgx-internals.html

(完)