Análisis profundo de otra vulnerabilidad crítica del lenguaje Move
Antes descubrimos un grave fallo en Aptos Moveevm, y tras una investigación profunda, encontramos un nuevo fallo de desbordamiento entero. El proceso de activación de este fallo es aún más interesante, a continuación vamos a analizar en profundidad este fallo, al mismo tiempo que introducimos algunos conocimientos de fondo sobre el lenguaje Move. Creemos que a través de la explicación de este artículo, tendrás una comprensión más profunda del lenguaje Move.
Como todos saben, el lenguaje Move verifica las unidades de código antes de ejecutar el bytecode. El proceso de verificación se divide en 4 pasos, y esta vulnerabilidad aparece en el paso de reference_safety.
El módulo reference_safety define funciones de transferencia utilizadas para verificar la seguridad de referencia del sujeto del proceso. Las comprobaciones incluyen verificar que no haya referencias colgantes, si el acceso a referencias mutables es seguro, si el acceso a referencias de almacenamiento global es seguro, etc.
La función de entrada que invoca la verificación de seguridad llamará a analyze_function. En analyze_function, la función validará cada bloque básico. Un bloque básico se refiere a una secuencia de código que no tiene instrucciones de bifurcación, excepto en la entrada y salida.
El lenguaje Move determina bloques básicos al recorrer el bytecode, buscando todas las instrucciones de bifurcación y las secuencias de instrucciones de bucle. Un ejemplo típico de un bloque básico de código IR de Move puede incluir 3 bloques básicos, determinados por las instrucciones BrTrue, Branch y Ret.
Seguridad de referencia en Move
Inspirándose en la filosofía del lenguaje Rust, Move admite dos tipos de referencias: referencia inmutable (&) y referencia mutable (&mut). La referencia inmutable se utiliza para leer datos de una estructura, mientras que la referencia mutable se utiliza para modificar datos. El uso adecuado de los tipos de referencia ayuda a mantener la seguridad y a identificar los módulos de lectura.
El módulo de seguridad de referencias de Move escaneará las instrucciones de bytecode de los bloques básicos en funciones, verificando que todas las operaciones de referencia sean legales. El proceso de verificación involucra principalmente la estructura AbstractState, que contiene el grafo de préstamos y los locales, para garantizar la seguridad de las referencias en la función.
El proceso de verificación comparará el estado antes y después de ejecutar el bloque básico, y combinará los resultados para actualizar el estado del bloque, al mismo tiempo que propagará las condiciones posteriores del bloque a los bloques siguientes. Este proceso es similar a la idea Sea of Nodes en el turbofan de V8.
El ciclo principal ejecutará el código del bloque y luego intentará fusionar el estado previo y el estado posterior. Si el estado cambia y el bloque actual tiene un borde hacia atrás que apunta a sí mismo (, lo que indica que hay un bucle ), regresará al inicio del bucle y continuará ejecutando este bloque básico hasta que el estado posterior sea igual al estado previo o se interrumpa por un error.
Análisis de vulnerabilidades
La vulnerabilidad se presenta en el proceso de determinar si el resultado del join ha cambiado. La función join_ se utiliza para actualizar las variables locales y el gráfico de relaciones de préstamos. Cuando la longitud de los parámetros de la función más la longitud de las variables locales supera 256, dado que las variables locales son de tipo u8, se producirá un desbordamiento al recorrer los locals.
Aunque Move tiene un proceso para verificar el número de locals, en el módulo check bounds solo se verifica los locals y no se incluye el parámetro length. Los desarrolladores parecen darse cuenta de que es necesario verificar la suma de los parámetros y los valores locales, pero el código en realidad solo verifica la cantidad de variables locales.
De desbordamiento de enteros a ataques de DoS
El bucle principal escaneará el bloque de código y llamará a la función execute_block, luego combinará el estado antes y después de la ejecución. Cuando hay un bucle en el código, saltará al inicio del bloque de código para ejecutarlo nuevamente.
Si construimos un bloque de código en un bucle y utilizamos el desbordamiento para cambiar el estado del bloque, de modo que el nuevo mapa de locales sea diferente del anterior, entonces al ejecutar nuevamente execute_block, el análisis de la secuencia de instrucciones en el bloque básico accederá al nuevo mapa de locales. En este momento, si el índice que necesita acceder la instrucción no existe en el nuevo mapa, esto provocará un DoS.
En el módulo de referencia de seguridad, los códigos de operación MoveLoc/CopyLoc/FreeRef pueden lograr este objetivo. Tomando como ejemplo la función copy_loc, intentará obtener el valor local a través de LocalIndex; si LocalIndex no existe, provocará un panic, lo que hará que todo el nodo se bloquee.
Demostración de PoC
Podemos construir un bloque básico que contenga instrucciones de salto incondicional, haciendo que se llame varias veces a las funciones execute_block y join. Al establecer parámetros adecuados y la cantidad de variables locales, se puede reducir la longitud del nuevo mapa de locales a 8 después de la primera ejecución.
En la segunda ejecución, intentar acceder a un offset que no existe causará un pánico, lo que provocará un DoS.
Resumen
Esta vulnerabilidad demuestra que incluso el lenguaje Move, que ha sido sometido a una rigurosa verificación estática, presenta riesgos de seguridad. Las vulnerabilidades por desbordamiento pueden eludir las comprobaciones de límites, lo que resalta la importancia de la auditoría de código.
Como líderes en la investigación de seguridad del lenguaje Move, recomendamos a los diseñadores del lenguaje que añadan más código de verificación en el tiempo de ejecución de Move para prevenir situaciones inesperadas. Actualmente, Move realiza verificaciones de seguridad principalmente en la fase de verificación, pero carece de un reforzamiento de seguridad adecuado en la fase de ejecución, lo que podría dar lugar a problemas más graves.
También hemos descubierto otra vulnerabilidad en el lenguaje Move, que compartiremos con todos más adelante.
Esta página puede contener contenido de terceros, que se proporciona únicamente con fines informativos (sin garantías ni declaraciones) y no debe considerarse como un respaldo por parte de Gate a las opiniones expresadas ni como asesoramiento financiero o profesional. Consulte el Descargo de responsabilidad para obtener más detalles.
18 me gusta
Recompensa
18
5
Compartir
Comentar
0/400
ConsensusDissenter
· 08-05 10:34
Realmente sigue siendo inseguro
Ver originalesResponder0
DefiPlaybook
· 08-05 10:31
Los pasos de verificación deben ser optimizados.
Ver originalesResponder0
MEVHunterWang
· 08-05 10:26
Quien entienda las vulnerabilidades, ganará mucho dinero.
Análisis de la nueva vulnerabilidad de desbordamiento de enteros en el lenguaje Move: de la seguridad de las referencias a los ataques DoS
Análisis profundo de otra vulnerabilidad crítica del lenguaje Move
Antes descubrimos un grave fallo en Aptos Moveevm, y tras una investigación profunda, encontramos un nuevo fallo de desbordamiento entero. El proceso de activación de este fallo es aún más interesante, a continuación vamos a analizar en profundidad este fallo, al mismo tiempo que introducimos algunos conocimientos de fondo sobre el lenguaje Move. Creemos que a través de la explicación de este artículo, tendrás una comprensión más profunda del lenguaje Move.
Como todos saben, el lenguaje Move verifica las unidades de código antes de ejecutar el bytecode. El proceso de verificación se divide en 4 pasos, y esta vulnerabilidad aparece en el paso de reference_safety.
El módulo reference_safety define funciones de transferencia utilizadas para verificar la seguridad de referencia del sujeto del proceso. Las comprobaciones incluyen verificar que no haya referencias colgantes, si el acceso a referencias mutables es seguro, si el acceso a referencias de almacenamiento global es seguro, etc.
La función de entrada que invoca la verificación de seguridad llamará a analyze_function. En analyze_function, la función validará cada bloque básico. Un bloque básico se refiere a una secuencia de código que no tiene instrucciones de bifurcación, excepto en la entrada y salida.
El lenguaje Move determina bloques básicos al recorrer el bytecode, buscando todas las instrucciones de bifurcación y las secuencias de instrucciones de bucle. Un ejemplo típico de un bloque básico de código IR de Move puede incluir 3 bloques básicos, determinados por las instrucciones BrTrue, Branch y Ret.
Seguridad de referencia en Move
Inspirándose en la filosofía del lenguaje Rust, Move admite dos tipos de referencias: referencia inmutable (&) y referencia mutable (&mut). La referencia inmutable se utiliza para leer datos de una estructura, mientras que la referencia mutable se utiliza para modificar datos. El uso adecuado de los tipos de referencia ayuda a mantener la seguridad y a identificar los módulos de lectura.
El módulo de seguridad de referencias de Move escaneará las instrucciones de bytecode de los bloques básicos en funciones, verificando que todas las operaciones de referencia sean legales. El proceso de verificación involucra principalmente la estructura AbstractState, que contiene el grafo de préstamos y los locales, para garantizar la seguridad de las referencias en la función.
El proceso de verificación comparará el estado antes y después de ejecutar el bloque básico, y combinará los resultados para actualizar el estado del bloque, al mismo tiempo que propagará las condiciones posteriores del bloque a los bloques siguientes. Este proceso es similar a la idea Sea of Nodes en el turbofan de V8.
El ciclo principal ejecutará el código del bloque y luego intentará fusionar el estado previo y el estado posterior. Si el estado cambia y el bloque actual tiene un borde hacia atrás que apunta a sí mismo (, lo que indica que hay un bucle ), regresará al inicio del bucle y continuará ejecutando este bloque básico hasta que el estado posterior sea igual al estado previo o se interrumpa por un error.
Análisis de vulnerabilidades
La vulnerabilidad se presenta en el proceso de determinar si el resultado del join ha cambiado. La función join_ se utiliza para actualizar las variables locales y el gráfico de relaciones de préstamos. Cuando la longitud de los parámetros de la función más la longitud de las variables locales supera 256, dado que las variables locales son de tipo u8, se producirá un desbordamiento al recorrer los locals.
Aunque Move tiene un proceso para verificar el número de locals, en el módulo check bounds solo se verifica los locals y no se incluye el parámetro length. Los desarrolladores parecen darse cuenta de que es necesario verificar la suma de los parámetros y los valores locales, pero el código en realidad solo verifica la cantidad de variables locales.
De desbordamiento de enteros a ataques de DoS
El bucle principal escaneará el bloque de código y llamará a la función execute_block, luego combinará el estado antes y después de la ejecución. Cuando hay un bucle en el código, saltará al inicio del bloque de código para ejecutarlo nuevamente.
Si construimos un bloque de código en un bucle y utilizamos el desbordamiento para cambiar el estado del bloque, de modo que el nuevo mapa de locales sea diferente del anterior, entonces al ejecutar nuevamente execute_block, el análisis de la secuencia de instrucciones en el bloque básico accederá al nuevo mapa de locales. En este momento, si el índice que necesita acceder la instrucción no existe en el nuevo mapa, esto provocará un DoS.
En el módulo de referencia de seguridad, los códigos de operación MoveLoc/CopyLoc/FreeRef pueden lograr este objetivo. Tomando como ejemplo la función copy_loc, intentará obtener el valor local a través de LocalIndex; si LocalIndex no existe, provocará un panic, lo que hará que todo el nodo se bloquee.
Demostración de PoC
Podemos construir un bloque básico que contenga instrucciones de salto incondicional, haciendo que se llame varias veces a las funciones execute_block y join. Al establecer parámetros adecuados y la cantidad de variables locales, se puede reducir la longitud del nuevo mapa de locales a 8 después de la primera ejecución.
En la segunda ejecución, intentar acceder a un offset que no existe causará un pánico, lo que provocará un DoS.
Resumen
Esta vulnerabilidad demuestra que incluso el lenguaje Move, que ha sido sometido a una rigurosa verificación estática, presenta riesgos de seguridad. Las vulnerabilidades por desbordamiento pueden eludir las comprobaciones de límites, lo que resalta la importancia de la auditoría de código.
Como líderes en la investigación de seguridad del lenguaje Move, recomendamos a los diseñadores del lenguaje que añadan más código de verificación en el tiempo de ejecución de Move para prevenir situaciones inesperadas. Actualmente, Move realiza verificaciones de seguridad principalmente en la fase de verificación, pero carece de un reforzamiento de seguridad adecuado en la fase de ejecución, lo que podría dar lugar a problemas más graves.
También hemos descubierto otra vulnerabilidad en el lenguaje Move, que compartiremos con todos más adelante.