Confusão de Dependência
Reading time: 9 minutes
tip
Aprenda e pratique Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprenda e pratique Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE)
Aprenda e pratique Hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Supporte o HackTricks
- Confira os planos de assinatura!
- Junte-se ao 💬 grupo do Discord ou ao grupo do telegram ou siga-nos no Twitter 🐦 @hacktricks_live.
- Compartilhe truques de hacking enviando PRs para o HackTricks e HackTricks Cloud repositórios do github.
Informações Básicas
Confusão de Dependência (a.k.a. ataques de substituição) ocorre quando um gerenciador de pacotes resolve um nome de dependência de um registro/fonte não intencionado e menos confiável (geralmente um registro público) em vez do privado/interno pretendido. Isso geralmente leva à instalação de um pacote controlado por um atacante.
Causas raiz comuns:
- Typosquatting/erro de digitação: Importando
reqests
em vez derequests
(resolve de registro público). - Pacote interno inexistente/abandonado: Importando
company-logging
que não existe mais internamente, então o resolvedor procura em registros públicos e encontra um pacote de um atacante. - Preferência de versão entre múltiplos registros: Importando um
company-requests
interno enquanto o resolvedor também pode consultar registros públicos e prefere a versão “melhor”/mais nova publicada publicamente por um atacante.
Ideia chave: Se o resolvedor pode ver múltiplos registros para o mesmo nome de pacote e é permitido escolher o “melhor” candidato globalmente, você está vulnerável a menos que restrinja a resolução.
Exploração
warning
Em todos os casos, o atacante só precisa publicar um pacote malicioso com o mesmo nome da dependência que sua construção resolve de um registro público. Hooks de tempo de instalação (por exemplo, scripts npm) ou caminhos de código de tempo de importação frequentemente permitem a execução de código.
Erros de Digitação & Inexistente
Se seu projeto referencia uma biblioteca que não está disponível no registro privado, e suas ferramentas recorrem a um registro público, um atacante pode semear um pacote malicioso com esse nome no registro público. Seus runners/máquinas de CI/dev irão buscá-lo e executá-lo.
Versão Não Especificada / Seleção de “Melhor-versão” entre índices
Desenvolvedores frequentemente deixam versões não fixadas ou permitem faixas amplas. Quando um resolvedor é configurado com índices internos e públicos, ele pode selecionar a versão mais nova independentemente da fonte. Para nomes internos como requests-company
, se o índice interno tem 1.0.1
mas um atacante publica 1.0.2
no registro público e seu resolvedor considera ambos, o pacote público pode vencer.
Correção AWS
Essa vulnerabilidade foi encontrada no AWS CodeArtifact (leia os detalhes neste post do blog). A AWS adicionou controles para marcar dependências/alimentações como internas vs externas para que o cliente não busque nomes “internos” de registros públicos upstream.
Encontrando Bibliotecas Vulneráveis
No post original sobre confusão de dependência, o autor procurou milhares de manifests expostos (por exemplo, package.json
, requirements.txt
, arquivos de bloqueio) para inferir nomes de pacotes internos e então publicou pacotes de versões superiores em registros públicos.
Playbook Prático do Atacante (para equipes vermelhas em testes autorizados)
- Enumerar nomes:
- Grep repositórios e configurações de CI em busca de arquivos de manifest/lock e namespaces internos.
- Procurar por prefixos específicos da organização (por exemplo,
@company/*
,company-*
, groupIds internos, padrões de ID do NuGet, caminhos de módulos privados para Go, etc.). - Verificar registros públicos para disponibilidade:
- Se o nome não estiver registrado publicamente, registre-o; se existir, tente o sequestro de subdependência visando nomes transitivos internos.
- Publicar com precedência:
- Escolha um semver que “vença” (por exemplo, uma versão muito alta) ou que corresponda às regras do resolvedor.
- Incluir execução mínima em tempo de instalação onde aplicável (por exemplo, scripts npm
preinstall
/install
/postinstall
). Para Python, prefira caminhos de execução em tempo de importação, pois wheels normalmente não executam código arbitrário na instalação. - Exfiltrar controle:
- Assegure que a saída é permitida de CI para seu endpoint controlado; caso contrário, use consultas DNS ou mensagens de erro como um canal lateral para provar a execução de código.
caution
Sempre obtenha autorização por escrito, use nomes/versões de pacotes únicos para o engajamento e imediatamente despublique ou coordene a limpeza quando o teste concluir.
Playbook do Defensor (o que realmente previne confusão)
Estratégias de alto nível que funcionam em ecossistemas:
- Use namespaces internos únicos e vincule-os a um único registro.
- Evite misturar níveis de confiança no momento da resolução. Prefira um único registro interno que faça proxy de pacotes públicos aprovados em vez de dar aos gerenciadores de pacotes tanto endpoints internos quanto públicos.
- Para gerenciadores que suportam, mapeie pacotes para fontes específicas (sem “melhor-versão” global entre registros).
- Fixar e bloquear:
- Use arquivos de bloqueio que registram as URLs de registro resolvidas (npm/yarn/pnpm) ou use fixação de hash/atestado (pip
--require-hashes
, verificação de dependência do Gradle). - Bloquear fallback público para nomes internos na camada de registro/rede.
- Reserve seus nomes internos em registros públicos quando viável para prevenir futuros squats.
Notas do Ecossistema e Trechos de Configuração Segura
Abaixo estão configurações pragmáticas e mínimas para reduzir ou eliminar a confusão de dependência. Prefira impor isso em ambientes de CI e desenvolvedores.
JavaScript/TypeScript (npm, Yarn, pnpm)
- Use pacotes com escopo para todo código interno e fixe o escopo ao seu registro privado.
- Mantenha instalações imutáveis em CI (arquivo de bloqueio npm,
yarn install --immutable
).
.npmrc (nível do projeto)
# Bind internal scope to private registry; do not allow public fallback for @company/*
@company:registry=https://registry.corp.example/npm/
# Always authenticate to the private registry
//registry.corp.example/npm/:_authToken=${NPM_TOKEN}
strict-ssl=true
package.json (para pacote interno)
{
"name": "@company/api-client",
"version": "1.2.3",
"private": false,
"publishConfig": {
"registry": "https://registry.corp.example/npm/",
"access": "restricted"
}
}
Yarn Berry (.yarnrc.yml)
npmScopes:
company:
npmRegistryServer: "https://registry.corp.example/npm/"
npmAlwaysAuth: true
# CI should fail if lockfile would change
enableImmutableInstalls: true
Dicas operacionais:
- Publique apenas pacotes internos dentro do escopo
@company
. - Para pacotes de terceiros, permita o registro público através do seu proxy espelho privado, não diretamente dos clientes.
- Considere habilitar a proveniência de pacotes npm para pacotes públicos que você publica para aumentar a rastreabilidade (não previne confusão por si só).
Python (pip / Poetry)
Regra principal: Não use --extra-index-url
para misturar níveis de confiança. Ou:
- Exponha um único índice interno que proxy e cache pacotes PyPI aprovados, ou
- Use seleção de índice explícita e fixação de hash.
pip.conf
[global]
index-url = https://pypi.corp.example/simple
# Disallow source distributions when possible
only-binary = :all:
# Lock with hashes generated via pip-tools
require-hashes = true
Gerar requisitos hash com pip-tools:
# From pyproject.toml or requirements.in
pip-compile --generate-hashes -o requirements.txt
pip install --require-hashes -r requirements.txt
Se você precisar acessar o PyPI público, faça isso através do seu proxy interno e mantenha uma lista de permissões explícita lá. Evite --extra-index-url
no CI.
.NET (NuGet)
Use o Mapeamento de Fonte de Pacote para vincular padrões de ID de pacote a fontes explícitas e evitar a resolução de feeds inesperados.
nuget.config
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<clear />
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
<add key="corp" value="https://nuget.corp.example/v3/index.json" />
</packageSources>
<packageSourceMapping>
<packageSource key="nuget.org">
<package pattern="*" />
</packageSource>
<packageSource key="corp">
<package pattern="Company.*" />
<package pattern="Internal.Utilities" />
</packageSource>
</packageSourceMapping>
</configuration>
Java (Maven/Gradle)
Maven settings.xml (espelho tudo para interno; desautorizar repositórios ad-hoc em POMs via Enforcer):
<settings>
<mirrors>
<mirror>
<id>internal-mirror</id>
<mirrorOf>*</mirrorOf>
<url>https://maven.corp.example/repository/group</url>
</mirror>
</mirrors>
</settings>
Adicione Enforcer para banir repositórios declarados em POMs e forçar o uso do seu espelho:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>
<version>3.6.1</version>
<executions>
<execution>
<id>enforce-no-repositories</id>
<goals><goal>enforce</goal></goals>
<configuration>
<rules>
<requireNoRepositories />
</rules>
</configuration>
</execution>
</executions>
</plugin>
Gradle: Centralize e bloqueie dependências.
- Aplique repositórios apenas em
settings.gradle(.kts)
:
dependencyResolutionManagement {
repositoriesMode = RepositoriesMode.FAIL_ON_PROJECT_REPOS
repositories {
maven { url = uri("https://maven.corp.example/repository/group") }
}
}
- Ative a verificação de dependências (checksums/siganturas) e comite
gradle/verification-metadata.xml
.
Go Modules
Configure módulos privados para que o proxy público e o banco de dados de checksums não sejam usados para eles.
# Use corporate proxy first, then public proxy as fallback
export GOPROXY=https://goproxy.corp.example,https://proxy.golang.org
# Mark private paths to skip proxy and checksum db
export GOPRIVATE=*.corp.example.com,github.com/your-org/*
export GONOSUMDB=*.corp.example.com,github.com/your-org/*
Rust (Cargo)
Substitua crates.io por um espelho interno aprovado ou diretório de fornecedor para builds; não permita fallback público arbitrário.
.cargo/config.toml
[source.crates-io]
replace-with = "corp-mirror"
[source.corp-mirror]
registry = "https://crates-mirror.corp.example/index"
Para publicação, seja explícito com --registry
e mantenha as credenciais restritas ao registro de destino.
Ruby (Bundler)
Use blocos de origem e desative Gemfiles de múltiplas fontes para que os gems venham apenas do repositório pretendido.
Gemfile
source "https://gems.corp.example"
source "https://rubygems.org" do
gem "rails"
gem "pg"
end
source "https://gems.corp.example" do
gem "company-logging"
end
Impor em nível de configuração:
bundle config set disable_multisource true
CI/CD e Controles de Registro que Ajudam
- Registro privado como um único ponto de entrada:
- Use Artifactory/Nexus/CodeArtifact/GitHub Packages/Azure Artifacts como o único endpoint que desenvolvedores/CI podem acessar.
- Implemente regras de bloqueio/permissão para que namespaces internos nunca sejam resolvidos a partir de fontes públicas upstream.
- Lockfiles são imutáveis em CI:
- npm: comite
package-lock.json
, usenpm ci
. - Yarn: comite
yarn.lock
, useyarn install --immutable
. - Python: comite
requirements.txt
com hash, imponha--require-hashes
. - Gradle: comite
verification-metadata.xml
e falhe em artefatos desconhecidos. - Controle de egressão: bloqueie o acesso direto do CI a registros públicos, exceto via o proxy aprovado.
- Reserva de nomes: pré-registre seus nomes/namespaces internos em registros públicos onde suportado.
- Proveniência de pacotes / atestações: ao publicar pacotes públicos, habilite a proveniência/atestações para tornar a adulteração mais detectável a montante.
Referências
- https://medium.com/@alex.birsan/dependency-confusion-4a5d60fec610
- https://zego.engineering/dependency-confusion-in-aws-codeartifact-86b9ff68963d
- https://learn.microsoft.com/en-us/nuget/consume-packages/package-source-mapping
- https://yarnpkg.com/configuration/yarnrc/
tip
Aprenda e pratique Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprenda e pratique Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE)
Aprenda e pratique Hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Supporte o HackTricks
- Confira os planos de assinatura!
- Junte-se ao 💬 grupo do Discord ou ao grupo do telegram ou siga-nos no Twitter 🐦 @hacktricks_live.
- Compartilhe truques de hacking enviando PRs para o HackTricks e HackTricks Cloud repositórios do github.