News

Un nombre de wallet que podía ejecutar comandos

Bitcoin Core entrecomillaba el nombre de la wallet antes de pegarlo en el comando de -walletnotify, y luego el paso de pegado volvía a leer ese texto ya entrecomillado como una cadena de formato de expresiones regulares. La corrección se integró el 2 de septiembre de 2026 y aún no está en ninguna versión publicada.

4 min de lecturaBitcoin Core
Un nombre de wallet que podía ejecutar comandos

Qué pasó

El 2 de septiembre de 2026 Bitcoin Core integró la pull request 36048, "util: keep wallet names literal in notification commands". La nota de versión que añade el parche lo dice sin rodeos: "En sistemas que no son Windows, quien llamara al RPC de forma autenticada y tuviera permiso para crear wallets podía ejecutar comandos arbitrarios con la cuenta del proceso del nodo cuando -walletnotify estaba configurado, construyendo un nombre de wallet con caracteres de sustitución de expresiones regulares". El cambio está en master, apunta a la versión 32.0 y ninguna versión publicada lo lleva.

Qué cambia

-walletnotify es una opción de un nodo propio que ejecuta un comando de shell cada vez que llega una transacción de la wallet. El comando es una plantilla, y Core sustituye marcadores en ella antes de ejecutarla: %s por el identificador de la transacción, %b por el hash del bloque, %h por la altura y %w por el nombre de la wallet. Los nombres de wallet los elige quien llama a createwallet, así que Core ya los trataba como entrada hostil y los entrecomillaba primero. La línea de wallet.cpp dice ReplaceAll(strCmd, "%w", ShellEscape(GetName())).

El entrecomillado nunca fue el problema. El paso siguiente volvía a leer ese texto ya entrecomillado, en otro idioma. Desde la pull request 25803, integrada el 16 de agosto de 2022 para quitar una dependencia de Boost, ReplaceAll era una única llamada a std::regex_replace. En esa función el argumento de reemplazo no es una cadena literal. Es una cadena de formato, en la que $& representa el texto que hizo coincidencia, $` todo lo anterior a la coincidencia y $' todo lo posterior. Un nombre de wallet que llevara $' se expandía entonces, en el momento de la sustitución, a una copia del resto de la línea de comandos, pegada dentro de las comillas que ShellEscape acababa de poner a su alrededor. Una vez que las comillas dejan de cuadrar, el texto del atacante deja de ser un argumento del comando y pasa a ser un comando.

El parche tira la expresión regular y pone un recorrido llano: buscar, añadir el reemplazo, avanzar más allá de él y no volver a mirarlo. El nuevo comentario de la función enuncia la propiedad que faltaba, que ambos argumentos se tratan "literalmente; el texto de reemplazo no se vuelve a buscar", y las pruebas unitarias ganaron un caso que le pasa de golpe todos los caracteres de formato de expresiones regulares.

Qué no cambia

Ningún binario publicado está corregido. La versión con std::regex_replace llegó en v24.0 en noviembre de 2022 y sigue en v31.1, del 8 de julio de 2026. Hasta la 32.0, quien quiera esta corrección hoy compila desde master.

Tampoco es algo que un peer pueda hacerle a nadie. Hace falta una llamada autenticada a la interfaz RPC con permiso para crear wallets, y un operador que haya configurado -walletnotify en primer lugar. Nada de esto es alcanzable por la red peer-to-peer, y la nota de versión excluye Windows. Lo que sí cambió es cuánto vale un permiso de creación de wallets: en un nodo donde varias personas o varios servicios tienen credenciales RPC, poder poner nombre a una wallet era en silencio poder ejecutar código con la cuenta bajo la que corre el nodo, que es la cuenta que puede leer todos los archivos de wallet de la máquina.

Tampoco agota la cuestión de los nombres de wallet. Un cambio aparte, la pull request 35833, rechaza caracteres de control en los nombres de wallet nuevos, porque falsifican líneas de registro y se esconden dentro de las rutas. Ninguno de los dos cambios convierte un nombre de wallet en algo seguro de entregar a otra cosa sin examinarlo.

Contexto

Core publica correcciones como esta a la vista de todos en lugar de como aviso de seguridad, que es la elección que describe su política de divulgación: la gravedad decide el embargo, y un fallo que exige acceso autenticado no se trata como una emergencia de red. El mismo mes produjo un contador de derivación de claves que podía volverse negativo, otro caso de un valor leído de un archivo en el que se confió un poco más de lo que se había comprobado.

La parte transferible trata de dónde deja de funcionar el escapado. El escapado siempre es relativo a un consumidor: ShellEscape hace que un texto sea seguro para un shell, y para nada más. Si se pone un segundo intérprete entre el escapado y el shell, la garantía desaparece, porque el segundo tiene sus propios metacaracteres y nunca ha oído hablar de los del primero. Aquí ese segundo intérprete llegó en 2022 como una limpieza de dependencias, con el mismo nombre de función y la misma firma en todos los sitios donde se llamaba, y pasaron cuatro años hasta que alguien preguntó qué hace std::regex_replace con un signo de dólar.

Boletín

Bitcoin, sin ruido

Qué ha pasado en Bitcoin, qué cambia de verdad, y las fuentes para que puedas comprobarnos. Un número cada vez, directo a tu correo.

  • Un correo por número, nunca una secuencia automática
  • Sin píxeles de seguimiento y sin compartir direcciones
  • Baja desde cualquier número con un clic

Recibe el próximo número

Un correo por número, sin píxeles de seguimiento, y te puedes dar de baja desde cualquiera de ellos. No compartimos tu dirección. Política de privacidad