
Недestructive детектор неаутентифицированного RCE в BeyondTrust Remote Support и PRA, объединяющий инъекцию аргументов и обход экранирования PostgreSQL для проверки эксплуатируемости через дифференциальный анализ по времени.
Этическое предупреждение: Только для исследовательских, образовательных и авторизованных целей тестирования. Пожалуйста, не используйте против систем без разрешения.
BeyondTrust Remote Support (RS) и Privileged Remote Access (PRA) — это устройства, которые выступают посредниками при привилегированных удалённых сеансах во внутренние системы организации, поэтому они часто доступны из интернета и занимают доверенное положение в сети.
Неаутентифицированный злоумышленник, имеющий доступ к WebSocket-конечной точке устройства, может выполнять команды операционной системы от имени пользователя site, объединив две уязвимости: внедрение аргументов в скрипте thin-scc-wrapper устройства (CVE-2024-12356) и обход экранирования строк PostgreSQL, превращающий недопустимый байт UTF-8 в преждевременный разрыв SQL-литерала (CVE-2025-1094). Уязвимый путь достигается до любой аутентификации.
bt_rs_rce_check.py в этом репозитории — это неразрушающий детектор для этой цепочки. Он проходит ровно по тому же неаутентифицированному пути кода, который использует реальная эксплойтация, но останавливается на доказательстве достижимости: единственное, что он когда-либо просит выполнить цель, — это безвредный sleep (временная дифференциальная проверка) или безвредный запрос имени хоста/HTTP к внешнему коллектору, которым вы управляете. Он никогда не открывает оболочку, не записывает файлы, не читает данные, не закрепляется и не перемещается латерально.
psql через недопустимый UTF-8, объединённая для завершения RCEBT24-10-ONPREM1 / BT24-10-ONPREM2 для самостоятельно размещённых устройств; облачные экземпляры были пропатчены BeyondTrust).CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:HЦепочка обеспечивает неаутентифицированное удалённое выполнение кода от имени пользователя site на устройстве, чьё единственное назначение — выступать посредником при привилегированном доступе во внутренние системы, поэтому один анонимный запрос превращает открытый хост RS или PRA в плацдарм и точку опоры для продвижения к каждой нижестоящей системе, которую обслуживает устройство.
Для этого не требуются учётные данные, взаимодействие с пользователем или предварительный доступ: злоумышленник внешний и анонимный, а единственное предусловие — сетевая достижимость WebSocket-конечной точки устройства.
Уязвимость была раскрыта после обнаружения её эксплуатации в дикой природе, была добавлена в каталог CISA Known Exploited Vulnerabilities 19 декабря 2024 года и публично связана с взломом Министерства финансов США в декабре 2024 года, приписываемым спонсируемому государством актору.
Уязвимая логика находится в shell-скрипте thin-scc-wrapper устройства, который читает контролируемый злоумышленником ключ сеанса (gskey) из неаутентифицированного WebSocket-кадра и передаёт его к базе данных. Полное RCE — это цепочка из трёх отдельных проблем.
1. Внедрение аргументов в thin-scc-wrapper (CVE-2024-12356). Обёртка передаёт недоверенный gskey вспомогательной программе без кавычек, поэтому оболочка разделяет его на аргументы по пробелам до выполнения echo:
quoted=$(export PHPRC="$BG_app_root/config/php-cli.ini"; echo $gskey | $ingrediRoot/app/dbquote)
Ключ, начинающийся с -e, превращает echo в echo -e, который интерпретирует escape-последовательности с обратной косой чертой и позволяет злоумышленнику выдавать произвольные сырые байты — прежде всего \xC0 — в данные, поступающие на следующий этап. Злоумышленник контролирует флаг существующей команды, а не внедряет новую команду, поэтому это внедрение аргументов (CWE-88), а не классическое внедрение команд.
2. Обход экранирования PostgreSQL через недопустимый UTF-8 (CVE-2025-1094). dbquote экранирует значение с помощью PHP-функции pg_escape_string(), которая вызывает PQescapeStringInternal() из libpq. Для многобайтового символа она доверяет длине, сообщаемой pg_encoding_mblen(), и копирует байты символа дословно, без их проверки:
len = pg_encoding_mblen(encoding, source);
for (i = 0; i < len; i++) /* copy the character */
*target++ = *source++;
Байт типа 0xC0 объявляет двухбайтовый символ, поэтому экранировщик проглатывает следующий байт как его «продолжение» — даже если этот байт является одинарной кавычкой (0x27). Таким образом, последовательность 0xC0 0x27 проходит с неэкранированным байтом одинарной кавычки, всё ещё встроенным в «экранированную» строку.
3. psql выходит за пределы литерала и выполняет \! (CVE-2025-1094). «Экранированная» строка передаётся в psql, который не рассматривает 0xC0 0x27 как один символ, поэтому 0x27 преждевременно закрывает SQL-строковый литерал, и всё после него анализируется как новый ввод. Злоумышленник использует это для доступа к мета-команде \! в psql, которая выполняет команду оболочки от имени пользователя site:
<0xC0>'; \! <cmd> #
Rapid7 обнаружила, что RCE на практике всегда опирается на CVE-2025-1094 и что её достаточно самой по себе: доставка сырого байта 0xC0 внутри бинарного WebSocket-кадра (а не текстового, где проверка UTF-8 удалила бы его) достигает gskey без изменений и запускает SQL-прорыв вообще без этапа echo -e. Этот детектор использует подход с бинарным кадром.
Вся цепочка достижима до аутентификации через один WebSocket:
/nw (обработчик Tornado на устройстве).Sec-WebSocket-Protocol: ingredi support desk customer thin, отправляемым в URL-кодированном виде как ingredi%20support%20desk%20customer%20thin.Host (сопоставляется с установленной компанией) или, как запасной вариант, из заголовка X-Ns-Company. Если ни один из них не сопоставляется с установленной компанией, устройство закрывает сокет до обработки gskey.1, UUID thinMint, authType 0 (аутентификация gskey), затем вредоносный gskey (версия 1 означает отсутствие строки locale_code):1
<UUID thinMint>
0
<0xC0>'; \! <cmd> #
BT24-10 добавляет шлюз проверки ввода, который отклоняет любой неалфавитно-цифровой gskey до его передачи в dbquote/psql:
elif [[ ! "$gskey" =~ ^[a-zA-Z0-9]{32}$ ]]; then
blog "bad session key given"
...
exit 1
Любой ключ, содержащий 0xC0, кавычку или метасимвол оболочки, отклоняется, и скрипт завершается, поэтому пропатченное устройство не выдаёт ответ 1 failure и не выполняет команды. Детектор превращает это поведенческое различие в независимую от исходящего трафика временную дифференциальную проверку, которой не требуется исходящее соединение от цели:
A × 32). На пропатченных и непропатченных сборках он достигает запроса к БД и возвращает ['2\n', '1 failure\n'] быстро.<0xC0>'; \! sleep <задержка> #. На уязвимом хосте psql выполняет внедрённый sleep, поэтому ответ 1 failure задерживается примерно на <задержка> секунд. На пропатченном хосте регулярное выражение gskey отклоняет ключ, скрипт завершается, и сокет закрывается быстро без ответа.Поскольку вердикт основан на времени собственного ответа устройства, он сохраняется даже когда цель блокирует исходящий DNS/HTTP — случай, когда только OAST-обратный вызов дал бы ложный отрицательный результат. Дополнительный режим обратного вызова служит лишь подтверждением для хостов, которые разрешают исходящий трафик.
Установите единственную зависимость:
pip install -r requirements.txt
Дифференциальный режим (рекомендуется, не зависит от исходящего трафика):
python3 bt_rs_rce_check.py --target rs.example.com --mode differential
Дифференциальный с подтверждением через OAST:
python3 bt_rs_rce_check.py --target rs.example.com \
--oast <ваш-коллектор>.oast.pro --mode both
Когда заголовок Host не разрешается в тенант, укажите явный slug или файл с кандидатами slug (по одному в строке), которые перебираются по очереди через заголовок 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
Ключевые параметры:
| Параметр | Значение |
|---|---|
--target <host> | Виртуальный хост / FQDN устройства (обязательно). |
--company <slug> | Явный slug тенанта, отправляемый как X-Ns-Company. |
--company-list <file> | Файл с кандидатами slug, перебирается до успешного разрешения. |
--oast <domain> | Домен внешнего коллектора для подтверждения обратным вызовом. |
--mode {differential,callback,both} | Стратегия обнаружения (по умолчанию differential). |
--delay <seconds> | Длительность sleep для проверки по времени (по умолчанию 9). |
--timeout <seconds> | Таймаут сокета на один зонд (по умолчанию 25). |
--header 'H: v' | Дополнительный сырой заголовок запроса; можно повторять. |
Интерпретация результата:
VULNERABLE — ответ 1 failure с инъекцией был задержан примерно на --delay секунд относительно контроля. sleep выполнился; цепочка неаутентифицированного RCE эксплуатируема на этом хосте.PATCHED — контроль вернул 1 failure быстро, но инъекция была отклонена без ответа. Регулярное выражение gskey из BT24-10 действует, и инъекция никогда не достигает приёмника psql.UNRESOLVED («company did not resolve») — ни один зонд не выдал 1 failure, значит, контроль также не достиг обработчика gskey. Это не результат «пропатчено»; устройство закрыло сокет, потому что Host/компания не сопоставились с установленной компанией. Повторите с корректным виртуальным хостом (--target) или действительным slug (--company / --company-list).Примените BT24-10 (BT24-10-ONPREM1 / BT24-10-ONPREM2) или обновитесь до версии новее 24.3.1. Поскольку цепочка также зависит от уязвимости PostgreSQL psql (CVE-2025-1094), поддерживайте пропатченными и клиентские инструменты PostgreSQL, а также ограничьте сетевую доступность WebSocket-конечной точки устройства, где это возможно.
psql через недопустимый UTF-8 / SQL-инъекцию, объединённая для завершения RCE.rapid7/metasploit-framework, неаутентифицированное RCE против BeyondTrust RS/PRA.Это программное обеспечение предоставляется только для авторизованного тестирования безопасности и оборонительных исследований. Запускайте его исключительно против систем, которыми вы владеете или на тестирование которых имеете явное письменное разрешение. Это неразрушающий детектор, который намеренно избегает применения оружейного выполнения команд, доступа к данным и закрепления. Вы несёте единоличную ответственность за соблюдение всех применимых законов и за получение надлежащего разрешения; авторы не несут ответственности за неправомерное использование.