
Detector no destructivo para RCE no autenticado en BeyondTrust Remote Support y PRA, encadenando inyección de argumentos y bypass de escape de PostgreSQL para verificar la explotabilidad mediante diferencial basado en tiempo.
Advertencia Ética: Solo para fines de investigación, educativos y de pruebas autorizadas. No lo utilice contra sistemas sin permiso.
BeyondTrust Remote Support (RS) y Privileged Remote Access (PRA) son appliances que intermedian sesiones remotas privilegiadas hacia los sistemas internos de una organización, por lo que con frecuencia están expuestos a internet y ocupan una posición de confianza en la red.
Un atacante no autenticado que pueda alcanzar el endpoint WebSocket del appliance puede ejecutar comandos del sistema operativo como el usuario site encadenando dos fallos: una inyección de argumentos en el script thin-scc-wrapper del appliance (CVE-2024-12356) y una omisión del escape de cadenas de PostgreSQL que convierte un byte UTF-8 no válido en una ruptura temprana del literal SQL (CVE-2025-1094). La ruta vulnerable se alcanza antes de cualquier autenticación.
bt_rs_rce_check.py en este repositorio es un detector no destructivo para esa cadena. Recorre exactamente la ruta de código no autenticada que usa el exploit real, pero se detiene en la prueba de alcanzabilidad: lo único que le pide al objetivo ejecutar es un sleep benigno (una diferencial basada en tiempo) o una consulta benigna de hostname/HTTP hacia un recolector fuera de banda que usted controle. Nunca abre un shell, escribe archivos, lee datos, persiste o se mueve lateralmente.
psql de PostgreSQL, encadenado para completar la RCEBT24-10-ONPREM1 / BT24-10-ONPREM2 para appliances autoalojados; las instancias en la nube fueron parcheadas por BeyondTrust).CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:HLa cadena produce ejecución remota de código no autenticada como el usuario site en un appliance cuyo propósito principal es intermediar acceso privilegiado hacia sistemas internos, por lo que una única solicitud anónima convierte un host RS o PRA expuesto en un punto de apoyo y un pivote hacia todos los sistemas posteriores que el appliance media.
No requiere credenciales, ni interacción del usuario, ni acceso previo: el atacante es externo y anónimo, y el único requisito previo es la alcanzabilidad de red al endpoint WebSocket del appliance.
La vulnerabilidad se divulgó después de que se descubriera que estaba siendo explotada en el medio silvestre, se añadió al catálogo de Vulnerabilidades Explotadas Conocidas de CISA el 19 de diciembre de 2024, y se ha vinculado públicamente con la brecha de diciembre de 2024 en el Departamento del Tesoro de los EE. UU., atribuida a un actor patrocinado por un estado.
La lógica vulnerable reside en el script shell thin-scc-wrapper del appliance, que lee una clave de sesión controlada por el atacante (gskey) desde una trama WebSocket no autenticada y la pasa hacia la base de datos. La RCE completa es una cadena de tres problemas distintos.
1. Inyección de argumentos en thin-scc-wrapper (CVE-2024-12356). El wrapper pasa la gskey no confiable a un helper sin comillas, por lo que el shell la divide en argumentos antes de que se ejecute echo:
quoted=$(export PHPRC="$BG_app_root/config/php-cli.ini"; echo $gskey | $ingrediRoot/app/dbquote)
Una clave que comienza con -e convierte echo en echo -e, que interpreta escapes de barra invertida y permite al atacante emitir bytes brutos arbitrarios — lo más importante \xC0 — hacia los datos que fluyen a la siguiente etapa. El atacante controla una bandera de un comando existente en lugar de inyectar un comando nuevo, razón por la cual esto es inyección de argumentos (CWE-88) y no inyección clásica de comandos.
2. Omisión del escape de PostgreSQL mediante UTF-8 no válido (CVE-2025-1094). dbquote escapa el valor con pg_escape_string() de PHP, que llama a PQescapeStringInternal() de libpq. Para un carácter multibyte confía en la longitud reportada por pg_encoding_mblen() y copia los bytes del carácter textualmente, sin validarlos:
len = pg_encoding_mblen(encoding, source);
for (i = 0; i < len; i++) /* copy the character */
*target++ = *source++;
Un byte como 0xC0 anuncia un carácter de 2 bytes, por lo que el escapador se traga el siguiente byte como su "continuación" — incluso cuando ese byte es una comilla simple (0x27). Por lo tanto, la secuencia 0xC0 0x27 pasa con un byte de comilla simple sin escapar aún incrustado en la cadena "escapada".
3. psql sale del literal y ejecuta \! (CVE-2025-1094). La cadena "escapada" se canaliza a psql, que no trata 0xC0 0x27 como un solo carácter, por lo que el 0x27 cierra el literal de cadena SQL antes de tiempo y todo lo que sigue se analiza como entrada nueva. El atacante usa eso para alcanzar el metacomando \! de psql, que ejecuta un comando shell como el usuario site:
<0xC0>'; \! <cmd> #
Rapid7 descubrió que la RCE en la práctica siempre depende de CVE-2025-1094, y que es suficiente por sí sola: entregar el byte bruto 0xC0 dentro de una trama WebSocket binaria (en lugar de una trama de texto, donde la validación UTF-8 lo eliminaría) llega a gskey sin modificaciones y desencadena la ruptura SQL sin necesidad del paso de echo -e. Este detector utiliza ese enfoque de trama binaria.
Toda la cadena es alcanzable pre-autenticación a través de un único WebSocket:
/nw (un manejador Tornado en el appliance).Sec-WebSocket-Protocol: ingredi support desk customer thin, enviada codificada en URL como ingredi%20support%20desk%20customer%20thin.Host (mapeada a una compañía instalada) o, como alternativa, desde la cabecera X-Ns-Company. Si ninguna se mapea a una compañía instalada, el appliance cierra el socket antes de que se procese gskey.1, un UUID thinMint, authType 0 (autenticación gskey), y luego la gskey maliciosa (la versión 1 significa que no hay línea de locale_code):