Una nueva familia de vulnerabilidades
Un nuevo exploit encontrado por los investigadores de Sayfer y @rugpullfinder El equipo permite a los atacantes saber cuál es el NFT más raro antes de la revelación del proyecto. Esto permite que un atacante tenga una ventaja desigual entre los inversores para comprar la pieza más rara y cara. En algunos proyectos, los más raros pueden tener un precio 50 veces mayor que el precio de la pieza estándar.
La vulnerabilidad ocurre debido a malas prácticas de codificación. Encontramos la vulnerabilidad en docenas de proyectos. Posiblemente existan miles que aún no hemos probado manualmente.
No hay forma de probarlo automáticamente, por lo que si desea saber si un proyecto en el que desea invertir o en el que ya ha invertido es vulnerable a BadReveal, etiquétenos en Twitter. @SayferSeguridad con #BadReveal y nuestros investigadores lo comprobarán.
Afectando a Múltiples Proyectos Activos
Para comprender la magnitud del problema, analizamos más de 100 proyectos diferentes y encontramos que 12 eran vulnerables al ataque. Estos son algunos de los proyectos vulnerables:
CatBloxPUMAcápsula (Tiempo de explotación: 2 días 18 horas, Capitalización de mercado: 628.19 ETH)
Héroes Everai: Dúo (Tiempo de explotación: 4 días 2 horas, Capitalización de mercado: 1,905.37 ETH)
Degen Toonz (Tiempo de explotación: 3 días 13 horas, Capitalización de mercado: 6,221.6 ETH)
Más detalles como tiempos de transacción específicos están en el apéndice.
Detalles Técnicos de la Vulnerabilidad
Antecedentes
Como son los NFT Tokens ERC721, cada token es único con sus propios metadatos. Por lo general, en un proyecto de NFT con un paso de revelación, los tokens se acuñan a ciegas y, una vez que se ha acuñado todo, se revelan los NFT y sus metadatos se hacen públicos. Los NFT con metadatos raros podrían valer 50 veces más que otros NFT en los proyectos. El más raro de todos ellos se llama la "Leyenda".
Pero, ¿qué sucede si un atacante de alguna manera logra obtener los metadatos antes de que se revelen y usa estos datos para comprar la leyenda? El atacante obtiene un beneficio injusto y obtiene más ganancias que todos los demás compradores al explotar estos datos para comprar la leyenda.
¿Qué es un token URI?
El token URI de un NFT es un identificador único de cómo "se ve" el token. Un URI puede ser una dirección HTTP, un hash IPFS o incluso un blob JSON que se mantiene en la cadena. Apunta a un archivo JSON que contiene los metadatos del token.
Al crear su proyecto NFT, debe asegurarse de que los URI del token no sean accesibles o adivinables antes de que se revele el NFT. De lo contrario, esto podría permitir que los atacantes obtengan el control de los NFT más valiosos del proyecto. De hecho, podrían usar estos URI para verificar algunas propiedades raras en los metadatos antes de que alguien pueda verlos.
La vulnerabilidad
Descubrimos que muchos proyectos establecen el URI del token en una transacción y luego lo revelan en una transacción diferente. En el tiempo entre esas dos transacciones, que a veces pueden ser horas, un atacante puede escanear todos los NFT en el proyecto y encontrar cuál es el más raro, y luego comprarlo en función de su tokenID.
Ejemplo de explotación
Aquí tenemos un ejemplo de una función que devuelve el token URI si el reveal
flag es verdadero (es falso por defecto) o un URI que apunta a una imagen oculta común para cada token si el indicador es falso.
function tokenURI(uint256 tokenId) public view virtual override
returns (string memory)
{
require(_exists(tokenId), "Nonexistent token");
if (reveal == false) {
return hideURI;
}
string memory URI = baseURI;
uint256 randomId = ((randomNumber + tokenId) % supplyMax) + 1;
return
bytes(URI).length > 0
? string(abi.encodePacked(URI, randomId.toString(), ".json"))
: "";
}
El URI se crea con cuatro elementos: baseURI
, tokenId
, supplyMax
y randomNumber
. Estos cuatro elementos son públicos en nuestro código, y conocemos fácilmente los primeros tres elementos pero no sabemos cuándo randomNumber
se inicializa.
Ahora veamos la función donde reveal
se vuelve verdad.
function revealNFT() external onlyOwner {
require(randomNumber != 0);
reveal = true;
}
Entendemos que se puede acceder a cada URI una vez que se revelan los NFT. Antes de eso, solo teníamos una imagen genérica para cada NFT. Pero antes de llamar revealNFT()
, el propietario tiene que asegurarse de que randomNumber
se ha inicializado.
Así que veamos dónde el código asigna un valor a randomNumber
.
function requestRandomNumbers() external onlyOwner {
requestId = COORDINATOR.requestRandomWords(
keyHash,
s_subscriptionId,
requestConfirmations,
callbackGasLimit,
numWords
);
}
function fulfillRandomWords(
uint256,
uint256[] memory randomNumbers
) internal override {
randomNumber = randomNumbers[0];
}
En este ejemplo, utilizan VRF de Chainlink, un generador de números aleatorios fuera de la cadena, para crear una compensación en URI con randomNumber
. Estas dos funciones anteriores muestran cómo funciona. El dueño tendrá que llamar requestRandomNumbers()
que asignará un número aleatorio a randomNumber
con una llamada interna de fulfillRandomWords()
por Chainlink.
¿Qué sucede en la vida real cuando el propietario quiere revelar los NFT?
El propietario comenzará llamando requestRandomNumbers()
para asignar un valor aleatorio a randomNumber
. Porque randomNumber
tiene ahora un valor distinto de cero, el propietario puede llamar revealNFT()
para convertir el reveal
marca en verdadero, y luego todos los URI son públicos con tokenURI()
.
Pero recuerda que para predecir las URI solo necesitamos cuatro elementos, incluidos tres ya conocidos y randomNumber
que también es público y tiene su valor asignado cuando requestRandomNumbers()
se llama.
Así que mientras tanto entre requestRandomNumbers()
y revealNFT()
se llaman, las URI aún no son públicas, pero tenemos todos los elementos para predecirlas.
Existe otra versión de esta vulnerabilidad. Aunque es más común, es más difícil de explotar. Si no hay un paso de revelación, el baseURI a menudo se establece al principio o en medio de la fase de acuñación. Entonces, tan pronto como se acuña un NFT, se revela, pero los tokens restantes permanecen desconocidos. Luego podemos recuperar el URI y usarlo para conocer los NFT más raros entre los restantes. Entonces tienes que intentar acuñar en el momento adecuado para encontrarlos.
Como dijimos anteriormente, el resultado es que los atacantes pueden obtener el control de los NFT más valiosos del proyecto, ya que podrían usar estos URI para verificar algunas propiedades raras en los metadatos antes de que alguien pueda verlos.
Para evitar tener el mismo tipo de vulnerabilidades, puede consultar estos Estrategias de aleatorización para gotas NFT Por William Entrike. Le ayudará a aleatorizar tokens URI de forma segura.
Mitigación
Entonces, ¿qué debe hacer si posee un proyecto NFT y desea mitigar los riesgos y proteger la base de código de BadReveal?
Dado que la esencia de la vulnerabilidad BadReveal se basa en la ventaja que tiene el atacante entre establecer la transacción URI del token en la transacción de revelación (o mint), la mitigación sería combinar las dos en una sola transacción.
Si planea revelar los NFT, configure el URI del token base al mismo tiempo:
function reveal(string memory baseURI) public onlyOwner {
revealed = true;
baseTokenURI = baseURI;
}
En el ejemplo que vimos anteriormente, la solución también podría ser mover la revelación para que la transacción de Chianlink la establezca automáticamente:
function fulfillRandomness(bytes32, uint256 _randomness) internal override {
revealed = true;
randomNumber = randomNumbers[0];
}
Aprovechando este enfoque, es posible que desee considerar descartar el revealed
booleano de su código y reemplazándolo con la verificación sobre la marcha de Chainlink randomNumber
asignación:
function someMintingFunction(uint256 _quantity) internal {
require(!randomNumber, "Not revealed yet");
//…
}
El uso de este mecanismo es la forma más sencilla de mitigar BadReveal, ya que no introduce la brecha entre las transacciones que permite a un atacante comprar antes de que otros compradores obtengan la información de los metadatos.
Resumen
Los riesgos cibernéticos de los proyectos NFT pueden tomar muchas formas y formas, es una creencia común que si su NFT no fue robado, no fue pirateado, pero ¿qué sucede si se suponía que obtendría el NFT más raro en el mazo, pero un atacante fue lo suficientemente inteligente como para comprarlo antes que tú? Te hackearon sin siquiera saberlo.
Es importante establecer requisitos para que los proyectos NFT inviertan en auditorías de alta calidad y utilicen buenas prácticas de seguridad.
Creemos firmemente que esta vulnerabilidad se explota comúnmente en la naturaleza bajo el radar de los inversionistas y las comunidades de NFT. Comparta esta información con tantos proyectos como sea posible para que garanticen su seguridad.
Nos gustaría dar las gracias buscador de alfombras @rugpullfinder la ayuda en la investigación y verificación de la vulnerabilidad con nosotros. Utilizamos su experiencia y conocimiento en el espacio NFT para profundizar en la madriguera del conejo de BarReveal.
Apéndice: proyecto afectado
CatBloxPUMADetalles de la cápsula
https://etherscan.io/address/0x8b8D1225bB21CA07812FF3c2ee9358f7b5d90EcA
- Sin llamada a setBaseURI()
- BaseURI actual establecido en el constructor
- 04-jun-2022 04:29:09 AM +UTC → Creación de contrato
- 06-jun-2022 10:14:21 PM +UTC → Último NFT acuñado
Everai Heroes: Detalles del dúo
https://etherscan.io/address/0x9a38dec0590abc8c883d72e52391090e948ddf12
- 25-mar-2022 11:06:27 PM +UTC → Creación de contrato
- 25 de marzo de 2022 11:11:02 p. m. + UTC → 1.er conjunto de llamada BaseUri
- 30-mar-2022 01:21:45 AM +UTC → Último NFT acuñado
- 01-abr-2022 07:58:31 p. m. + UTC → 2.º conjunto de llamadas BaseURI
- 03-abr-2022 08:32:22 p. m. + UTC → 3.er conjunto de llamadas BaseURI
Detalles de Degen Toonz
https://etherscan.io/address/0x19b86299c21505cdf59ce63740b240a9c822b5e4
- 19 de febrero de 2022 12:46:19 a. m. + UTC → 1.er conjunto de llamada BaseURI
- 21 de febrero de 2022 03:03:48 a. m. + UTC → 2.º conjunto de llamadas BaseURI
- 23-feb-2022 01:38:10 AM +UTC → Último NFT acuñado
- 23 de febrero de 2022 04:23:54 a. m. + UTC → 3.er conjunto de llamadas BaseURI
- 23 de febrero de 2022 07:38:38 a. m. + UTC → 4.º conjunto de llamadas BaseURI
- 01-may-2022 03:46:53 AM +UTC → 5.º Establecer llamada BaseURI
- 01 de mayo de 2022 09:57:31 p. m. + UTC → 6.º Establecer llamada BaseURI
¿Quieres escuchar más?
Una reunión de consultoría gratuita incluida.