Entorno de ejecución confiable en el ejemplo de Intel SGX. Principios básicos en términos simples. "¡Hola Mundo!"

Este artículo está dirigido principalmente a un especialista novato que solo

comenzó a investigar métodos y formas de garantizar la seguridad de la información del código del programa ejecutable. Tarde o temprano, todos los desarrolladores de software e ingenieros de sistemas se enfrentan a tal tarea, que sucedió en uno de los proyectos de Altiriks Systems, en cuyo marco era necesario implementar la ejecución segura del código del programa en un entorno condicionalmente desprotegido. Para lo cual, además de los métodos y medios de protección de la información ya bien conocidos y bien descritos, se eligió la tecnología Trusted Execution Environment (TEE), que rara vez se usa en proyectos rusos, o, hablando en ruso, la tecnología de entornos de ejecución confiables. Específicamente en este artículo, decidimos describir un ejemplo práctico del uso de enclaves de procesadores Intel para un entorno de ejecución de código confiable (Intel Software Guard Extensions o SGX).



Los tiempos de ejecución confiables no solo son compatibles con procesadores de un fabricante determinado. Además, TEE es compatible con varios procesadores AMD (Secure Execution Environment, Secure Technology), procesadores de arquitectura ARM (TrustZone) y procesadores RISC-V. Además, TEE es compatible con mainframes IBM Z modernos. Elegimos Intel SGX para nuestro ejemplo porque creemos que en el momento de escribir este artículo (verano de 2020) los procesadores Intel son los más populares y están disponibles para principiantes en el espacio postsoviético. Para obtener una lista completa de los modelos de procesadores Intel compatibles con Intel SGX, visite el sitio web de Intel en las especificaciones de productos de Intel (ARK) seleccionando la tecnología adecuada para buscar. Y sí, quizás aproveche las emulaciones Intel SGX con fines educativos o de investigación.Trabajar con varias de estas emulaciones reveló una serie de dificultades para configurarlas. También debe comprender que para proyectos reales de "combate", ninguna emulación de tecnología basada en la funcionalidad del aparato es, por supuesto, aceptable.



Cualquiera de sus comentarios, especialmente con comentarios y adiciones de especialistas que ya tienen experiencia en el uso de TEE en sus proyectos, o con preguntas de quienes recién están comenzando a sumergirse en esta tecnología, contribuirán a una divulgación más detallada de este tema en los siguientes artículos. ¡Gracias por adelantado!



Introducción



La pregunta principal que nos hacemos al comienzo del viaje de exploración de entornos de ejecución confiables es: ¿podemos confiar en los componentes de un sistema informático? Y si podemos, ¿cómo? Los desarrolladores, y en particular los ingenieros de Intel, dan una respuesta inequívoca a esta pregunta: nadie excepto el propio Intel. ¿Qué significa esto? Propongo entender esto con más detalle.



Anillos de privilegio



Por motivos de seguridad, los componentes del sistema de cualquier computadora se dividen por niveles de privilegios. Todos los sistemas modernos basados ​​en procesadores Intel y posteriores tienen un sistema de anillo de privilegios. De externo a interno, existe una expansión de autoridad para el código que actualmente está siendo procesado por el procesador.





Número de timbre 3. El anillo exterior contiene todas las aplicaciones de usuario que usamos en la computadora en la vida cotidiana, tienen el nivel más bajo de acceso.

Ring No. 2 y No. 1. Los sistemas operativos y los controladores de dispositivos se encuentran en estos niveles.

Número de timbre 0. Modo supervisor. Aquí es donde se encuentra el kernel del sistema operativo (administración de periféricos, asignación de recursos entre procesos), así como los controladores del sistema.

Número de timbre-1. Hipervisor. Responsable de la asignación de recursos en caso de que varios sistemas operativos se estén ejecutando en la computadora al mismo tiempo, y también es responsable de aislarlos.

Anillo número 2.Modo de gestión del sistema (SMM - Modo de gestión del sistema). Administra la fuente de alimentación del sistema, administra las tarjetas de expansión.



Podemos formar cada vez más anillos para limitar los poderes de los componentes de la jerarquía, creando un sistema cada vez más complejo y cargado. Sin embargo, esto solo facilitará el trabajo de un atacante: cuanto más complejo sea el sistema, más fácil será encontrar vulnerabilidades en él. Pero, ¿cómo puede proporcionar una capa adicional de seguridad donde la necesita? La respuesta es una palabra.



Enclaves



La tarea principal de un atacante es obtener un nivel de privilegio que le proporcione acceso a los recursos necesarios del sistema. Si este es el secreto de la aplicación víctima, entonces la aplicación maliciosa necesita exactamente el nivel de acceso que es responsable de almacenar secretos en el sistema. De ahí la conclusión de que la gestión de los secretos de las aplicaciones debe confiarse al círculo más íntimo, porque el acceso allí es el más difícil de todos. Sin embargo, este enfoque se ha repensado un poco. Ahora todos los secretos se almacenan al mismo nivel que las aplicaciones de usuario, así como el código que gestiona estos secretos bajo una condición: nadie, absolutamente nadie, excepto el procesador, puede acceder a ellos. El programa y los datos están, por así decirlo, empaquetados en un almacenamiento, en este caso este almacenamiento se llama enclave (Enclave - cerrado, bloqueado),la clave de la que solo tiene el procesador.





Aplicaciones que funcionan con un entorno confiable



Cuanto más simple es el sistema, menos código se escribe en él, más difícil es abrirlo en base a agujeros de seguridad (no estamos hablando de sistemas fundamentalmente desprotegidos), obtenemos un cierto axioma: el código que trabaja con un secreto debe ser lo más simple y corto posible. Empaquetar todo el código del programa en un enclave no es práctico, por lo que una aplicación que utilice enclaves debe dividirse en dos partes: "confiable" y "no confiable". El de confianza almacena enclaves (puede haber varios) y el de no confianza almacena el código principal del programa.



La parte de confianza es un conjunto de funciones y procedimientos denominado ECALL (Enclave Call). La firma de tales funciones debe escribirse en un archivo de encabezado especial y su implementación en el archivo de código fuente. En general, el enfoque es similar al que usamos para la escritura habitual de encabezados, sin embargo, en este contexto, se usa un lenguaje especial tipo C EDL (Enclave Definition Language). También es necesario escribir prototipos de aquellas funciones que se pueden llamar desde dentro del enclave, tales funciones se denominan OCALL (Outside Call). Los prototipos se escriben en el mismo encabezado donde están las funciones de ECALL y la implementación, a diferencia de ECALL, se escribe en consecuencia en la parte no confiable de la aplicación.

Los códigos confiables y no confiables están rígidamente unidos por la certificación que utiliza el protocolo Diffie-Hellman. El procesador es responsable del procedimiento de firma, donde se almacena la clave de intercambio de información, que se actualiza cada vez que se reinicia el sistema. El contenido de los enclaves se almacena en la memoria compartida utilizada por las aplicaciones de los usuarios, pero el almacenamiento está encriptado. Solo el procesador puede descifrar el contenido. En un mundo idealizado, donde el código del enclave se escribe sin un solo error, y todo el hardware funciona exactamente como lo pretendía el fabricante y nada más, obtendríamos un sistema universal y completamente seguro. La principal ventaja de este sistema es la ejecución de la parte secreta en el mismo procesador, donde se ejecutan todos los demás programas, incluidos los programas de usuario.



Sin embargo, en los últimos años, una gran cantidad de vulnerabilidades microarquitectónicas de los procesadores modernos han aparecido ante una amplia audiencia, permitiendo el acceso al interior del enclave: Foreshadow (vulnerabilidad de clase Spectre), SGAxe, Zombieload, CacheOut y muchas otras. No hay garantía de que esta lista no se reponga con otra vulnerabilidad de hardware grave, cuya corrección de software no se puede llamar "parche" de software. Quizás vivamos para ver el momento en que se presente al mundo una arquitectura de procesador completamente nueva, en la que se corregirán todas las deficiencias, pero por ahora, vale la pena hablar de lo que tenemos a mano. Y tenemos a mano una herramienta versátil y poderosa que aumenta drásticamente la seguridad de los sistemas actuales. Recaudando tantoque se implementa en una interpretación u otra en miles de millones de dispositivos en todo el mundo: desde relojes inteligentes, teléfonos inteligentes hasta grandes clústeres informáticos.



¡Hola Mundo!



Pasemos de la teoría a la práctica. Escribamos un pequeño programa que implemente la tarea ya canónica: imprima la cadena "¡Hola mundo!" En esta interpretación, también indicaremos el lugar desde donde se enviará el mensaje.



Primero debe descargar e instalar el SDK para trabajar con SGX desde el sitio web oficial. Para descargar, debe seguir un sencillo procedimiento de registro. En la etapa de instalación, se le pedirá que integre el paquete de desarrollo en la versión de VS disponible en su computadora, haga esto. Todo está listo para la implementación exitosa del primer proyecto usando SGX.





Inicie VS y cree un proyecto Intel SGX.





Elegimos un nombre para el proyecto y la solución y esperamos "siguiente".



A continuación, se le pedirá que seleccione una configuración de proyecto, no cambie nada, deje los valores que se propusieron originalmente.







Luego, agregue otro proyecto a la solución creada: una aplicación de consola C ++ normal.

Como resultado, la siguiente imagen debería aparecer en el cuadro de diálogo de proyectos:







Entonces necesitas vincular el enclave a la parte que no es de confianza. Haz clic derecho en el proyecto "Pieza que no es de confianza".







A continuación, debe cambiar algunas propiedades de los proyectos.



imagen






Esto debe hacerse para que el programa funcione correctamente. Repetimos los pasos para ambos proyectos.



También es necesario indicar el proyecto principal en las propiedades de la solución.





Eso es todo, nuestro programa está listo para su implementación.



Este programa tendrá 3 archivos con los que trabajaremos: Enclave.edl (el mismo encabezado), Enclave.cpp (la implementación de ECALL está detallada), Untrusted Part.cpp (el archivo principal del proyecto es la parte que no es de confianza). Pongamos el



siguiente código en archivos:



Untusted Part.cpp:



#define ENCLAVE_FILE "Enclave.signed.dll" //,     

#include "sgx_urts.h" // ,           
#include "Enclave_u.h" //   
#include "stdio.h"

void print_string(char* buf) //OCALL     -   
{
	printf("ocall output: %s\n", buf);
}

int main()
{
	sgx_enclave_id_t eid; // id ,      ,    id
	sgx_status_t ret = SGX_SUCCESS; //        
	sgx_launch_token_t token = { 0 }; //    
	int updated = 0; //      
	const int BUF_LEN = 30; //  ,     

	ret = sgx_create_enclave(ENCLAVE_FILE, SGX_DEBUG_FLAG, &token, &updated, &eid, NULL); //  

	if (ret != SGX_SUCCESS)
	{
		printf("Failed to create enclave with error number: %#x\n", ret); //  
		return 0;
	}
	char buf[BUF_LEN]; //  ,      

	enclaveChat(eid, buf, BUF_LEN); // ECALL  

	printf("\noutput form main(): %s\n", buf); //  
}


Enclave.edl:



enclave {
    from "sgx_tstdc.edl" import *;

    trusted {
        /* define ECALLs here. */
        public void enclaveChat([out, size=len] char* str, size_t len);
        /*   ,    . OUT -   ,   
                , out     .
           ,          ,
                 .
        */
    };

    untrusted {
        /* define OCALLs here. */
        void print_string([in, string] char* buf); //  ,     
    };
};


Enclave.cpp:



#include "Enclave_t.h"

#include "sgx_trts.h"
#include <cstring>

void enclaveChat(char* str, size_t len)
{
	char* secret = "Hello from better place"; //   

	memcpy(str, secret, len); //   ,  

	print_string(secret); // OCALL-  
}


Presione f7 - construya la solución, y luego ctrl + f5 para ejecutar.



Si recibe un error como este:





asegúrese de que Intel SGX esté habilitado en BIOS: Bios: Security / IntelSGX / Enabled.



En caso de que no hubiera errores, y frente a la pantalla de la consola, veías las siguientes líneas:





... felicitaciones, su primer programa con tecnología Intel SGX está listo. Espero que los comentarios en el código hayan sido completos para su comprensión; de lo contrario, siempre puede hacer preguntas aquí en los comentarios o en mensajes privados.



All Articles