From 6e08a82077a44533bbd3ce23c2cb9d7029f9e69e Mon Sep 17 00:00:00 2001 From: Translator Date: Thu, 21 Aug 2025 02:40:56 +0000 Subject: [PATCH] Translated ['', 'src/pentesting-web/dependency-confusion.md'] to ko --- src/pentesting-web/dependency-confusion.md | 254 +++++++++++++++++++-- 1 file changed, 238 insertions(+), 16 deletions(-) diff --git a/src/pentesting-web/dependency-confusion.md b/src/pentesting-web/dependency-confusion.md index efe1962e7..381224f2b 100644 --- a/src/pentesting-web/dependency-confusion.md +++ b/src/pentesting-web/dependency-confusion.md @@ -2,44 +2,266 @@ {{#include ../banners/hacktricks-training.md}} - ## Basic Information -요약하자면, dependency confusion 취약점은 프로젝트가 **잘못된** 이름, **존재하지 않는** 이름 또는 **명시되지 않은 버전**의 라이브러리를 사용할 때 발생하며, 사용된 의존성 저장소가 **공개** 저장소에서 **업데이트된 버전을 수집**할 수 있도록 허용합니다. +Dependency Confusion (또는 대체 공격)은 패키지 관리자가 의도하지 않은, 덜 신뢰할 수 있는 레지스트리/소스(보통 공개 레지스트리)에서 의존성 이름을 해결할 때 발생합니다. 이는 일반적으로 공격자가 제어하는 패키지가 설치되는 결과를 초래합니다. -- **잘못된**: **`reqests`**를 가져오는 대신 `requests`를 가져오기 -- **존재하지 않는**: 더 이상 **존재하지 않는** 내부 라이브러리인 `company-logging` 가져오기 -- **명시되지 않은 버전**: **내부**에 **존재하는** `company-requests` 라이브러리를 가져오지만, 저장소가 **더 높은 버전**이 있는지 **공개 저장소**를 확인합니다. +일반적인 근본 원인: +- 타이포스쿼팅/오타: `reqests` 대신 `requests`를 가져오기(공개 레지스트리에서 해결됨). +- 존재하지 않거나 방치된 내부 패키지: 더 이상 내부에 존재하지 않는 `company-logging`을 가져오기 때문에 해결자가 공개 레지스트리에서 공격자의 패키지를 찾습니다. +- 여러 레지스트리 간의 버전 선호: 내부 `company-requests`를 가져오면서 해결자가 공개 레지스트리도 쿼리할 수 있고 공격자가 공개적으로 게시한 "최고의"/더 최신 버전을 선호합니다. + +핵심 아이디어: 해결자가 동일한 패키지 이름에 대해 여러 레지스트리를 볼 수 있고 "최고" 후보를 전 세계적으로 선택할 수 있다면, 해결을 제한하지 않는 한 취약합니다. ## Exploitation > [!WARNING] -> 모든 경우에 공격자는 피해 회사에서 사용하는 라이브러리의 **이름**으로 **악성 패키지**를 게시하기만 하면 됩니다. +> 모든 경우에 공격자는 공개 레지스트리에서 빌드가 해결하는 의존성과 동일한 이름의 악성 패키지만 게시하면 됩니다. 설치 시 후크(예: npm 스크립트) 또는 가져오기 시 코드 경로는 종종 코드 실행을 제공합니다. ### Misspelled & Inexistent -귀사가 **내부 라이브러리가 아닌 라이브러리**를 **가져오려고** 한다면, 라이브러리 저장소가 **공개 저장소**에서 이를 검색할 가능성이 높습니다. 공격자가 이를 생성했다면, 귀하의 코드와 실행 중인 머신은 매우 높은 확률로 손상될 것입니다. +프로젝트가 개인 레지스트리에서 사용할 수 없는 라이브러리를 참조하고 도구가 공개 레지스트리로 되돌아가면, 공격자는 공개 레지스트리에 해당 이름의 악성 패키지를 심을 수 있습니다. 귀하의 러너/CI/개발 머신은 이를 가져와 실행합니다. -### Unspecified Version +### Unspecified Version / “Best-version” selection across indexes -개발자들이 사용된 라이브러리의 **버전을 명시하지 않거나** 단지 **주 버전**만 명시하는 경우가 매우 흔합니다. 그러면 인터프리터는 해당 요구 사항에 맞는 **최신 버전**을 다운로드하려고 시도합니다.\ -라이브러리가 **잘 알려진 외부 라이브러리**(예: python `requests`)인 경우, **공격자는 많은 것을 할 수 없습니다**, 왜냐하면 그는 `requests`라는 이름의 라이브러리를 만들 수 없기 때문입니다(그가 원래 저자가 아닌 한).\ -그러나 라이브러리가 **내부**에 있는 경우, 이 예제의 `requests-company`처럼, **라이브러리 저장소**가 **외부에서도 새로운 버전을 확인**할 수 있도록 허용한다면, 공개적으로 사용 가능한 더 최신 버전을 검색할 것입니다.\ -따라서 **공격자가** 회사가 `requests-company` 라이브러리 **버전 1.0.1**(소규모 업데이트 허용)을 사용하고 있다는 것을 알게 되면, 그는 `requests-company` **버전 1.0.2**를 **게시**할 수 있으며, 회사는 내부 라이브러리 대신 **해당 라이브러리**를 **사용하게 됩니다**. +개발자는 종종 버전을 고정하지 않거나 넓은 범위를 허용합니다. 해결자가 내부 및 공개 인덱스 모두로 구성된 경우, 출처에 관계없이 최신 버전을 선택할 수 있습니다. 내부 이름인 `requests-company`의 경우, 내부 인덱스에 `1.0.1`이 있지만 공격자가 공개 레지스트리에 `1.0.2`를 게시하고 해결자가 둘 다 고려하면, 공개 패키지가 우선할 수 있습니다. ## AWS Fix -이 취약점은 AWS **CodeArtifact**에서 발견되었습니다(자세한 내용은 [**이 블로그 게시물**](https://zego.engineering/dependency-confusion-in-aws-codeartifact-86b9ff68963d)에서 읽어보세요).\ -AWS는 라이브러리가 내부인지 외부인지 명시할 수 있도록 하여 외부 저장소에서 내부 의존성을 다운로드하는 것을 방지했습니다. +이 취약점은 AWS CodeArtifact에서 발견되었습니다(자세한 내용은 이 블로그 게시물을 참조하십시오). AWS는 클라이언트가 상위 공개 레지스트리에서 "내부" 이름을 가져오지 않도록 의존성/피드를 내부와 외부로 표시하는 제어를 추가했습니다. ## Finding Vulnerable Libraries -[**dependency confusion에 대한 원본 게시물**](https://medium.com/@alex.birsan/dependency-confusion-4a5d60fec610)에서 저자는 자바스크립트 프로젝트의 의존성을 포함하는 수천 개의 노출된 package.json 파일을 검색했습니다. +의존성 혼란에 대한 원래 게시물에서 저자는 수천 개의 노출된 매니페스트(예: `package.json`, `requirements.txt`, lockfiles)를 찾아 내부 패키지 이름을 유추한 다음, 더 높은 버전의 패키지를 공개 레지스트리에 게시했습니다. -## References +## Practical Attacker Playbook (for red teams in authorized tests) + +- Enumerate names: +- 매니페스트/락 파일 및 내부 네임스페이스에 대한 리포지토리 및 CI 구성 grep. +- 조직 특정 접두사 찾기(예: `@company/*`, `company-*`, 내부 groupIds, NuGet ID 패턴, Go의 개인 모듈 경로 등). +- 공개 레지스트리에서 가용성 확인: +- 이름이 공개적으로 등록되지 않은 경우, 등록하십시오; 존재하는 경우 내부 전이 이름을 대상으로 하여 하위 의존성 탈취를 시도하십시오. +- 우선 순위로 게시: +- "이기는" 세멘틱 버전 선택(예: 매우 높은 버전) 또는 해결자 규칙에 맞추기. +- 적용 가능한 경우 최소 설치 시간 실행 포함(예: npm `preinstall`/`install`/`postinstall` 스크립트). Python의 경우, 휠이 일반적으로 설치 시 임의 코드를 실행하지 않으므로 가져오기 시간 실행 경로를 선호합니다. +- Exfil control: +- CI에서 귀하의 제어된 엔드포인트로의 아웃바운드가 허용되는지 확인하십시오; 그렇지 않으면 DNS 쿼리 또는 오류 메시지를 사이드 채널로 사용하여 코드 실행을 증명하십시오. + +> [!CAUTION] +> 항상 서면 승인을 받고, 참여를 위해 고유한 패키지 이름/버전을 사용하며, 테스트가 종료되면 즉시 게시 취소하거나 정리를 조정하십시오. + +## Defender Playbook (what actually prevents confusion) + +생태계 전반에 걸쳐 작동하는 고급 전략: +- 고유한 내부 네임스페이스를 사용하고 이를 단일 레지스트리에 바인딩합니다. +- 해결 시간에 신뢰 수준 혼합을 피하십시오. 승인된 공개 패키지를 프록시하는 단일 내부 레지스트리를 선호하고 패키지 관리자에게 내부 및 공개 엔드포인트를 모두 제공하지 마십시오. +- 지원하는 관리자에 대해 패키지를 특정 소스에 매핑하십시오(레지스트리 간의 전역 "최고 버전" 없음). +- Pin and lock: +- 해결된 레지스트리 URL을 기록하는 락파일(npm/yarn/pnpm)을 사용하거나 해시/증명 고정을 사용하십시오(pip `--require-hashes`, Gradle 의존성 검증). +- 레지스트리/네트워크 계층에서 내부 이름에 대한 공개 폴백을 차단하십시오. +- 가능할 경우 공개 레지스트리에서 내부 이름을 예약하여 향후 스쿼트를 방지하십시오. + +## Ecosystem Notes and Secure Config Snippets + +아래는 의존성 혼란을 줄이거나 제거하기 위한 실용적이고 최소한의 구성입니다. CI 및 개발 환경에서 이를 시행하는 것을 선호합니다. + +### JavaScript/TypeScript (npm, Yarn, pnpm) + +- 모든 내부 코드에 대해 범위가 지정된 패키지를 사용하고 범위를 개인 레지스트리에 고정합니다. +- CI에서 설치를 불변으로 유지합니다(npm lockfile, `yarn install --immutable`). + +.npmrc (프로젝트 수준) +``` +# 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 (내부 패키지용) +``` +{ +"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 +``` +운영 팁: +- `@company` 범위 내에서만 내부 패키지를 게시하세요. +- 타사 패키지의 경우, 클라이언트에서 직접가 아닌 개인 프록시/미러를 통해 공개 레지스트리를 허용하세요. +- 게시하는 공개 패키지에 대해 npm 패키지 출처를 활성화하여 추적 가능성을 높이는 것을 고려하세요 (혼란을 방지하지는 않습니다). + +### Python (pip / Poetry) + +핵심 규칙: 신뢰 수준을 혼합하기 위해 `--extra-index-url`을 사용하지 마세요. 다음 중 하나를 선택하세요: +- 승인된 PyPI 패키지를 프록시하고 캐시하는 단일 내부 인덱스를 노출하거나, +- 명시적 인덱스 선택 및 해시 고정을 사용하세요. + +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 +``` +pip-tools로 해시된 요구 사항 생성: +``` +# From pyproject.toml or requirements.in +pip-compile --generate-hashes -o requirements.txt +pip install --require-hashes -r requirements.txt +``` +공식 PyPI에 접근해야 하는 경우, 내부 프록시를 통해 접근하고 명시적인 허용 목록을 유지하십시오. CI에서 `--extra-index-url` 사용을 피하십시오. + +### .NET (NuGet) + +패키지 ID 패턴을 명시적인 소스에 연결하고 예상치 못한 피드에서의 해제를 방지하기 위해 패키지 소스 매핑을 사용하십시오. + +nuget.config +``` + + + + + + + + + + + + + + + + + +``` +### Java (Maven/Gradle) + +Maven settings.xml (모든 것을 내부로 미러링; Enforcer를 통해 POM에서 임시 저장소를 허용하지 않음): +``` + + + +internal-mirror +* +https://maven.corp.example/repository/group + + + +``` +POM에 선언된 리포지토리를 금지하고 미러 사용을 강제하기 위해 Enforcer 추가: +``` + +org.apache.maven.plugins +maven-enforcer-plugin +3.6.1 + + +enforce-no-repositories +enforce + + + + + + + + +``` +Gradle: 종속성을 중앙 집중화하고 잠금. +- `settings.gradle(.kts)`에서만 리포지토리 강제 적용: +``` +dependencyResolutionManagement { +repositoriesMode = RepositoriesMode.FAIL_ON_PROJECT_REPOS +repositories { +maven { url = uri("https://maven.corp.example/repository/group") } +} +} +``` +- 종속성 검증(체크섬/서명)을 활성화하고 `gradle/verification-metadata.xml`을 커밋합니다. + +### Go Modules + +공용 프록시와 체크섬 DB가 사용되지 않도록 개인 모듈을 구성합니다. +``` +# 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) + +승인된 내부 미러 또는 공급업체 디렉토리로 crates.io를 교체하여 빌드를 수행하십시오; 임의의 공개 대체를 허용하지 마십시오. + +.cargo/config.toml +``` +[source.crates-io] +replace-with = "corp-mirror" + +[source.corp-mirror] +registry = "https://crates-mirror.corp.example/index" +``` +게시를 위해 `--registry`를 명시적으로 사용하고 자격 증명을 대상 레지스트리로 제한하십시오. + +### Ruby (Bundler) + +소스 블록을 사용하고 다중 소스 Gemfile을 비활성화하여 gem이 의도한 리포지토리에서만 오도록 하십시오. + +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 +``` +구성 수준에서 강제 적용: +``` +bundle config set disable_multisource true +``` +## CI/CD 및 레지스트리 제어 + +- 단일 진입점으로서의 개인 레지스트리: +- Artifactory/Nexus/CodeArtifact/GitHub Packages/Azure Artifacts를 개발자/CI가 접근할 수 있는 유일한 엔드포인트로 사용합니다. +- 내부 네임스페이스가 항상 업스트림 공개 소스에서 해결되지 않도록 차단/허용 규칙을 구현합니다. +- CI에서 잠금 파일은 불변입니다: +- npm: `package-lock.json`을 커밋하고, `npm ci`를 사용합니다. +- Yarn: `yarn.lock`을 커밋하고, `yarn install --immutable`을 사용합니다. +- Python: 해시된 `requirements.txt`를 커밋하고, `--require-hashes`를 강제합니다. +- Gradle: `verification-metadata.xml`을 커밋하고, 알 수 없는 아티팩트에서 실패합니다. +- 아웃바운드 이그레스 제어: 승인된 프록시를 통해서만 CI에서 공개 레지스트리에 직접 접근을 차단합니다. +- 이름 예약: 지원되는 공개 레지스트리에서 내부 이름/네임스페이스를 사전 등록합니다. +- 패키지 출처/증명: 공개 패키지를 게시할 때, 변조를 더 쉽게 감지할 수 있도록 출처/증명을 활성화합니다. + + +## 참고 문헌 - [https://medium.com/@alex.birsan/dependency-confusion-4a5d60fec610](https://medium.com/@alex.birsan/dependency-confusion-4a5d60fec610) - [https://zego.engineering/dependency-confusion-in-aws-codeartifact-86b9ff68963d](https://zego.engineering/dependency-confusion-in-aws-codeartifact-86b9ff68963d) +- [https://learn.microsoft.com/en-us/nuget/consume-packages/package-source-mapping](https://learn.microsoft.com/en-us/nuget/consume-packages/package-source-mapping) +- [https://yarnpkg.com/configuration/yarnrc/](https://yarnpkg.com/configuration/yarnrc/) {{#include ../banners/hacktricks-training.md}}