
Detector não destrutivo para RCE não autenticado no BeyondTrust Remote Support e PRA, encadeando injeção de argumentos e bypass de escape do PostgreSQL para verificar explorabilidade via diferencial baseado em tempo.
Aviso Ético: Apenas para fins de pesquisa, educacionais e testes autorizados. Não utilize contra sistemas sem permissão.
BeyondTrust Remote Support (RS) e Privileged Remote Access (PRA) são appliances que intermediam sessões remotas privilegiadas nos sistemas internos de uma organização, portanto são frequentemente expostos à internet e ocupam uma posição de confiança na rede.
Um atacante não autenticado que consiga alcançar o endpoint WebSocket do appliance pode executar comandos do sistema operacional como o usuário site ao encadear duas falhas: uma injeção de argumentos no script thin-scc-wrapper do appliance (CVE-2024-12356) e um bypass de escape de string do PostgreSQL que transforma um byte UTF-8 inválido em uma quebra prematura de literal SQL (CVE-2025-1094). O caminho vulnerável é alcançado antes de qualquer autenticação.
O bt_rs_rce_check.py neste repositório é um detector não destrutivo para essa cadeia. Ele percorre exatamente o caminho de código não autenticado que o exploit real utiliza, mas para na prova de alcançabilidade: a única coisa que ele pede ao alvo para executar é um inofensivo (um diferencial baseado em tempo) ou uma consulta inofensiva de hostname/HTTP para um coletor out-of-band que você controla. Ele nunca abre um shell, grava arquivos, lê dados, persiste ou se move lateralmente.
sleeppsql do PostgreSQL, encadeada para completar a RCEBT24-10-ONPREM1 / BT24-10-ONPREM2 para appliances auto-hospedados; instâncias em nuvem foram corrigidas pela BeyondTrust).CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:HA cadeia resulta em execução remota de código não autenticada como o usuário site em um appliance cujo propósito inteiro é intermediar acesso privilegiado a sistemas internos, portanto uma única solicitação anônima converte um host RS ou PRA exposto em um ponto de apoio e um pivô em direção a todos os sistemas downstream que o appliance media.
Não exige credenciais, interação do usuário ou acesso prévio: o atacante é externo e anônimo, e o único pré-requisito é alcançabilidade de rede ao endpoint WebSocket do appliance.
A vulnerabilidade foi divulgada depois de ser encontrada sendo explorada na natureza, foi adicionada ao catálogo de Vulnerabilidades Exploradas Conhecidas da CISA em 19 de dezembro de 2024 e foi publicamente associada à violação de dezembro de 2024 do Departamento do Tesouro dos EUA, atribuída a um ator patrocinado por um estado.
A lógica vulnerável reside no script shell thin-scc-wrapper do appliance, que lê uma chave de sessão controlada pelo atacante (gskey) de um frame WebSocket não autenticado e a encaminha ao banco de dados. A RCE completa é uma cadeia de três problemas distintos.
1. Injeção de argumentos no thin-scc-wrapper (CVE-2024-12356). O wrapper passa a gskey não confiável para um helper sem aspas, então o shell divide a string em argumentos antes de o echo ser executado:
quoted=$(export PHPRC="$BG_app_root/config/php-cli.ini"; echo $gskey | $ingrediRoot/app/dbquote)
Uma chave que começa com -e transforma echo em echo -e, que interpreta escapes de barra invertida e permite que o atacante emita bytes brutos arbitrários — mais importante, \xC0 — nos dados que fluem para o próximo estágio. O atacante controla uma flag de um comando existente em vez de injetar um novo comando, razão pela qual isso é injeção de argumentos (CWE-88) em vez de injeção clássica de comandos.
2. Bypass de escape do PostgreSQL via UTF-8 inválido (CVE-2025-1094). O dbquote escapa o valor com pg_escape_string() do PHP, que chama PQescapeStringInternal() da libpq. Para um caractere multibyte, ele confia no comprimento relatado por pg_encoding_mblen() e copia os bytes do caractere verbatim, sem validá-los:
len = pg_encoding_mblen(encoding, source);
for (i = 0; i < len; i++) /* copy the character */
*target++ = *source++;
Um byte como 0xC0 anuncia um caractere de 2 bytes, então o escaper engole o próximo byte como sua "continuação" — mesmo quando esse byte é uma aspa simples (0x27). A sequência 0xC0 0x27 portanto passa com um byte de aspa simples sem escape ainda embutido na string "escapada".
3. psql sai do literal e executa \! (CVE-2025-1094). A string "escapada" é canalizada para o psql, que não trata 0xC0 0x27 como um único caractere, então o 0x27 fecha o literal de string SQL prematuramente e tudo depois dele é analisado como nova entrada. O atacante usa isso para alcançar o meta-comando \! do psql, que executa um comando shell como o usuário site:
<0xC0>'; \! <cmd> #
A Rapid7 descobriu que a RCE na prática sempre depende da CVE-2025-1094, e que ela é suficiente por si só: entregar o byte bruto 0xC0 dentro de um frame WebSocket binário (em vez de um frame de texto, onde a validação UTF-8 o removeria) alcança gskey sem modificação e dispara a quebra SQL sem nenhuma etapa de echo -e. Este detector usa essa abordagem de frame binário.
Toda a cadeia é alcançável pré-autenticação por meio de um único WebSocket:
/nw (um handler Tornado no appliance).Sec-WebSocket-Protocol: ingredi support desk customer thin, enviado codificado em URL como ingredi%20support%20desk%20customer%20thin.Host (mapeado para uma empresa instalada) ou, como fallback, do cabeçalho X-Ns-Company. Se nenhum mapear para uma empresa instalada, o appliance fecha o socket antes de gskey ser processada.1, um UUID thinMint, authType 0 (autenticação gskey) e então a gskey maliciosa (versão 1 significa que não há linha locale_code):1
<UUID thinMint>
0
<0xC0>'; \! <cmd> #
O BT24-10 adiciona uma barreira de validação de entrada que rejeita qualquer gskey não alfanumérica antes que ela alcance dbquote/psql:
elif [[ ! "$gskey" =~ ^[a-zA-Z0-9]{32}$ ]]; then
blog "bad session key given"
...
exit 1
Qualquer chave contendo 0xC0, uma aspa ou um metacaractere de shell é rejeitada e o script sai, portanto um appliance corrigido não produz resposta 1 failure nem execução de comando. O detector transforma essa diferença comportamental em um diferencial baseado em tempo independente de egress que não precisa de conectividade de saída do alvo:
A × 32). Em builds corrigidos e não corrigidos, isso alcança a consulta ao banco e retorna ['2\n', '1 failure\n'] rapidamente.<0xC0>'; \! sleep <atraso> #. Em um host vulnerável, o psql executa o sleep injetado, então a resposta 1 failure é atrasada em aproximadamente <atraso> segundos. Em um host corrigido, a regex da gskey rejeita a chave, o script sai e o socket fecha rapidamente sem resposta.Como o veredito depende do tempo da própria resposta do appliance, ele se mantém mesmo quando o alvo bloqueia DNS/HTTP de saída — um caso em que um callback OAST sozinho geraria falso negativo. O modo de callback opcional é apenas corroboração, para hosts que permitem egress.
Instale a única dependência:
pip install -r requirements.txt
Modo diferencial (recomendado, independente de egress):
python3 bt_rs_rce_check.py --target rs.example.com --mode differential
Diferencial com corroboração OAST:
python3 bt_rs_rce_check.py --target rs.example.com \
--oast <seu-coletor>.oast.pro --mode both
Quando o cabeçalho Host não resolve para um tenant, forneça um slug explícito, ou um arquivo de slugs candidatos (um por linha) tentados em sequência via cabeçalho X-Ns-Company:
python3 bt_rs_rce_check.py --target rs.example.com --company acme
python3 bt_rs_rce_check.py --target rs.example.com --company-list slugs.txt
Opções principais:
| Opção | Significado |
|---|---|
--target <host> | Host virtual / FQDN do appliance (obrigatório). |
--company <slug> | Slug explícito do tenant, enviado como X-Ns-Company. |
--company-list <arquivo> | Arquivo de slugs candidatos, tentados até um resolver. |
--oast <domínio> | Domínio do coletor out-of-band para corroboração por callback. |
--mode {differential,callback,both} | Estratégia de detecção (padrão differential). |
--delay <segundos> | Duração do sleep para o teste baseado em tempo (padrão 9). |
--timeout <segundos> | Timeout de socket por sonda (padrão 25). |
--header 'H: v' | Cabeçalho de solicitação bruto extra; repetível. |
Interpretando o resultado:
VULNERABLE — a resposta 1 failure injetada foi atrasada em aproximadamente --delay segundos em relação ao controle. O sleep foi executado; a cadeia de RCE não autenticada é explorável neste host.PATCHED — o controle retornou 1 failure rapidamente, mas a injeção foi rejeitada sem resposta. A regex da gskey do BT24-10 está em vigor e a injeção nunca alcança o sink do psql.UNRESOLVED ("company did not resolve") — nenhuma das sondas produziu 1 failure, então o controle também nunca alcançou o handler da gskey. Isso não é um resultado de patch; o appliance fechou o socket porque o Host/empresa não mapeou para uma empresa instalada. Tente novamente com o host virtual correto (--target) ou um slug válido (--company / --company-list).Aplique o BT24-10 (BT24-10-ONPREM1 / BT24-10-ONPREM2) ou atualize para além da versão 24.3.1. Como a cadeia também depende da falha do psql do PostgreSQL (CVE-2025-1094), mantenha as ferramentas de cliente PostgreSQL corrigidas também e restrinja a exposição de rede do endpoint WebSocket do appliance quando viável.
psql do PostgreSQL, encadeada para completar a RCE.rapid7/metasploit-framework, RCE não autenticada contra BeyondTrust RS/PRA.Este software é fornecido apenas para testes de segurança autorizados e pesquisa defensiva. Execute-o exclusivamente contra sistemas que você possui ou para os quais tenha autorização explícita por escrito para testar. É um detector não destrutivo que deliberadamente evita execução de comandos armamentizada, acesso a dados e persistência. Você é o único responsável por cumprir todas as leis aplicáveis e por obter a autorização adequada; os autores não aceitam responsabilidade por uso indevido.