
Signal-Server v20261007.0.0 - Высокий - Удалённый - Остаточные привилегии - unauth Dropwizard admin :8081
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · signal-server-dropwizard-admin
Разбор: https://abraxaslabs.tech/research/signal-server-dropwizard-admin
Класс: Остаточные привилегии Доступ: Удалённый
Signal-Server v20261007.0.0 (a15d5cd) — Signal Messenger, LLC
DefaultServerFactory из Dropwizard 5.0.2 поставляет HTTP-коннектор администратора на порту 8081 с незаданным bindHost (все интерфейсы) и без аутентификации администратора. Signal-Server это никогда не переопределяет. В service/config/sample.yml нет блока server:. YAML, в котором перечислены только TLS applicationConnectors, всё равно наследует открытый 8081 на 0.0.0.0. server вместе с тремя воркерами ServerCommand запускает его. ExternalRequestFilter присутствует только в контексте приложения.
Неаутентифицированный HTTP к :8081 — это операционная плоскость администрирования. Не RCE. Не захват аккаунта. Не чтение запечатанных сообщений.
| ID | CVE пока нет |
| Класс | Остаточные привилегии (неаутентифицированный администратор Dropwizard) |
| Доступ | Удалённый (все интерфейсы :8081, без аутентификации) |
| CWE | CWE-306, CWE-1188 |
| CVSS | Высокий: 7.3 CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:L |
| Продукт | Signal-Server |
| Затронуто | v20261007.0.0 (a15d5cd) Dropwizard 5.0.2. Пример в репозитории / опущенный adminConnectors. |
| Аутентификация | отсутствует на коннекторе администратора |
| Лицензия | GNU Affero GPL v3.0 |
| Лаборатория | только 127.0.0.1 |
Любой, кто может открыть TCP-соединение к порту администратора (по умолчанию 0.0.0.0:8081, без дополнительной конфигурации, без заголовка Authorization), получает все стандартные сервлеты администратора Dropwizard 5 плюс дополнительную задачу Signal. Это полный набор привилегий. Ничего в этом списке не является RCE, ATO или чтением тел запечатанных сообщений.
| Метод | Путь | Привилегия |
|---|---|---|
| GET | /ping | Подтвердить, что слушатель администратора работает. Тело — pong. |
| GET | /metrics | Прочитать весь реестр Codahale в формате JSON: куча и не-куча JVM, счётчики GC, количество потоков, метры коннектора Jetty, датчики 4xx/5xx, таймеры. Используйте это для фингерпринтинга процесса, оценки нагрузки и наблюдения за эффектом последующих задач. |
| GET | /threads | Полный дамп потоков JVM: имя, id, состояние и кадры стека каждого потока. В лаборатории это показало dw-admin-* плюс кадры nio из java.base. На живом узле Signal тот же дамп может содержать имена классов, имена логгеров и любую строку, случайно оказавшуюся в стеке (пути к файлам, имена хостов, toString аргументов в обработке). Это не дамп кучи и не дамп очереди сообщений. |
| GET | /healthcheck | Запустить зарегистрированные проверки работоспособности. Это дерево никогда не регистрирует дополнительные проверки, поэтому тело — проверка взаимоблокировок по умолчанию (deadlocks.healthy). Ответ 200 или 500 говорит о том, считает ли JVM, что она во взаимоблокировке. |
| GET | /tasks | Список задач администратора. На этой фиксации: gc, log-level, set-request-logging-enabled. |
| Метод | Путь | Привилегия |
|---|---|---|
| POST | /tasks/log-level?logger=<name>&level=<LEVEL> | Установить любой логгер Logback в TRACE, DEBUG, INFO, WARN, ERROR или OFF. TRACE может заставить логгеры приложения выводить больше (включая значения, которые залогировал разработчик). OFF для логгера безопасности или доступа отключает его. Атакующий не получает поток логов по этому HTTP. Он меняет то, что процесс записывает на диск или в stdout. Оракул лаборатории: имя логгера SIGNAL-SERVER-DROPWIZARD-ADMIN-WITNESS в TRACE. |
| POST | /tasks/gc | Вызвать System.gc(). Ответ — Running GC... / Done!. Это stop-the-world-толчок, а не завершение процесса. |
| POST | /tasks/set-request-logging-enabled?enabled=true | Задача только с сигналом, зарегистрированная в WhisperServerService.run. Включает фильтр access-log запросов (RequestLogManager). После этого HTTP к коннектору приложения может записывать метод, URI и заголовки (включая Authorization Basic) в access-log на диске. Админ-клиент не получает эти строки обратно. На message-persister-service / APN / idle-job воркерах у порта приложения нет ресурсов Signal Jersey, поэтому эта задача важна на server. enabled=false снова отключает её. |
ProcessBuilder, ни Runtime.exec.GET /heapdump возвращает 404.sample.yml или в адресах привязки.Соседний контейнер в той же сети Docker достучался до http://vuln:8081/ping (200 pong) и не смог достучаться до http://patched:8081/ping, когда bindHost был 127.0.0.1. Это и есть удалённая половина.
Я зафиксировал signalapp/Signal-Server v20261007.0.0. Восемнадцать поисков по исходникам. Чат, ключи, сообщения, резервные копии, пожертвования, звонки, MFA, gRPC-омнибус и исходящий HTTP вернулись пустыми на уровне Critical/High. Поиск по воркерам/админке — нет. Dropwizard 5.0.2 HttpConnectorFactory.admin() по-прежнему использует порт 8081 и null bind host. Signal никогда не задаёт adminConnectors. Админ Caddy по умолчанию — localhost. Этот — нет.
Неверный поворот: попытка загрузить полный Signal-Server. sample.yml проходит check и всё равно требует FoundationDB, Dynamo, Redis и пакет секретов для run. Остаток — это коннектор фреймворка, который ServerCommand запускает до всего этого. Лаборатория — Dropwizard 5.0.2 с тем же пустым YAML server: и дополнительным именем задачи Signal.
cd lab
./run.sh
Loopback 127.0.0.1:19010. Проект Compose signal-server-dropwizard-admin. Свидетель SIGNAL-SERVER-DROPWIZARD-ADMIN-WITNESS. В YAML гаджета нет блока server:. Негативный YAML задаёт bindHost: 127.0.0.1. Последняя строка:
SUCCESS signal-server-dropwizard-admin how=unauth-admin-8081 ping=200 metrics=200 threads=200 healthcheck=200 tasks=200 log-level=TRACE gc=200 set-log=enabled bind=* remote-vuln=200 remote-patched=fail SIGNAL-SERVER-DROPWIZARD-ADMIN-WITNESS