Resumen
El fallo está en cryptonote::core_rpc_server::on_is_key_image_spent, en src/rpc/core_rpc_server.cpp: la función que atiende la llamada RPC is_key_image_spent del nodo de Monero, con la que se pregunta si una o varias key images ya se han gastado.
La función recibe cada key image como datos binarios (la variable b) y comprueba si su tamaño coincide con el de crypto::key_image. Según el reporte, falta un return justo después de la línea 1289 (líneas 1278-1292 del commit 17f6fb871c09507cd13c23fdecd9cbcca3f01326), así que cuando el tamaño no coincide la ejecución sigue adelante. En la línea 1291 el código trata b.data() como si fuese una crypto::key_image completa y la añade al vector con push_back. Si el tamaño no cuadra, se copian 32 bytes desde un objeto que ocupa menos: una lectura fuera de límites en la pila.
HackerOne lo clasifica como Integer Overflow, aunque lo que muestra la traza es un desbordamiento de búfer en la pila (stack-buffer-overflow) de lectura.
El hallazgo procede de un trabajo de fuzzing sobre los endpoints RPC de Monero que el MAGIC Monero Fund (un comité de la organización benéfica MAGIC Grants) encargó a Ada Logics. MAGIC Grants lo remitió al programa y renunció expresamente a la recompensa económica.
Pasos de reproducción
El reporte no describe una petición HTTP concreta; el fallo salió de un fuzzer (libFuzzer con AddressSanitizer) que llama directamente al manejador:
- Compilar Monero con AddressSanitizer y un arnés de fuzzing que pase datos arbitrarios a
on_is_key_image_spent(en el reporte,fuzz_is_key_image_spentenfuzz_base.cpp, invocado desdeLLVMFuzzerTestOneInputenfuzz_rpc.cpp). - Darle como entrada el caso mínimo que encontró el fuzzer, de solo tres bytes:
0x0,0x8,0x23,
\000\010#
Base64: AAg
- Con esa entrada, la key image no mide lo que debería, la función no retorna tras la comprobación de tamaño y ASan aborta en la línea 1291 (extracto de la traza):
==9082==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7fa31d153228 at pc 0x56045cd8cc32 bp 0x7ffd3ad28590 sp 0x7ffd3ad27d50
READ of size 32 at 0x7fa31d153228 thread T0
#0 0x56045cd8cc31 in __asan_memcpy /src/llvm-project/compiler-rt/lib/asan/asan_interceptors_memintrinsics.cpp:63:3
...
#4 0x56045d6919ee in push_back /usr/local/bin/../include/c++/v1/vector:1467:13
#5 0x56045d6919ee in cryptonote::core_rpc_server::on_is_key_image_spent(...) /src/monero/monero/src/rpc/core_rpc_server.cpp:1291:18
#6 0x56045d16ffd2 in fuzz_is_key_image_spent(cryptonote::core_rpc_server&, unsigned char const*, unsigned long&, unsigned long) /src/fuzz_base.cpp:191:7
...
#9 0x56045cdcead0 in LLVMFuzzerTestOneInput /src/fuzz_rpc.cpp:20:5
...
[528, 552) 'b' (line 1281) <== Memory access at offset 552 overflows this variable
...
SUMMARY: AddressSanitizer: stack-buffer-overflow /src/monero/monero/src/rpc/core_rpc_server.cpp:1291:18 in cryptonote::core_rpc_server::on_is_key_image_spent(...)
La traza confirma el problema: b ocupa 24 bytes en la pila (offsets 528-552) y se intenta leer 32 desde ella.
Impacto
El reporte lo resume como una posible denegación de servicio: una key image con un tamaño incorrecto en la llamada is_key_image_spent provoca una lectura de memoria fuera de límites dentro del proceso del nodo. El original no detalla más consecuencias.
Remediación
El reporte señala la causa y, con ella, la corrección: añadir el return que falta tras la comprobación de tamaño, para que una key image mal formada no llegue nunca al push_back. El programa marcó el reporte como resuelto; el original no da más detalles sobre el parche.
Qué aprender de este caso
- En manejadores que validan la entrada y preparan una respuesta de error, revisa que cada rama de error termine con
return: si la función sigue, el código posterior trabaja con datos que se acaban de declarar inválidos. - Busca código que interprete
b.data()(o cualquier búfer recibido de la red) como un tipo de tamaño fijo: el tamaño real del búfer tiene que comprobarse y, si no cuadra, abortar antes de copiarlo. - Los endpoints RPC que deserializan blobs binarios de longitud fija (hashes, claves, key images) son buenos objetivos de fuzzing: entradas de pocos bytes, como la de este caso, bastan para encontrar estas rutas de error mal cerradas.