Finding Secrets...
One of the main advantages of SGX technology is that it cannot only protect against external attackers but also against internal attackers that have root access. We introduce a simple approach to demonstrate that your application is indeed running inside of an enclave and that its secrets are not accessible.
Note
To ensure that your SCONE application runs inside of an enclave and to distribute secrets, please to use the SCONE configuration and attestation service.
To demonstrate how one can check that SGX/SCONE can protect against users with root access1, let's consider a simplistic example: we store some secret in a variable secret2. By default, binaries are not encrypted. Hence, do not store any secrets in a binary as it is done in this simplistic example.
If you really need to to protect your binaries - for example, since you have some legacy code with embedded secrets - SCONE can protect these secrets by permitting to encrypt shared libraries.
Example
We use the SCONE crosscompiler image and start an container with access to the SGX device. We determine which SGX device to mount with function determine_sgx_device.
determine_sgx_device
docker pull registry.scontain.com/sconecuratedimages/crosscompilers
docker run $MOUNT_SGXDEVICE -it registry.scontain.com/sconecuratedimages/crosscompilers
Let's create some program that stores a secret (MYBIGS) in a local variable secret:
cat > mysecret.c << EOF
#include <stdio.h>
#include <unistd.h>
const char *code_is_not_encrypted="THIS_IS_NOT_SECRET";
int main() {
char secret[7];
secret[0] ='M';
secret[1] ='Y';
secret[2] ='B';
secret[3] ='I';
secret[4] ='G';
secret[5] ='S';
secret[6] =0;
printf("'%s' SECRET at %lx\n", secret, secret);
printf("Kill with Ctrl-C.\n");
for(;;)
sleep(1); // loop forever
}
EOF
Compile this program with the SCONE crosscompiler (i.e., gcc):
gcc -g -o mysecret mysecret.c
Simulation Mode
You can run this program in SIMULATION MODE, i.e., this program does not protect your secrets:
SCONE_VERSION=1 SCONE_MODE=SIM ./mysecret
Log into a different terminal on your host. Let us figure out the process ID of the mysecret program:
SPID=$(ps -a | grep -v grep | grep mysecret | awk '{print $1}')
Now, we can dump the memory of this process via the /proc filesystem. You can determine
the different memory regions of your process via cat /proc/$SPID/maps
and
the memory is stored in /proc/$SPID/mem. We can use the following
Python program to write all pages to stdout:
cat > dumpmemory.py << EOF
import sys,re
pid = sys.argv[1]
print("PID = %s \n" % pid)
maps_file = open("/proc/%s/maps" % pid, 'r')
mem_file= open("/proc/%s/mem" % pid, 'rb')
for line in maps_file.readlines():
m = re.match(r'([0-9A-Fa-f]+)-([0-9A-Fa-f]+) ([-r][-w])', line)
if m.group(3) == "rw" or m.group(3) == "r-" :
try:
start = int(m.group(1), 16)
if start > 0xFFFFFFFFFFFF:
continue
print("\nOK : \n" + line+"\n")
end = int(m.group(2), 16)
mem_file.seek(start)
chunk = mem_file.read(end - start)
print(chunk)
sys.stdout.flush()
except Exception as e:
print(str(e))
else:
print("\nPASS : \n" + line+"\n")
print("END")
EOF
Run this program and grep for the prefix of our secret:
sudo python3 dumpmemory.py $SPID >& memdump.log
grep -o MYBIGS memdump.log
This will take some time but eventually it will print the full secret MYBIGS.
Enclaves in SIM mode
You will find your secret in memory region used by enclave but you will notice that SGX device is not used.
Hardware Mode
Let us now run the program in hardware mode. First, ensure that you kill the original program by typing control-C.
Let's start mysecret in an enclave, i.e., in hardware mode inside the container:
SCONE_VERSION=1 SCONE_MODE=HW ./mysecret
Update environment variable SPID in a second terminal on your host:
SPID=$(ps -a | grep -v grep | grep mysecret | awk '{print $1}')
and then try to find the secret:
sudo python3 dumpmemory.py $SPID >& memdump_HW.log
grep -o MYBIGS memdump_HW.log
And in few seconds you will see your secret.
Enclaves in debug mode do not protect your secrets
If your enclave is in debug mode, one can access all secrets of the enclave.
Production Mode
To protect your secrets you have provided them only to the attested production enclaves. If enclave is signed with a suitable key, it will start in production mode. One can simply generate such a key for CPU that supports FLC. To check, if your CPU supports FLC use:
cpuid | grep SGX_LC
or
lscpu | grep sgx_lc
You can generate a key with following script:
openssl genrsa -3 -out key.pem 3072
And sign a binary with scone signer like this:
scone-signer sign -p --key=key.pem ./mysecret
Then, kill the original program by typing control-C and start a new one that will run in production mode. And one more time, update environment variable SPID in a second terminal on your host:
And then you run:
sudo python3 dumpmemory.py $SPID >& memdump_PRD.log
grep -o MYBIGS memdump_PRD.log
This time grep
will not find secretes on stack.
That is because enclave memory is encrypted and protected from outside access.
Also, this example shows that if you provide secretes after attestation, it can not be extracted from SGX enclave.
Note that secrets stored in the binaries can be found because the binary is not encrypted: a copy of the original binary - which is used to start the enclave - stays in the main memory outside the enclave.
Let's look for the string THIS_IS_NOT_SECRET in our example application. We can find this secret as follows:
sudo python3 dumpmemory.py $SPID | strings -n 5 | grep THIS_IS_NOT_SECRET
Always use SCONE policy to provide secrets to enclaves.
-
Note that the enclave runs in this example runs in debug mode, i.e., one can still attach to this enclave with scone-gdb. To prevent access via the debugger, you need to run your enclave in production mode. ↩
-
Note that an adversary could analyse the binary and figure out the secret. The standard way to provide an enclave with secrets is to use SCONE CAS. ↩