Signal-Server v20261007.0.0 - Élevé - À distance - Privilège résiduel - admin Dropwizard non authentifié :8081
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · signal-server-dropwizard-admin
Write-up : https://abraxaslabs.tech/research/signal-server-dropwizard-admin
Classe : Privilège résiduel Portée : Distante
Signal-Server v20261007.0.0 (a15d5cd) - Signal Messenger, LLC
Le DefaultServerFactory de Dropwizard 5.0.2 expose un connecteur d'administration HTTP sur le port 8081 avec bindHost non défini (toutes les interfaces) et sans authentification d'administration. Signal-Server ne le surcharge jamais. service/config/sample.yml ne contient aucun bloc server:. Un YAML qui ne liste que des applicationConnectors TLS hérite toujours du 8081 en clair sur 0.0.0.0. server ainsi que les trois workers ServerCommand le démarrent. ExternalRequestFilter n'est présent que sur le contexte applicatif.
Un accès HTTP non authentifié sur :8081 constitue le plan d'administration opérationnel. Pas de RCE. Pas de prise de contrôle de compte. Pas de lecture de messages scellés.
| ID | pas encore de CVE |
| Classe | Privilège résiduel (administration Dropwizard non authentifiée) |
| Portée | Distante (:8081 sur toutes les interfaces, sans authentification) |
| CWE | CWE-306, CWE-1188 |
| CVSS | Élevé : 7.3 CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:L |
| Produit | Signal-Server |
| Affecté | v20261007.0.0 (a15d5cd) Dropwizard 5.0.2. Échantillon dans l'arborescence / adminConnectors omis. |
| Authentification | aucune sur le connecteur d'administration |
| Licence | GNU Affero GPL v3.0 |
| Labo | 127.0.0.1 uniquement |
Quiconque peut ouvrir une connexion TCP vers le port d'administration (par défaut 0.0.0.0:8081, sans configuration supplémentaire, sans en-tête Authorization) obtient toutes les servlets d'administration standard de Dropwizard 5 ainsi que la tâche supplémentaire de Signal. C'est l'ensemble complet des privilèges. Rien dans cette liste ne constitue une RCE, une ATO ou une lecture du corps de messages scellés.
| Méthode | Chemin | Privilège |
|---|---|---|
| GET | /ping | Confirmer que l'écouteur d'administration est actif. Le corps est pong. |
| GET | /metrics | Lire l'intégralité du registre Codahale au format JSON : tas et hors-tas de la JVM, compteurs de GC, nombre de threads, compteurs des connecteurs Jetty, jauges 4xx/5xx, minuteries. À utiliser pour identifier le processus, estimer la charge et observer l'effet des tâches ultérieures. |
| GET | /threads | Vidage complet des threads de la JVM : nom, id, état et trames de pile de chaque thread. Dans le labo, cela affichait dw-admin-* ainsi que des trames nio de java.base. Sur un nœud Signal en production, le même vidage peut inclure des noms de classes, des noms de loggers et toute chaîne se trouvant sur une pile (chemins de fichiers, noms d'hôtes, toString d'arguments en cours de traitement). Ce n'est pas un vidage de tas et ce n'est pas un vidage de file de messages. |
| GET | /healthcheck | Exécuter les contrôles de santé enregistrés. Cette arborescence n'enregistre jamais d'extras, donc le corps est le contrôle de blocage par défaut (deadlocks.healthy). Un 200 vs 500 vous indique si la JVM se croit bloquée. |
| GET | /tasks | Lister les tâches d'administration. Sur cette version : gc, log-level, set-request-logging-enabled. |
| Méthode | Chemin | Privilège |
|---|---|---|
| POST | /tasks/log-level?logger=<name>&level=<LEVEL> | Définit n'importe quel logger Logback sur TRACE, DEBUG, INFO, WARN, ERROR ou OFF. TRACE peut faire en sorte que les loggers applicatifs impriment davantage (y compris des valeurs journalisées par un développeur). OFF sur un logger de sécurité ou d'accès le rend muet. L'attaquant ne reçoit pas le flux de logs via ce HTTP. Il modifie ce que le processus écrit sur disque ou sur stdout. Oracle du lab : nom de logger SIGNAL-SERVER-DROPWIZARD-ADMIN-WITNESS défini sur TRACE. |
| POST | /tasks/gc | Appelle System.gc(). La réponse est Running GC... / Done!. Il s'agit d'une incitation stop-the-world, pas d'un arrêt du processus. |
| POST | /tasks/set-request-logging-enabled?enabled=true | Tâche signal-only enregistrée dans WhisperServerService.run. Active le filtre de journal d'accès aux requêtes (RequestLogManager). Ensuite, le HTTP vers le connecteur applicatif peut écrire la méthode, l'URI et les en-têtes (y compris Authorization Basic) dans le journal d'accès sur disque. Le client admin ne récupère pas ces lignes. Sur message-persister-service / APN / les workers idle-job, le port applicatif n'a pas de ressources Signal Jersey, donc cette tâche compte sur server. enabled=false le désactive à nouveau. |
ProcessBuilder, pas de Runtime.exec.GET /heapdump renvoie 404.sample.yml ni des adresses de bind.Un conteneur voisin sur le même réseau Docker a atteint http://vuln:8081/ping (200 pong) et n'a pas pu atteindre http://patched:8081/ping lorsque bindHost valait 127.0.0.1. C'est la moitié Remote.
J'ai épinglé signalapp/Signal-Server v20261007.0.0. Dix-huit chasses dans le code source. Chat, clés, messages, sauvegardes, dons, appels, MFA, gRPC omnibus et HTTP sortant sont revenus vides en Critical/High. La chasse workers/admin, non. Dropwizard 5.0.2 HttpConnectorFactory.admin() utilise toujours le port 8081 et un bind host nul. Signal ne définit jamais adminConnectors. L'admin par défaut de Caddy est localhost. Celui-ci ne l'est pas.
Fausse piste : tenter de démarrer le Signal-Server complet. sample.yml passe check et nécessite toujours FoundationDB, Dynamo, Redis et un bundle de secrets pour run. Le résidu est le connecteur du framework que ServerCommand démarre avant tout cela. Le lab est Dropwizard 5.0.2 avec le même YAML server: vide et le nom de tâche supplémentaire de Signal.
cd lab
./run.sh
Loopback 127.0.0.1:19010. Projet Compose signal-server-dropwizard-admin. Témoin SIGNAL-SERVER-DROPWIZARD-ADMIN-WITNESS. Le YAML du gadget n'a pas de bloc server:. Le YAML négatif définit bindHost: 127.0.0.1. Dernière ligne :