Dependency Confusion
Reading time: 9 minutes
tip
Ucz się i ćwicz Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Ucz się i ćwicz Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE)
Ucz się i ćwicz Hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Wsparcie dla HackTricks
- Sprawdź plany subskrypcyjne!
- Dołącz do 💬 grupy Discord lub grupy telegramowej lub śledź nas na Twitterze 🐦 @hacktricks_live.
- Dziel się trikami hackingowymi, przesyłając PR-y do HackTricks i HackTricks Cloud repozytoriów na githubie.
Basic Information
Dependency Confusion (znane również jako ataki substytucyjne) występuje, gdy menedżer pakietów rozwiązuje nazwę zależności z niezamierzonego, mniej zaufanego rejestru/źródła (zwykle publicznego rejestru) zamiast zamierzonego prywatnego/wewnętrznego. Zwykle prowadzi to do zainstalowania pakietu kontrolowanego przez atakującego.
Typowe przyczyny:
- Typosquatting/błędne pisanie: Importowanie
reqests
zamiastrequests
(rozwiązuje z publicznego rejestru). - Nieistniejący/porzucony wewnętrzny pakiet: Importowanie
company-logging
, który już nie istnieje wewnętrznie, więc resolver szuka w publicznych rejestrach i znajduje pakiet atakującego. - Preferencje wersji w różnych rejestrach: Importowanie wewnętrznego
company-requests
, podczas gdy resolver ma również prawo do zapytań w publicznych rejestrach i preferuje „najlepszą” / nowszą wersję opublikowaną publicznie przez atakującego.
Kluczowa idea: Jeśli resolver może zobaczyć wiele rejestrów dla tej samej nazwy pakietu i ma prawo wybrać „najlepszego” kandydata globalnie, jesteś narażony, chyba że ograniczysz rozwiązywanie.
Exploitation
warning
W każdym przypadku atakujący musi tylko opublikować złośliwy pakiet o tej samej nazwie, co zależność, którą twoja kompilacja rozwiązuje z publicznego rejestru. Hooki w czasie instalacji (np. skrypty npm) lub ścieżki kodu w czasie importu często dają możliwość wykonania kodu.
Misspelled & Inexistent
Jeśli twój projekt odnosi się do biblioteki, która nie jest dostępna w prywatnym rejestrze, a twoje narzędzia wracają do publicznego rejestru, atakujący może umieścić złośliwy pakiet o tej nazwie w publicznym rejestrze. Twoje maszyny wykonawcze/CI/deweloperskie pobiorą i wykonają go.
Unspecified Version / “Best-version” selection across indexes
Programiści często pozostawiają wersje nieprzypisane lub pozwalają na szerokie zakresy. Gdy resolver jest skonfigurowany z zarówno wewnętrznymi, jak i publicznymi indeksami, może wybrać najnowszą wersję niezależnie od źródła. Dla wewnętrznych nazw, takich jak requests-company
, jeśli wewnętrzny indeks ma 1.0.1
, ale atakujący publikuje 1.0.2
w publicznym rejestrze i twój resolver bierze pod uwagę obie, pakiet publiczny może wygrać.
AWS Fix
Ta podatność została znaleziona w AWS CodeArtifact (przeczytaj szczegóły w tym poście na blogu). AWS dodało kontrole, aby oznaczyć zależności/źródła jako wewnętrzne lub zewnętrzne, aby klient nie pobierał „wewnętrznych” nazw z upstream publicznych rejestrów.
Finding Vulnerable Libraries
W oryginalnym poście na temat zamieszania z zależnościami autor szukał tysięcy ujawnionych manifestów (np. package.json
, requirements.txt
, pliki blokady), aby wywnioskować wewnętrzne nazwy pakietów, a następnie publikował pakiety o wyższych wersjach w publicznych rejestrach.
Practical Attacker Playbook (for red teams in authorized tests)
- Enumerate names:
- Grep repos i konfiguracje CI w poszukiwaniu manifestów/pliki blokady i wewnętrznych przestrzeni nazw.
- Szukaj prefiksów specyficznych dla organizacji (np.
@company/*
,company-*
, wewnętrzne groupIds, wzory ID NuGet, prywatne ścieżki modułów dla Go itp.). - Sprawdź dostępność w publicznych rejestrach:
- Jeśli nazwa nie jest zarejestrowana publicznie, zarejestruj ją; jeśli istnieje, spróbuj przejąć subzależności, celując w wewnętrzne nazwy przejściowe.
- Publish with precedence:
- Wybierz semver, który „wygrywa” (np. bardzo wysoką wersję) lub odpowiada zasadom resolvera.
- Dołącz minimalne wykonanie w czasie instalacji, gdzie to możliwe (np. skrypty npm
preinstall
/install
/postinstall
). Dla Pythona preferuj ścieżki wykonania w czasie importu, ponieważ koła zazwyczaj nie wykonują dowolnego kodu podczas instalacji. - Exfil control:
- Upewnij się, że wychodzące połączenia są dozwolone z CI do twojego kontrolowanego punktu końcowego; w przeciwnym razie użyj zapytań DNS lub komunikatów o błędach jako kanału bocznego do udowodnienia wykonania kodu.
caution
Zawsze uzyskuj pisemną autoryzację, używaj unikalnych nazw pakietów/wersji dla zaangażowania i natychmiast unpublikuj lub skoordynuj czyszczenie po zakończeniu testów.
Defender Playbook (what actually prevents confusion)
Strategie na wysokim poziomie, które działają w różnych ekosystemach:
- Używaj unikalnych wewnętrznych przestrzeni nazw i przypisuj je do jednego rejestru.
- Unikaj mieszania poziomów zaufania w czasie rozwiązywania. Preferuj jeden wewnętrzny rejestr, który pośredniczy w zatwierdzonych publicznych pakietach, zamiast dawać menedżerom pakietów zarówno wewnętrzne, jak i publiczne punkty końcowe.
- Dla menedżerów, którzy to wspierają, mapuj pakiety do konkretnych źródeł (brak globalnej „najlepszej wersji” w różnych rejestrach).
- Pin and lock:
- Używaj plików blokady, które rejestrują rozwiązane adresy URL rejestru (npm/yarn/pnpm) lub używaj przypinania haszy/atestacji (pip
--require-hashes
, weryfikacja zależności Gradle). - Zablokuj publiczne zaplecze dla wewnętrznych nazw na poziomie rejestru/sieci.
- Rezerwuj swoje wewnętrzne nazwy w publicznych rejestrach, gdy to możliwe, aby zapobiec przyszłemu squat.
Ecosystem Notes and Secure Config Snippets
Poniżej znajdują się pragmatyczne, minimalne konfiguracje, aby zredukować lub wyeliminować zamieszanie z zależnościami. Preferuj egzekwowanie ich w CI i środowiskach deweloperskich.
JavaScript/TypeScript (npm, Yarn, pnpm)
- Używaj pakietów z zakresem dla całego kodu wewnętrznego i przypisz zakres do swojego prywatnego rejestru.
- Utrzymuj instalacje niezmienne w CI (plik blokady npm,
yarn install --immutable
).
.npmrc (poziom projektu)
# 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 (dla wewnętrznego pakietu)
{
"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
Operational tips:
- Publikuj tylko wewnętrzne pakiety w obrębie zakresu
@company
. - Dla pakietów zewnętrznych, zezwól na publiczny rejestr za pośrednictwem swojego prywatnego proxy/lustra, a nie bezpośrednio od klientów.
- Rozważ włączenie pochodzenia pakietów npm dla publicznych pakietów, które publikujesz, aby zwiększyć śledzenie (samo w sobie nie zapobiega pomieszaniu).
Python (pip / Poetry)
Core rule: Nie używaj --extra-index-url
, aby mieszać poziomy zaufania. Albo:
- Udostępnij jeden wewnętrzny indeks, który proxy i buforuje zatwierdzone pakiety PyPI, lub
- Użyj jawnego wyboru indeksu i przypinania haszy.
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
Wygeneruj zhaszowane wymagania za pomocą pip-tools:
# From pyproject.toml or requirements.in
pip-compile --generate-hashes -o requirements.txt
pip install --require-hashes -r requirements.txt
Jeśli musisz uzyskać dostęp do publicznego PyPI, zrób to przez swój wewnętrzny proxy i utrzymuj tam wyraźną listę dozwolonych źródeł. Unikaj --extra-index-url
w CI.
.NET (NuGet)
Użyj mapowania źródeł pakietów, aby powiązać wzorce identyfikatorów pakietów z wyraźnymi źródłami i zapobiec rozwiązywaniu z nieoczekiwanych źródeł.
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 (lustro wszystko do wewnętrznego; zabroń repozytoriów ad-hoc w POM-ach za pomocą Enforcer):
<settings>
<mirrors>
<mirror>
<id>internal-mirror</id>
<mirrorOf>*</mirrorOf>
<url>https://maven.corp.example/repository/group</url>
</mirror>
</mirrors>
</settings>
Dodaj Enforcer, aby zablokować repozytoria zadeklarowane w POM-ach i wymusić użycie swojego lustra:
<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: Centralizuj i zablokuj zależności.
- Wymuś repozytoria tylko w
settings.gradle(.kts)
:
dependencyResolutionManagement {
repositoriesMode = RepositoriesMode.FAIL_ON_PROJECT_REPOS
repositories {
maven { url = uri("https://maven.corp.example/repository/group") }
}
}
- Włącz weryfikację zależności (sumy kontrolne/podpisy) i zatwierdź
gradle/verification-metadata.xml
.
Go Modules
Skonfiguruj prywatne moduły, aby publiczny proxy i baza danych sum kontrolnych nie były używane dla nich.
# 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)
Zastąp crates.io zatwierdzonym wewnętrznym lustrem lub katalogiem dostawcy dla kompilacji; nie zezwalaj na dowolne publiczne zapasowe źródło.
.cargo/config.toml
[source.crates-io]
replace-with = "corp-mirror"
[source.corp-mirror]
registry = "https://crates-mirror.corp.example/index"
Aby opublikować, bądź dokładny z --registry
i ogranicz dane uwierzytelniające do docelowego rejestru.
Ruby (Bundler)
Użyj bloków źródłowych i wyłącz multisource Gemfiles, aby gemy pochodziły tylko z zamierzonego repozytorium.
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
Wymuś na poziomie konfiguracji:
bundle config set disable_multisource true
CI/CD i Kontrole Rejestru, Które Pomagają
- Prywatny rejestr jako pojedynczy punkt dostępu:
- Użyj Artifactory/Nexus/CodeArtifact/GitHub Packages/Azure Artifacts jako jedynego punktu, do którego mogą uzyskać dostęp deweloperzy/CI.
- Wprowadź zasady blokowania/zezwalania, aby wewnętrzne przestrzenie nazw nigdy nie były rozwiązywane z publicznych źródeł upstream.
- Pliki blokad są niemutowalne w CI:
- npm: zatwierdź
package-lock.json
, użyjnpm ci
. - Yarn: zatwierdź
yarn.lock
, użyjyarn install --immutable
. - Python: zatwierdź haszowany
requirements.txt
, wymuś--require-hashes
. - Gradle: zatwierdź
verification-metadata.xml
i niepowodzenie w przypadku nieznanych artefaktów. - Kontrola wychodzącego ruchu: zablokuj bezpośredni dostęp z CI do publicznych rejestrów, z wyjątkiem zatwierdzonego proxy.
- Rezerwacja nazw: wstępnie zarejestruj swoje wewnętrzne nazwy/przestrzenie nazw w publicznych rejestrach, gdzie to możliwe.
- Pochodzenie pakietów / zaświadczenia: podczas publikowania publicznych pakietów włącz pochodzenie/zaświadczenia, aby uczynić manipulacje bardziej wykrywalnymi w dalszej kolejności.
Odniesienia
- 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
Ucz się i ćwicz Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Ucz się i ćwicz Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE)
Ucz się i ćwicz Hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Wsparcie dla HackTricks
- Sprawdź plany subskrypcyjne!
- Dołącz do 💬 grupy Discord lub grupy telegramowej lub śledź nas na Twitterze 🐦 @hacktricks_live.
- Dziel się trikami hackingowymi, przesyłając PR-y do HackTricks i HackTricks Cloud repozytoriów na githubie.