
Détecteur non destructif pour RCE non authentifié dans BeyondTrust Remote Support et PRA, enchaînant l'injection d'arguments et le contournement d'échappement PostgreSQL pour vérifier l'exploitabilité via une différentielle basée sur le temps.
Avertissement éthique : À des fins de recherche, d'éducation et de tests autorisés uniquement. Veuillez ne pas l'utiliser contre des systèmes sans autorisation.
BeyondTrust Remote Support (RS) et Privileged Remote Access (PRA) sont des appliances qui servent d'intermédiaires pour les sessions à distance privilégiées vers les systèmes internes d'une organisation. Elles sont donc fréquemment exposées à Internet et occupent une position de confiance sur le réseau.
Un attaquant non authentifié capable d'atteindre le point de terminaison WebSocket de l'appliance peut exécuter des commandes du système d'exploitation en tant qu'utilisateur du site en enchaînant deux failles : une injection d'arguments dans le script thin-scc-wrapper de l'appliance (CVE-2024-12356) et un contournement d'échappement de chaîne PostgreSQL qui transforme un octet UTF-8 invalide en une rupture anticipée de littéral SQL (CVE-2025-1094). Le chemin vulnérable est atteint avant toute authentification.
bt_rs_rce_check.py dans ce dépôt est un détecteur non destructif pour cette chaîne. Il emprunte exactement le chemin de code non authentifié utilisé par le véritable exploit mais s'arrête à la preuve d'accessibilité : la seule chose qu'il demande à la cible d'exécuter est un bénin (une différentielle temporelle) ou une recherche bénigne de nom d'hôte/HTTP vers un collecteur hors bande que vous contrôlez. Il n'ouvre jamais de shell, n'écrit pas de fichiers, ne lit pas de données, ne persiste pas et ne se déplace pas latéralement.
sleeppsql PostgreSQL à UTF-8 invalide, enchaînée pour compléter l'exécution de code à distanceBT24-10-ONPREM1 / BT24-10-ONPREM2 pour les appliances auto-hébergées ; les instances cloud ont été corrigées par BeyondTrust).CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:HLa chaîne permet une exécution de code à distance non authentifiée en tant qu'utilisateur du site sur une appliance dont l'objectif même est de servir d'intermédiaire pour l'accès privilégié aux systèmes internes. Une seule requête anonyme transforme donc un hôte RS ou PRA exposé en point d'ancrage et en pivot vers chaque système en aval que l'appliance gère.
Elle ne nécessite aucun identifiant, aucune interaction utilisateur et aucun accès préalable : l'attaquant est externe et anonyme, et la seule condition préalable est l'accessibilité réseau au point de terminaison WebSocket de l'appliance.
La vulnérabilité a été divulguée après avoir été découverte exploitée dans la nature, a été ajoutée au catalogue des vulnérabilités exploitées connues de la CISA le 19 décembre 2024, et a été publiquement liée à la violation de décembre 2024 du département du Trésor des États-Unis attribuée à un acteur parrainé par un État.
La logique vulnérable se trouve dans le script shell thin-scc-wrapper de l'appliance, qui lit une clé de session contrôlée par l'attaquant (gskey) à partir d'une trame WebSocket non authentifiée et la transmet vers la base de données. L'exécution de code à distance complète est une chaîne de trois problèmes distincts.
1. Injection d'arguments dans thin-scc-wrapper (CVE-2024-12356). Le wrapper transmet la gskey non fiable à un assistant sans guillemets, de sorte que le shell divise le mot en arguments avant que echo ne s'exécute :
quoted=$(export PHPRC="$BG_app_root/config/php-cli.ini"; echo $gskey | $ingrediRoot/app/dbquote)
Une clé commençant par -e transforme echo en echo -e, qui interprète les séquences d'échappement par antislash et permet à l'attaquant d'émettre des octets bruts arbitraires — surtout \xC0 — dans les données transmises à l'étape suivante. L'attaquant contrôle un drapeau d'une commande existante plutôt que d'injecter une nouvelle commande, c'est pourquoi il s'agit d'une injection d'arguments (CWE-88) plutôt que d'une injection de commande classique.
2. Contournement d'échappement PostgreSQL via UTF-8 invalide (CVE-2025-1094). dbquote échappe la valeur avec pg_escape_string() de PHP, qui appelle PQescapeStringInternal() de libpq. Pour un caractère multi-octets, il fait confiance à la longueur rapportée par pg_encoding_mblen() et copie les octets du caractère textuellement, sans les valider :
len = pg_encoding_mblen(encoding, source);
for (i = 0; i < len; i++) /* copier le caractère */
*target++ = *source++;
Un octet comme 0xC0 annonce un caractère de 2 octets, de sorte que l'échappeur avale l'octet suivant comme son « octet de continuation » — même lorsque cet octet est une apostrophe (0x27). La séquence 0xC0 0x27 passe donc avec un octet d'apostrophe non échappé toujours intégré dans la chaîne « échappée ».
3. psql sort du littéral et exécute \! (CVE-2025-1094). La chaîne « échappée » est transmise à psql, qui ne traite pas 0xC0 0x27 comme un seul caractère, de sorte que le 0x27 ferme le littéral de chaîne SQL prématurément et que tout ce qui suit est analysé comme une nouvelle entrée. L'attaquant utilise cela pour atteindre la méta-commande \! de psql, qui exécute une commande shell en tant qu'utilisateur du site :
<0xC0>'; \! <cmd> #
Rapid7 a constaté que l'exécution de code à distance en pratique repose toujours sur CVE-2025-1094, et que cela suffit à lui seul : la livraison de l'octet brut 0xC0 dans une trame WebSocket binaire (plutôt qu'une trame texte, où la validation UTF-8 le supprimerait) atteint gskey sans modification et déclenche la rupture SQL sans aucune étape echo -e. Ce détecteur utilise cette approche par trame binaire.
Toute la chaîne est accessible pré-authentification via un seul WebSocket :
/nw (un gestionnaire Tornado sur l'appliance).Sec-WebSocket-Protocol: ingredi support desk customer thin, envoyé encodé en URL comme ingredi%20support%20desk%20customer%20thin.Host (mappé à une société installée) ou, en secours, l'en-tête X-Ns-Company. Si aucun ne correspond à une société installée, l'appliance ferme le socket avant que gskey ne soit traitée.1, un UUID thinMint, authType 0 (authentification gskey), puis la gskey malveillante (la version 1 signifie qu'il n'y a pas de ligne locale_code) :1
<UUID thinMint>
0
<0xC0>'; \! <cmd> #
BT24-10 ajoute une passerelle de validation d'entrée qui rejette toute gskey non alphanumérique avant qu'elle n'atteigne dbquote/psql :
elif [[ ! "$gskey" =~ ^[a-zA-Z0-9]{32}$ ]]; then
blog "bad session key given"
...
exit 1
Toute clé contenant 0xC0, une apostrophe ou un métacaractère shell est rejetée et le script se termine, de sorte qu'une appliance corrigée ne produit aucune réponse 1 failure et aucune exécution de commande. Le détecteur transforme cette différence comportementale en une différentielle temporelle indépendante de l'egress qui ne nécessite aucune connectivité sortante depuis la cible :
A × 32). Sur les versions corrigées et non corrigées, cela atteint la recherche en base de données et renvoie ['2\n', '1 failure\n'] rapidement.<0xC0>'; \! sleep <délai> #. Sur un hôte vulnérable, psql exécute le sleep injecté, de sorte que la réponse 1 failure est retardée d'environ <délai> secondes. Sur un hôte corrigé, l'expression régulière gskey rejette la clé, le script se termine et le socket se ferme rapidement sans réponse.Comme le verdict repose sur le minutage de la propre réponse de l'appliance, il tient même lorsque la cible bloque le DNS/HTTP sortant — un cas où un rappel OAST seul donnerait un faux négatif. Le mode de rappel optionnel n'est qu'une corroboration, pour les hôtes qui autorisent l'egress.
Installez la seule dépendance :
pip install -r requirements.txt
Mode différentiel (recommandé, indépendant de l'egress) :
python3 bt_rs_rce_check.py --target rs.example.com --mode differential
Différentiel avec corroboration OAST :
python3 bt_rs_rce_check.py --target rs.example.com \
--oast <votre-collecteur>.oast.pro --mode both
Lorsque l'en-tête Host ne résout pas vers un locataire, fournissez un slug explicite, ou un fichier de slugs candidats (un par ligne) essayés à tour de rôle via l'en-tête 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
Options clés :
| Option | Signification |
|---|---|
--target <hôte> | Hôte virtuel / FQDN de l'appliance (obligatoire). |
--company <slug> | Slug de locataire explicite, envoyé comme X-Ns-Company. |
--company-list <fichier> | Fichier de slugs candidats, essayés jusqu'à ce que l'un résolve. |
--oast <domaine> | Domaine du collecteur hors bande pour la corroboration par rappel. |
--mode {differential,callback,both} | Stratégie de détection (par défaut differential). |
--delay <secondes> | Durée du sleep pour le test temporel (par défaut 9). |
--timeout <secondes> | Délai d'expiration du socket par sonde (par défaut 25). |
--header 'H: v' | En-tête de requête brute supplémentaire ; répétable. |
Lecture du résultat :
VULNERABLE — la réponse 1 failure injectée a été retardée d'environ --delay secondes par rapport au témoin. Le sleep s'est exécuté ; la chaîne d'exécution de code à distance non authentifiée est exploitable sur cet hôte.PATCHED — le témoin a renvoyé 1 failure rapidement, mais l'injection a été rejetée sans réponse. L'expression régulière gskey de BT24-10 est en place et l'injection n'atteint jamais le puits psql.UNRESOLVED (« la société n'a pas résolu ») — aucune des deux sondes n'a produit de 1 failure, de sorte que le témoin n'a pas non plus atteint le gestionnaire gskey. Ce n'est pas un résultat corrigé ; l'appliance a fermé le socket parce que le Host/la société ne correspondait pas à une société installée. Réessayez avec le bon hôte virtuel (--target) ou un slug valide (--company / --company-list).Appliquez BT24-10 (BT24-10-ONPREM1 / BT24-10-ONPREM2) ou mettez à niveau au-delà de la version 24.3.1. Comme la chaîne dépend également de la faille psql PostgreSQL (CVE-2025-1094), maintenez également les outils clients PostgreSQL corrigés et restreignez l'exposition réseau du point de terminaison WebSocket de l'appliance lorsque cela est possible.
psql PostgreSQL à UTF-8 invalide / injection SQL, enchaînée pour compléter l'exécution de code à distance.rapid7/metasploit-framework, exécution de code à distance non authentifiée contre BeyondTrust RS/PRA.Ce logiciel est fourni pour les tests de sécurité autorisés et la recherche défensive uniquement. Exécutez-le exclusivement contre des systèmes que vous possédez ou pour lesquels vous êtes explicitement autorisé par écrit à tester. C'est un détecteur non destructif qui évite délibérément l'exécution de commandes militarisées, l'accès aux données et la persistance. Vous êtes seul responsable du respect de toutes les lois applicables et de l'obtention d'une autorisation appropriée ; les auteurs déclinent toute responsabilité en cas d'utilisation abusive.