
Signal-Server v20261007.0.0 - Alta - Remoto - Privilégio remanescente - admin Dropwizard não autenticado :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égio residual Alcance: Remoto
Signal-Server v20261007.0.0 (a15d5cd) - Signal Messenger, LLC
O DefaultServerFactory do Dropwizard 5.0.2 disponibiliza um conector HTTP de administração na porta 8081 com bindHost não definido (todas as interfaces) e sem autenticação de administração. O Signal-Server nunca substitui isso. O service/config/sample.yml não tem bloco server:. Um YAML que apenas lista applicationConnectors TLS ainda herda o 8081 em texto simples em 0.0.0.0. O server mais os três workers ServerCommand iniciam-no. O ExternalRequestFilter está apenas no contexto da aplicação.
HTTP não autenticado para :8081 é o plano de administração operacional. Não é RCE. Não é tomada de conta. Não é leitura de mensagens seladas.
| ID | ainda sem CVE |
| Classe | Privilégio residual (administração Dropwizard não autenticada) |
| Alcance | Remoto (:8081 em todas as interfaces, sem autenticação) |
| CWE | CWE-306, CWE-1188 |
| CVSS | Alto: 7.3 CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:L |
| Produto | Signal-Server |
| Afetado | v20261007.0.0 (a15d5cd) Dropwizard 5.0.2. Amostra no repositório / adminConnectors omitido. |
| Autenticação | nenhuma no conector de administração |
| Licença | GNU Affero GPL v3.0 |
| Laboratório | apenas 127.0.0.1 |
Qualquer pessoa que consiga abrir TCP para a porta de administração (padrão 0.0.0.0:8081, sem configuração extra, sem cabeçalho Authorization) obtém todos os servlets de administração padrão do Dropwizard 5, além da tarefa extra do Signal. Esse é o conjunto completo de privilégios. Nada nesta lista é RCE, ATO ou leitura de corpos de mensagens seladas.
| Método | Caminho | Privilégio |
|---|---|---|
| GET | /ping | Confirma que o listener de administração está ativo. O corpo é pong. |
| GET | /metrics | Lê o registo Codahale completo como JSON: heap e non-heap da JVM, contagens de GC, contagens de threads, medidores de conectores Jetty, medidores 4xx/5xx, temporizadores. Use isto para identificar o processo, estimar a carga e observar o efeito de tarefas posteriores. |
| GET | /threads | Despejo completo de threads da JVM: nome, id, estado e frames de pilha de cada thread. No laboratório isto mostrou dw-admin-* mais frames nio de java.base. Num nó Signal em produção, o mesmo despejo pode incluir nomes de classes, nomes de loggers e qualquer string que esteja numa pilha (caminhos de ficheiros, hostnames, toString de argumentos em curso). Não é um despejo de heap e não é um despejo de fila de mensagens. |
| GET | /healthcheck | Executa as verificações de saúde registadas. Esta árvore nunca regista extras, pelo que o corpo é a verificação de deadlock padrão (deadlocks.healthy). Um 200 vs 500 indica se a JVM pensa que está em deadlock. |
| GET | /tasks | Lista as tarefas de administração. Neste pin: gc, log-level, set-request-logging-enabled. |
| Método | Caminho | Privilégio |
|---|---|---|
| POST | /tasks/log-level?logger=<name>&level=<LEVEL> | Define qualquer logger Logback como TRACE, DEBUG, INFO, WARN, ERROR ou OFF. TRACE pode fazer os loggers da aplicação imprimirem mais (incluindo valores registrados por um desenvolvedor). OFF em um logger de segurança ou de acesso o silencia. O atacante não recebe o fluxo de logs por este HTTP. Ele altera o que o processo grava em disco ou em stdout. Oráculo do laboratório: nome do logger SIGNAL-SERVER-DROPWIZARD-ADMIN-WITNESS para TRACE. |
| POST | /tasks/gc | Chama System.gc(). A resposta é Running GC... / Done!. Isso é um empurrão de stop-the-world, não a morte do processo. |
| POST | /tasks/set-request-logging-enabled?enabled=true | Tarefa apenas de sinal registrada em WhisperServerService.run. Habilita o filtro de log de acesso a requisições (RequestLogManager). Depois disso, o HTTP para o conector da aplicação pode gravar método, URI e cabeçalhos (incluindo Authorization Basic) no log de acesso em disco. O cliente admin não recebe essas linhas de volta. Em message-persister-service / APN / workers de idle-job, a porta da aplicação não tem recursos Signal Jersey, então esta tarefa importa em server. enabled=false desliga novamente. |
ProcessBuilder, sem Runtime.exec.GET /heapdump retorna 404.sample.yml ou nos endereços de bind.Um contêiner irmão na mesma rede Docker alcançou http://vuln:8081/ping (200 pong) e não conseguiu alcançar http://patched:8081/ping quando bindHost era 127.0.0.1. Essa é a metade Remota.
Fixe a versão signalapp/Signal-Server v20261007.0.0. Dezoito buscas no código-fonte. Chat, chaves, mensagens, backups, doações, chamadas, MFA, omnibus gRPC e HTTP de saída voltaram vazios em Crítico/Alto. A busca em workers/admin não. O HttpConnectorFactory.admin() do Dropwizard 5.0.2 ainda usa a porta 8081 e um bind host nulo. O Signal nunca define adminConnectors. O admin padrão do Caddy é localhost. Este não é.
Caminho errado: tentar inicializar o Signal-Server completo. sample.yml passa no check e ainda precisa de FoundationDB, Dynamo, Redis e um pacote de segredos para o run. O resquício é o conector do framework que o ServerCommand inicia antes de tudo isso. O laboratório é o Dropwizard 5.0.2 com o mesmo YAML server: vazio e o nome de tarefa extra do Signal.
cd lab
./run.sh
Loopback 127.0.0.1:19010. Projeto Compose signal-server-dropwizard-admin. Witness SIGNAL-SERVER-DROPWIZARD-ADMIN-WITNESS. O YAML do gadget não tem bloco server:. O YAML negativo define bindHost: 127.0.0.1. Última linha:
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