
Entorno con kernel vulnerable para la explotación del controlador TEE (CVE-2021-44733)
Recientemente se descubrió una vulnerabilidad de use-after-free en el subsistema TEE del kernel de Linux, hasta la versión 5.15.11 inclusive, y se le asignó CVE-2021-44733 [1].
A primera vista no parecía explotable por varias razones; sin embargo, tras un análisis más profundo de la ruta de código vulnerable y mediante la implementación de un exploit de prueba de concepto rudimentario, fue posible sobrescribir un puntero a función en el kernel. En esta publicación no se presenta una carga útil de escalada de privilegios, pero todo el entorno para ejecutar OP-TEE y el exploit está disponible para realizar más pruebas; consulte 'Configuración del entorno'.
Un TEE (Entorno de Ejecución Confiable) es un sistema operativo confiable que se ejecuta en algún entorno seguro, por ejemplo, TrustZone en CPUs ARM. Un controlador TEE maneja los detalles necesarios para comunicarse con el TEE. Algunas de las funciones más importantes del controlador son proporcionar una API genérica hacia el TEE basada en la especificación GlobalPlatform TEE Client API [3], y también gestionar la memoria compartida entre Linux y el TEE. Este subsistema se puede habilitar configurando CONFIG_OPTEE en las configuraciones del kernel para arquitecturas ARM.
El mundo seguro contiene el sistema operativo confiable denominado OP-TEE OS [4]. Sobre este sistema operativo es posible ejecutar las llamadas Aplicaciones Confiables (TAs, por sus siglas en inglés) que pueden realizar algunas operaciones en el entorno aislado; consulte la Figura 1.
Figura 1: Descripción general de TEE - de la presentación de Linaro [5]
El mundo normal (espacio de usuario/kernel de Linux) puede interactuar con estas aplicaciones usando aplicaciones cliente (CAs, por sus siglas en inglés) y la API expuesta por el subsistema TEE. Una CA puede abrir una sesión hacia una TA específica e invocar las funciones que la TA implementa. El paso de argumentos entre la TA y la CA se realiza mediante memoria compartida. A continuación se describe la interacción entre una CA y una TA usando todas las syscalls relevantes.
Una CA abre /dev/tee[0-9] para comunicarse con el controlador. Tenga en cuenta que, en la forma convencional de usar estas APIs, esto se hace implícitamente mediante libteec.
La CA puede registrar la memoria compartida usando el IOCTL TEE_IOC_SHM_ALLOC. Esto asigna memoria compartida y devuelve un descriptor de archivo que el espacio de usuario puede usar como parte de mmap.
El siguiente paso es establecer una sesión usando el IOCTL TEE_IOC_OPEN_SESSION y especificando el uuid de una TA específica. Este uuid está codificado de forma fija durante la compilación de la TA.
Para invocar cualquier función específica en la TA, la CA la invoca especificando el identificador de una función junto con los argumentos de entrada; esto se hace mediante TEE_IOC_INVOKE.
Cuando la CA termina con todas las solicitudes, la sesión se puede cerrar usando TEE_IOC_CLOSE_SESSION.
Figura 2: Sesión entre CA y TA - de la presentación de Linaro [5]
Gran parte de la comunicación entre los clientes y el TEE es opaca para el controlador. La tarea principal del controlador es gestionar el contexto, recibir las solicitudes de los clientes, reenviarlas al TEE y enviar de vuelta los resultados [2].
CVE-2021-44733 se descubrió mediante fuzzing con syzkaller. El archivo de descripción utilizado para esto se proporciona a continuación. Tenga en cuenta que ioctl$TEE_SHM_REGISTER_FD solo forma parte del árbol del kernel de Linaro (mantenedores) y no está en upstream. El entorno proporcionado en 'Configuración del entorno' podría usarse para fuzzing si se configura adecuadamente según la documentación de syzkaller [6]```
#include <uapi/linux/tee.h>
resource fd_tee0[fd] resource session_resource[int32]
openat$tee0(fd const[AT_FDCWD], dev ptr[in, string["/dev/tee0"]], flags flags[open_flags], mode flags[open_mode]) fd_tee0 ioctl$TEE_OPEN_SESSION(fd fd_tee0, cmd const[0x8010a402], arg ptr[inout, tee_ioctl_buf_data_session]) ioctl$TEE_INVOKE(fd fd_tee0, cmd const[0x8010a403], arg ptr[inout, tee_ioctl_buf_data_invoke]) ioctl$TEE_CANCEL(fd fd_tee0, cmd const[0x8008a404], arg ptr[in, tee_ioctl_buf_data_cancel]) ioctl$TEE_CLOSE_SESSION(fd fd_tee0, cmd const[0x8004a405], arg ptr[in, tee_ioctl_buf_data_close]) ioctl$TEE_VERSION(fd fd_tee0, cmd const[0x800ca400], arg ptr[out, tee_ioctl_buf_data_version]) ioctl$TEE_SHM_ALLOC(fd fd_tee0, cmd const[0xc010a401], arg ptr[inout, tee_ioctl_buf_data_shm_alloc]) ioctl$TEE_SHM_REGISTER(fd fd_tee0, cmd const[0xc018a409], arg ptr[inout, tee_ioctl_buf_data_shm_register]) ioctl$TEE_SHM_REGISTER_FD(fd fd_tee0, cmd const[0xc018a408], arg ptr[inout, tee_ioctl_buf_data_shm_register_fd]) ioctl$TEE_SUPPL_RECV(fd fd_tee0, cmd const[0x8010a406], arg ptr[inout, tee_ioctl_buf_suppl_recv]) ioctl$TEE_SUPPL_SEND(fd fd_tee0, cmd const[0x8010a407], arg ptr[inout, tee_ioctl_buf_suppl_send])
#=======================================================
define TEE_IOCTL_UUID_LEN 16
tee_ioctl_param_struct { attr flags[TEE_IOCTL_PARAM_ATTR_TYPE, int64] a int64 b int64 c int64 }
TEE_IOCTL_PARAM_ATTR_TYPE = 0, 1, 2, 3, 5, 6, 7 TEE_LOGIN = 0, 1, 2, 4, 5, 6
#=======================================================
tee_ioctl_buf_data_session { buf_ptr ptr64[inout, tee_ioctl_open_session_struct] buf_len len[buf_ptr, int64] }
tee_ioctl_open_session_struct { uuid array[int8, TEE_IOCTL_UUID_LEN] (in) clnt_uuid array[int8, TEE_IOCTL_UUID_LEN] (in) clnt_login flags[TEE_LOGIN, int32] (in) cancel_id int32 (in) session session_resource (out) ret int32 (out) ret_origin int32 (out) num_params len[params, int32] (in) params array[tee_ioctl_param_struct] (in) }
#=======================================================
tee_ioctl_buf_data_invoke { buf_ptr ptr64[inout, tee_ioctl_invoke_struct] buf_len len[buf_ptr, int64] }
tee_ioctl_invoke_struct { func int32 (in) session session_resource (in) cancel_id int32 (in) ret int32 (out) ret_origin int32 (out) num_params len[params, int32] (in) params array[tee_ioctl_param_struct] (in) }
#=======================================================
tee_ioctl_buf_data_cancel { cancel_id int32 (in) session session_resource (in) }
#=======================================================
tee_ioctl_buf_data_close { session session_resource (in) }
#=======================================================
tee_ioctl_buf_data_version { impl_id int32 (out) impl_caps int32 (out) gen_caps int32 (out) }
#=======================================================
tee_ioctl_buf_data_shm_alloc { size int64 (inout) flags const[0, int32] (inout) id int32 (out) }
#=======================================================
tee_ioctl_buf_data_shm_register { addr int64 (in) length int64 (inout) flags const[0, int32] (inout) id int32 (out) }
#=======================================================
tee_ioctl_buf_data_shm_register_fd { fd int64 (in) size int64 (out) flags const[0, int32] (in) id int32 (out) } [align[8]]
#=======================================================
tee_ioctl_buf_suppl_recv { func int32 (in) num_params len[params, int32] (inout) params array[tee_ioctl_param_struct] (inout) }
#=======================================================
tee_ioctl_buf_suppl_send { ret int32 (out) num_params len[params, int32] (in) params array[tee_ioctl_param_struct] (in) }
Durante el fuzzing, el crash que llamó la atención estaba relacionado con un use-after-free de un objeto task_struct mientras se mantenía un mutex:```
==================================================================
BUG: KASAN: use-after-free in __mutex_lock.constprop.0+0x118c/0x11c4
Read of size 4 at addr 863b0714 by task optee_example_r/244
CPU: 0 PID: 244 Comm: optee_example_r Tainted: G D 5.14.0 #151
Hardware name: Generic DT based system
[<8012b204>] (unwind_backtrace) from [<8011f460>] (show_stack+0x20/0x24)
[<8011f460>] (show_stack) from [<81cf0108>] (dump_stack_lvl+0x5c/0x68)
[<81cf0108>] (dump_stack_lvl) from [<80650f04>] (print_address_description.constprop.0+0x38/0x304)
[<80650f04>] (print_address_description.constprop.0) from [<80651548>] (kasan_report+0x1c0/0x1dc)
[<80651548>] (kasan_report) from [<81d0a9b4>] (__mutex_lock.constprop.0+0x118c/0x11c4)
[<81d0a9b4>] (__mutex_lock.constprop.0) from [<81d0ada4>] (mutex_lock+0x128/0x13c)
[<81d0ada4>] (mutex_lock) from [<817424b0>] (tee_shm_release+0x4b0/0x6cc)
[<817424b0>] (tee_shm_release) from [<81303674>] (dma_buf_release+0x1b8/0x2f0)
[<81303674>] (dma_buf_release) from [<806d5ac0>] (__dentry_kill+0x4c4/0x678)
[<806d5ac0>] (__dentry_kill) from [<806d8a68>] (dput+0x630/0xba4)
[<806d8a68>] (dput) from [<8067d890>] (__fput+0x3b4/0x900)
[<8067d890>] (__fput) from [<801dd1d8>] (task_work_run+0x15c/0x230)
[<801dd1d8>] (task_work_run) from [<80172b70>] (do_exit+0x103c/0x3770)
[<80172b70>] (do_exit) from [<80179aec>] (do_group_exit+0x134/0x3ac)
[<80179aec>] (do_group_exit) from [<801a7658>] (get_signal+0x7d8/0x2f28)
[<801a7658>] (get_signal) from [<8011dea4>] (do_work_pending+0x984/0x154c)
[<8011dea4>] (do_work_pending) from [<801000d0>] (slow_work_pending+0xc/0x20)
Exception stack(0x85743fb0 to 0x85743ff8)
3fa0: 00023108 00000080 00000000 00000000
3fc0: 66bca2d0 66bca2d0 66bca2d0 000000f0 66bca2d0 66bca340 00000000 6ec00b0c
3fe0: 66bc9cc8 66bc9cb8 00011655 66c80c20 000e0130 00023108
Allocated by task 242:
set_alloc_info+0x48/0x50
__kasan_slab_alloc+0x48/0x58
kmem_cache_alloc+0x14c/0x314
copy_process+0x2014/0x7b18
kernel_clone+0x244/0xfc8
sys_clone+0xc8/0xec
ret_fast_syscall+0x0/0x58
0x6ec00a10
Freed by task 67:
kasan_set_track+0x28/0x30
kasan_set_free_info+0x20/0x34
__kasan_slab_free+0xdc/0x108
kmem_cache_free+0x80/0x394
__put_task_struct+0x2b4/0x35c
delayed_put_task_struct+0x104/0x384
rcu_core+0x91c/0x2a68
__do_softirq+0x2fc/0xfb8
Last potentially related work creation:
kasan_record_aux_stack+0xb8/0xc0
call_rcu+0x9c/0xfd0
put_task_struct_rcu_user+0x9c/0xbc
finish_task_switch+0x534/0xa10
__schedule+0x934/0x1adc
schedule_idle+0x9c/0x120
do_idle+0x2ec/0x434
cpu_startup_entry+0x18/0x1c
start_kernel+0x3ec/0x430
The buggy address belongs to the object at 863b0700
which belongs to the cache task_struct of size 1664
The buggy address is located 20 bytes inside of
1664-byte region [863b0700, 863b0d80)
The buggy address belongs to the page:
page:f09c9565 refcount:1 mapcount:0 mapping:00000000 index:0x0 pfn:0x463b0
head:f09c9565 order:3 compound_mapcount:0 compound_pincount:0
flags: 0x10200(slab|head|zone=0)
raw: 00010200 00000000 00000122 82802e00 00000000 80120012 ffffffff 00000001
page dumped because: kasan: bad access detected
Memory state around the buggy address:
863b0600: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
863b0680: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
>863b0700: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
^
863b0780: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
863b0800: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
==================================================================
Esto se desencadenó al cerrar todos los descriptores de archivo de TEE_IOC_SHM_ALLOC mientras un hilo diferente abre una sesión hacia, en nuestro caso, un TA inexistente. Syzkaller logró reproducirlo y, al experimentar con el código del reproductor y retrasar ligeramente la llamada a TEE_IOC_OPEN_SESSION, se produjo un UAF diferente para un objeto perteneciente a la caché kmalloc-64:```
================================================================== BUG: KASAN: use-after-free in tee_shm_put+0x8c/0x98 Read of size 4 at addr 86467020 by task optee_example_h/216
CPU: 0 PID: 216 Comm: optee_example_h Not tainted 5.14.0 #21 Hardware name: Generic DT based system [<80122584>] (unwind_backtrace) from [<80117fd4>] (show_stack+0x10/0x14) [<80117fd4>] (show_stack) from [<819d57a0>] (dump_stack_lvl+0x40/0x4c) [<819d57a0>] (dump_stack_lvl) from [<819ced74>] (print_address_description.constprop.0+0x5c/0x2d8) [<819ced74>] (print_address_description.constprop.0) from [<805a12c4>] (kasan_report+0x1b4/0x1d0) [<805a12c4>] (kasan_report) from [<814cc6b0>] (tee_shm_put+0x8c/0x98) [<814cc6b0>] (tee_shm_put) from [<814c9b2c>] (tee_ioctl+0x1578/0x2e44) [<814c9b2c>] (tee_ioctl) from [<806038ec>] (sys_ioctl+0x918/0x1e70) [<806038ec>] (sys_ioctl) from [<80100060>] (ret_fast_syscall+0x0/0x58) Exception stack(0x86417fa8 to 0x86417ff0) 7fa0: 00000080 00000000 00000003 8010a402 200001c0 00000003 7fc0: 00000080 00000000 00423018 00000036 66c562d0 66c55e10 66c562d0 6ebebafc 7fe0: 66c55cb0 66c55ca0 004114bd 66cebd72
Allocated by task 216: tee_shm_alloc+0x15c/0x7e8 tee_ioctl+0x8d0/0x2e44 sys_ioctl+0x918/0x1e70 ret_fast_syscall+0x0/0x58 0x66c55ca0
Freed by task 215: kasan_set_free_info+0x20/0x34 __kasan_slab_free+0xdc/0x108 kfree+0x98/0x294 tee_shm_release+0x1dc/0x610 dma_buf_release+0x180/0x2a0 __dentry_kill+0x488/0x6ac __fput+0x2f0/0x7b4 task_work_run+0x178/0x230 do_work_pending+0xaf8/0x10a8 slow_work_pending+0xc/0x20 0x66d5bd16
The buggy address belongs to the object at 86467000 which belongs to the cache kmalloc-64 of size 64 The buggy address is located 32 bytes inside of 64-byte region [86467000, 86467040) The buggy address belongs to the page: page:(ptrval) refcount:1 mapcount:0 mapping:00000000 index:0x0 pfn:0x46467 flags: 0x200(slab|zone=0) raw: 00000200 00000000 00000122 82401200 00000000 00200020 ffffffff 00000001 page dumped because: kasan: bad access detected
Memory state around the buggy address: 86466f00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 86466f80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
86467000: fa fb fb fb fb fb fb fb fc fc fc fc fc fc fc fc ^ 86467080: fa fb fb fb fb fb fb fb fc fc fc fc fc fc fc fc 86467100: fa fb fb fb fb fb fb fb fc fc fc fc fc fc fc fc ==================================================================
This vulnerability was discovered by fuzzing the TEE driver without any session being established with an existing TA running on the system. This could be further extended with so called pseudo syscalls in syzkaller in order to setup and initiate a session towards some TA.
## Análisis de la causa raíz
La conclusión es un problema de diseño en el seguimiento del ciclo de vida de un objeto `tee_shm:dmabuf`. El controlador está diseñado para permitir que el espacio de usuario mantenga la única referencia después de una llamada a `tee_ioctl_shm_alloc()`.
Se asume que si el objeto todavía se encuentra en el objeto IDR del controlador, entonces la referencia al dmabuf sigue siendo válida y su contador de referencias puede incrementarse. Resulta que esto solo es parcialmente cierto. La memoria del dmabuf sigue siendo propiedad del controlador de dmabuf, pero puede estar en proceso de destrucción y eso no se puede detener haciendo que el contador de referencias vuelva a ser distinto de cero.
El escenario que desencadena el problema es una aplicación multiproceso en la que un hilo cierra el descriptor de archivo del dmabuf al mismo tiempo que otro hilo realiza una llamada al comando IOCTL `TEE_IOC_OPEN_SESSION` o `TEE_IOC_INVOKE` que hace referencia a esa memoria compartida.
Trazar la destrucción del dmabuf cuando el espacio de usuario cierra el fd ejecutará este código en el kernel:
1. `fput()`
2. `fput_many()` >> El contador de referencias del archivo llega a cero. Se abre la ventana de carrera.
3. `[task_work gets scheduled]`
4. `__fput`
5. `dput`
6. `dma_buf_release`
7. `tee_shm_release`
8. `mutex_lock(teedev->mutex)`
9. `idr_remove(teedev->idr, shm->id)` >> Ahora el objeto shm ya no puede ser referenciado desde el espacio de usuario. La ventana de carrera se cierra.
10. `mutex_unlock()`
Esto significa que la tabla IDR y su bloqueo mutex no pueden garantizar que el dmabuf y el `tee_shm` correspondiente sigan vivos. Un proceso que compite con `fput()` llamando a `tee_shm_get_from_id()` puede obtener una referencia a un shm que está a punto de dejar de estar vivo.```
/**
* tee_shm_get_from_id() - Find shared memory object and increase reference
* count
* @ctx: Context owning the shared memory
* @id: Id of shared memory object
* @returns a pointer to 'struct tee_shm' on success or an ERR_PTR on failure
*/
struct tee_shm *tee_shm_get_from_id(struct tee_context *ctx, int id)
{
struct tee_device *teedev;
struct tee_shm *shm;
if (!ctx)
return ERR_PTR(-EINVAL);
teedev = ctx->teedev;
mutex_lock(&teedev->mutex);
shm = idr_find(&teedev->idr, id);
if (!shm || shm->ctx != ctx)
shm = ERR_PTR(-EINVAL);
else if (shm->flags & TEE_SHM_DMA_BUF)
get_dma_buf(shm->dmabuf);
mutex_unlock(&teedev->mutex);
return shm;
}
Para explotar esto, se debe realizar una reasignación después de que el objeto haya sido liberado y antes de desencadenar el UAF. Después de la llamada a tee_shm_get_from_id(), se llama a la función tee_shm_put() (para la cual ocurre el segundo fallo por UAF de syzkaller) que desreferencia el objeto tee_shm:dmabuf utilizado como argumento de entrada para dma_buf_put().```
/**
El objeto `tee_shm` podría ser reasignado antes del UAF, ya que pertenece a la caché kmalloc-64. Tendría que reasignarse con:
1. objetos fake `tee_shm`, `tee_shm:dmabuf`, `dma_buf:file`
2. establecer `file->f_count = 1`
3. crear un objeto `file:file_operations` que tenga el puntero de función `fasync` establecido a una dirección arbitraria
Esta función se invoca en `__fput()` después de la llamada a `dma_buf_put()` cuando `file->f_count` llega a cero.
PAN (Privileged Access Never) mitiga esto, ya que los objetos falsos deben estar referenciados en memoria de espacio de usuario para poder establecer un puntero de función arbitrario en la estructura `file:f_ops`. Por lo tanto, `CONFIG_CPU_SW_DOMAIN_PAN` debe estar deshabilitado para que esto funcione, y así está en el entorno proporcionado. Quedan algunas preguntas abiertas sobre si PAN puede omitirse en esta vulnerabilidad, por ejemplo, usando ret2dir.
Además, para realizar una reasignación exitosa del objeto shm liberado, la llamada IOCTL `TEE_IOC_OPEN_SESSION` o `TEE_IOC_INVOKE` debe ser adelantada por un hilo que cierre el descriptor de archivo y un hilo de heap spraying que llene la caché kmalloc-64. Para que esto funcione, el kernel debe estar configurado con `CONFIG_PREEMPT`. En este PoC se utilizó el heap spray de la entrada de blog de Nicolas Fabretti [7] basado en `sendmsg()` bloqueante.
Para resumir, el problema en cuanto a la explotación es que tanto la liberación como el UAF deben ocurrir dentro de la misma llamada al sistema. Además, la liberación es difícil de provocar porque requiere una condición de carrera dentro de la syscall. Después de la liberación, el tiempo entre esta y el UAF real es una pequeña ventana de tiempo en la que se debe realizar un heap spray para reasignar el objeto liberado. La siguiente figura muestra los hilos involucrados en el código del exploit y su función.
<p align="center">
<img src="https://raw.githubusercontent.com/pjlantz/pjlantz.github.io/master/docs/assets/Threads.png?raw=true" alt="Threads involved" width="50%" height="50%"/>
<br /><em>Figura 3: Hilos involucrados en el código del exploit</em>
</p>
Se ejecutan continuamente tres tipos de hilos. Para adelantarse al hilo que realiza la llamada al sistema, este se ejecuta con la prioridad más baja posible, `SCHED_IDLE`, mientras que los demás tienen la prioridad establecida a `SCHED_OTHER`. Dado que se usa `sendmsg()` bloqueante, cada intento de spray debe ejecutarse en su propio hilo y debe ejecutarse en el mismo núcleo de CPU que desencadena el UAF, ya que cada núcleo mantiene sus propias cachés kmalloc. También hay varios hilos de liberación que cierran el descriptor de archivo de la asignación de memoria compartida en el paso 1b). El código fuente completo para este desencadenante de UAF y la sobrescritura del puntero de función se puede encontrar en [10].
## Configuración del nuevo entorno
Para reproducir el entorno con un kernel vulnerable y OPTEE, se puede clonar desde el siguiente repositorio y compilarse usando:```
$ mkdir optee-qemu && cd optee-qemu
$ repo init -u https://github.com/pjlantz/optee-qemu.git
$ repo sync
$ cd build
$ make toolchains -j2
$ make run
Después de una compilación exitosa, se abrirán tres consolas: una para QEMU; presiona 'c' en la consola de QEMU para arrancar. Una segunda consola muestra la salida del mundo seguro y la última arrancará en Linux. Inicia sesión como root (sin contraseña).
Ejecuta el código del exploit hasta que el puntero de función fasync de la estructura file_operations esté configurado en 0x22000000.```
until optee_exploit | grep "0x22000000" /var/log/messages; do sleep 0.01; done
Esto se detendrá debido a que Privileged execute-never (PXN) bloquea la ejecución en `PC=0x22000000`. A partir de aquí, las estrategias de explotación pueden variar según la versión del kernel, pero podría ser posible ejecutar un ROP del kernel y realizar stack pivoting, o hacer que el área vDSO sea escribible y colocar el payload allí. También podría ser interesante para trabajos futuros investigar si PAN puede ser evadido usando ret2dir y algo de physmap spraying. PAN se puede habilitar en el kernel estableciendo `CONFIG_CPU_SW_DOMAIN_PAN=y` en `linux/.config`. En hardware real, está habilitado por defecto en ARMv8.1 y AArch64; para ARMv7 y AArch32 es posible tener PAN emulado por software usando esta configuración [8].
**Nota**: Este exploit no está muy bien optimizado y ocasionalmente puede colgar el driver si consigue liberar el objeto de memoria compartida demasiado pronto; en ese caso, PC estará en `tee_shm_get_from_id()`. Si esto ocurre, ejecuta un `system_reset` en la consola de QEMU para reiniciar el entorno.
## Agradecimientos
Gracias a Lars Persson de Axis Communications por su ayuda con el análisis de la causa raíz y a Jens Wiklander de Linaro, mantenedor del subsistema TEE, por una comunicación fluida y la rápida resolución de este problema [9].
## Referencias
[1] CVE-2021-44733 - https://nvd.nist.gov/vuln/detail/CVE-2021-44733
[2] Subsistema TEE - https://www.kernel.org/doc/html/latest/staging/tee.html
[3] API TEE de Globalplatform - https://globalplatform.org/specs-library/?filter-committee=tee
[4] OP-TEE OS - https://github.com/OP-TEE/optee_os
[5] BKK16-110: Una introducción sencilla a Trusted Execution y OP-TEE - https://connect.linaro.org/resources/bkk16/bkk16-110/
[6] Syzkaller - https://github.com/google/syzkaller
[7] Blog de seguridad de Lexfo, por Nicolas Fabretti: CVE-2017-11176: Una explotación del kernel de Linux paso a paso - https://blog.lexfo.fr/cve-2017-11176-linux-kernel-exploitation-part3.html
[8] Subsistema de seguridad del kernel de Linux: Métodos de explotación/Uso de datos del espacio de usuario - http://kernsec.org/wiki/index.php/Exploit_Methods/Userspace_data_usage
[9] [PATCH v2] tee: handle lookup of shm with reference count 0 - https://lore.kernel.org/lkml/[email protected]/T/
[10] Exploit de prueba de concepto - https://github.com/pjlantz/optee_examples/tree/master/exploit/host