sábado, junio 03, 2023

Level_Up!: WriteUp del reto "Safeguarding" para hacer pentesting en Web3

Entramos en el mes de junio y, en esta ocasión, nos toca dar solución al reto de hacking Web3 llamado “Safeguarding”, que es el número 3 de nuestra plataforma Level_UP! , con una dificultad de 2 sobre 5 puntos, para que todos los que queráis aprender pentestesting Web3 y hacking en Blockchain, SmartContracts y Tokenomics os podáis poner las pilas.

Figura 1: Level_Up!: WriteUp del reto "Safeguarding" para hacer pentesting en Web3

En otras ocasiones ya se han publicados otros writeups de los primeros niveles de la plataforma  Level_UP!, y todos los podéis encontrar en:
En esta ocasión, al acceder al reto, nos encontramos con 7 pasos y una pista. La pista nos indica que nos fijemos en qué hace keccak256. Además, revisando el código, y como en el contrato del reto de “Ownership”, aparece el modificador onlyOwner que comprueba que el msg.sender sea el propietario (owner) del contrato.

Figura 2: Modifier onlyOwner

Este modificador aparece en la función getFlag() y en el paso 6 ya se indica que debemos conseguir ser el owner del contrato para obtener la flag, por lo que vamos a desplegar el contrato y a revisar que funciones tenemos disponibles.

Comenzando el reto

Una vez desplegado el reto, revisamos las funciones disponibles con contract.functions:

Figura 3: Funciones del reto SafeGuarding

La función safeguard() es la que más llama la atención, ya que getFlag() se usará para conseguir la flag (ya sabemos que debemos conseguir ser propietarios del contrato para ello) y la función owner() nos va a devolver quien es el propietario del contrato. Revisamos con detenimiento que hace la función safeguard(). Vemos que necesita dos argumentos (2 strings) para su ejecución y que tal y cómo se indicaba en la pista del reto, ¿qué hace  que está presente dentro de esta función?

Figura 4: Función safeguard()

La función keccak256() va a calcular el hash Keccak-256 de la entrada indicada. Puede verse que para llamar la función safeguard() se pasan dos parámetros, _flag y _message. Si probamos a llamar a esta función con un par de valores aleatorios, por ejemplo, “hola” y “mundo” nos devuelve el siguiente error: “Mensaje incorrecto”.

Figura 5: Mensaje incorrecto al llamar a safeguard ("HOLA", "MUNDO")

Observamos en el código del SmartContract que este error aparece cuando no se cumple la siguiente condición:
  • keccak256(abi.encodePacked(message)) == keccak256(abi.encodePacked("secret"))
Es decir, que el segundo parámetro que se pasa a la función safeguard() debe ser el string "secret". Cuando esto se cumpla, se pasará de dicha linea y vemos que se asigna a la variable owner el valor de msg.sender.

Figura 6: Conseguimos ser el propietario del contrato

Además, la primera vez que se pasa de este require hay otra condición, y es que si el valor de la variable flag del contrato es igual a "firstuse", que es el valor que tiene el contrato al inicio, se asignarán las variables flag y message del SmartContract. El valor del primer argumento pasado en la función safeguard(), en el caso de la variable flag, y "random" en el caso de message. Un poco más abajo vemos la explicación sobre esto.

Figura 7: Condicional a la bandera en su primer uso

Ahora se puede llamar a la función getFlag(), firmar la transacción y validar la flag obtenida a través del contrato base: base.validateFlag(contract.address, "flag_encontrada")

Figura 8: Obtención de la flag

Superar este reto nos dará 75 puntos que se sumarán a la puntuación global del usuario.

Figura 9: Puntos conseguidos con este reto

Explicación de la vulnerabilidad

¿Recuerdas que la función safeguard() tenía como primer argumento un string que no se ha llegado a utilizar? Como se indica más arriba, este primer argumento, en el caso de ser "firstuse", entrará en una condición que asignará las variables flag y message del contrato.

Acabamos de comprobar que completar el reto no es muy complejo, sin embargo, debemos resaltar en que consiste esta vulnerabilidad. Si nos fijamos, la función safeguard() tiene un comentario que indica que dicha función es el constructor. Pero, ¿por qué se está utilizando una función como constructor cuando se podría utilizar la palabra reservada constructor?

Hay que retroceder en el tiempo hasta las versiones inferiores a la versión 0.4.22 de Solidity, ya que antes no era posible utilizar dicha palabra reservada. Esto hacía que se tuviera que crear el constructor mediante una función y, para que únicamente se ejecutase una vez, se tenía que crear una variable que actuase a modo de bandera, es decir, que tiene asignado un valor inicial y, tras realizar una acción, cambia el estado. 
Esto es lo que ocurre con la variable flag. El servidor a la hora de desplegar el contrato llama a la función safeguard() y, tras comprobar que la variable flag tiene el valor inicial establecido "firstuse", entrará en la condición y asignará el nuevo valor de flag, que posteriormente podrá recuperarse al llamar a la función getFlag(). Esta debilidad está catalogada como la 118 del registro SWC (Smart Contract Weakness Classification).

En definitiva, para superar este reto hay que fijarse en que para conseguir ser el propietario del contrato se está evaluando la condición de que el hash keccak256 del segundo argumento que se pasa a la función safeguard() debe ser igual a "secret". Sin embargo, no debemos olvidar que el uso incorrecto de una función como constructor permite que este sea llamado en más de una ocasión, pudiendo provocar fallos como los vistos durante este reto.


Esperamos que sigas disfrutando de la plataforma Level_UP! y mucho ánimo con el resto de retos. Y recuerda que si quieres aprender de estas tecnologías, tienes Bit2Me Academy, que es una plataforma online para aprender de Web3, BitCoin, Tokenomics o Ethereum con cursos gratuitos además del libro dedicado a "Bitcoin: La tecnología Blockchain y su investigación" de Yaiza Rubio y Félix Brezo que seguro que te ayudan a ponerte las pilas.

Más artículos de Web3, Blockchain & SmartContracts
¡Saludos!

Autor: Álvaro Núñez-Romero, director del BootCamp Online de Ciberseguridad

No hay comentarios:

Publicar un comentario