Write-upCloud Security

Dependency Confusion ve Yazılım Tedarik Zinciri Saldırıları

Paket isim karışıklığından kurumsal ele geçirmeye: çözümleme mantığının istismarı ve katmanlı savunma

AL
AltaySecAltaySec Red Team
26 Ağustos 2026
10 dakika okuma
Özet

Dependency confusion, bir kuruluşun özel (private) paket isimlerini bilen bir saldırganın aynı ismi yüksek sürüm numarasıyla public registry'ye (npm, PyPI, RubyGems) yüklemesiyle paket yöneticisinin kötü niyetli sürümü çözümlemesidir. Alex Birsan 2021'de bu teknikle Apple, Microsoft, PayPal dahil 35+ şirkete sızarak 130.000 dolardan fazla ödül kazandı; PyTorch ise Aralık 2022'de aynı sınıf saldırıya maruz kaldı. Savunma tek bir yapılandırma değil, scoped namespace, isim rezervasyonu, lockfile+hash, tek-kaynak registry ve SCA'dan oluşan katmanlı bir modeldir.

Dependency confusion tam olarak nedir?

Dependency confusion (bağımlılık karışıklığı / namespace confusion), bir paket yöneticisinin aynı isme sahip iki paket arasında yanlış olanı — kuruluşun özel iç paketi yerine public registry’deki kötü niyetli paketi — seçmesine dayanan bir yazılım tedarik zinciri saldırısıdır. Kök neden bir yazılım hatası değil, çözümleme (resolution) mantığının bir yan etkisidir: npm, pip gibi araçlar hem özel bir depoya hem de public depoya (npmjs.com, pypi.org) bakacak şekilde yapılandırıldığında, çoğu durumda en yüksek sürüm numarasına sahip paketi “en iyi eşleşme” kabul eder. Saldırgan, hedefin internal-auth-lib gibi bir iç paket ismini öğrenip aynı ismi public registry’ye 9.9.9 gibi absürt yüksek bir sürümle yüklerse, geliştiricinin makinesinde veya CI/CD hattında saldırganın kodu sessizce indirilip install anında çalıştırılır.

Bu saldırının tehlikesi, klasik zafiyetlerden farklı olarak bilinen bir CVE’ye dayanmamasıdır. Yeni yüklenen kötü niyetli bir paketin geçmişi yoktur, imza kaydı yoktur ve CVE’si yoktur; dolayısıyla yalnızca bilinen zafiyetleri arayan tarayıcılar bunu ıskalar. Saldırı, kodun kendisini değil, kodun nereden geldiğine dair güveni hedef alır. Bu yazı tekniğin kökenini, çözümleme mekaniğini, kamuya açık gerçek vakaları ve — AltaySec Red Team perspektifinden — kurumsal ölçekte uygulanabilir katmanlı savunmayı ele alır.

Alex Birsan’ın 2021 araştırması: tekniğin doğuşu

Teknik, güvenlik araştırmacısı Alex Birsan’ın Şubat 2021’de yayımladığı “Dependency Confusion: How I Hacked Into Apple, Microsoft and Dozens of Other Companies” başlıklı çalışmasıyla geniş kitlelere ulaştı. Birsan, hedef şirketlerin iç paket isimlerini kamuya açık kaynaklardan derledi: GitHub’da yanlışlıkla paylaşılmış package.json dosyaları, hata mesajları ve — en verimli kaynak olarak — public’e açılmış JavaScript build çıktıları. Modern build süreçleri iç package.json bağımlılık listelerini derlenmiş script dosyalarının içine gömdüğü için, bu isimler tarayıcıdan erişilebilen dosyalarda görünür hale geliyordu.

Birsan, topladığı isimleri npm, PyPI ve RubyGems’e yükledi; her pakete yalnızca sistem bilgisini (hostname, kullanıcı, DNS sorguları) araştırmacıya geri gönderen zararsız bir “phone-home” yükü koydu. Sonuç: 35’ten fazla büyük şirkette — Microsoft, Apple, PayPal, Shopify, Netflix, Yelp, Tesla ve Uber dahil — iç ortamlarda kod çalıştırıldığına dair kanıt topladı ve toplamda 130.000 doları aşan bug bounty ödülü kazandı. Bu araştırmanın önemi, tekniğin tek bir şirkete özgü bir yanlış yapılandırma değil, ekosistem çapında varsayılan davranıştan doğan sistemik bir sınıf olduğunu göstermesiydi.

Saldırı nasıl çalışır: çözümleme mantığının istismarı

Saldırının kalbinde iki karar kuralı yatar: (1) paket birden fazla index’te varsa en yüksek semver kazanır, (2) yanlış yapılandırılmış istemci, özel depoda bulamadığı paketi sessizce public depoda arar (fallback). Python tarafında bu davranış özellikle --extra-index-url bayrağıyla tetiklenir. --extra-index-url, index’leri kapsamlamaz (scope etmez); tüm index’leri birleştirir ve en yüksek sürümü kurar:

# TEHLİKELİ: pip her iki index'e de bakar, en yüksek sürümü seçer
pip install internal-auth-lib \
  --index-url https://nexus.sirket.local/simple \
  --extra-index-url https://pypi.org/simple
# Saldırgan pypi.org'a internal-auth-lib==9.9.9 yüklediyse -> o kurulur

Bu davranış pip’te CVE-2018-20225 olarak kayıtlıdır (CVSS 7.8, Yüksek). Ancak dürüstlük adına bir nüansı belirtmek gerekir: pip geliştiricileri bunu kod düzeyinde düzeltilecek bir zafiyet değil, kullanıcının yapılandırma sorumluluğu olarak değerlendirip davranışı değiştirmeyi reddetmiştir. Yani bu “açık” bir bug değil, tehlikeli bir varsayılantır — savunmanın istemci yapılandırmasında yapılması gerektiği anlamına gelir.

npm tarafında mekanik biraz farklıdır. Scope’suz (örneğin internal-auth-lib) bir paket için .npmrc yalnızca registry adresini tanımlar; paket özel registry’de bulunamazsa istemci public registry’ye düşer. Saldırganın public’e yüklediği yüksek sürüm, floating aralıklarla (^1.0.0) birlikte çözümlendiğinde tercih edilir. Kötü niyetli paket, package.json içindeki preinstall/install/postinstall script’leri sayesinde kurulum anında çalışır:

{
  "name": "internal-auth-lib",
  "version": "9.9.9",
  "scripts": {
    "preinstall": "node ./exfil.js"
  }
}

exfil.js içeriği tipik olarak ortam değişkenlerini, ~/.aws/credentials, .npmrc token’larını veya SSH anahtarlarını toplar ve DNS/HTTPS üzerinden dışarı sızdırır. CI/CD runner’ları bu saldırının en kârlı hedefidir çünkü genellikle bulut kimlik bilgileri ve registry token’larıyla dolu ayrıcalıklı bir bağlamda çalışırlar.

İç paket isimleri nasıl sızar?

Saldırının başarısı tamamen doğru iç paket isimlerini bilmeye bağlıdır; bu yüzden savunmanın ilk cephesi sızıntı yüzeyini anlamaktır. İsimlerin kamuya açık hale geldiği yaygın kanallar:

  • Build çıktıları: Webpack/Rollup gibi bundler’lar iç bağımlılık isimlerini derlenmiş JS’e gömer; source-map dosyaları (.map) tam bağımlılık ağacını ifşa edebilir.
  • Halka açık GitHub repoları ve fork’lar: Bir çalışanın kişisel hesabına push ettiği bir package.json veya requirements.txt yeterlidir.
  • Docker imaj katmanları: Public imajlardaki COPY package.json veya pip freeze çıktıları katman geçmişinde kalır.
  • Hata mesajları ve loglar: Public’e sızan stack trace’ler Cannot find module '@sirket/...' gibi isimler içerir.
  • CI logları ve artifact’lar: Açık bırakılmış pipeline logları kurulum komutlarını ve index adreslerini gösterir.

Bir red team değerlendirmesinde bu isimleri toplamak, GitHub dork’ları ve pasif keşifle başlar:

# Public JS build çıktılarında scope'lu iç paket isimleri ara
grep -roE '@[a-z0-9-]+/[a-z0-9-]+' ./dist/ | sort -u

Gerçek dünya vakası: PyTorch torchtriton (Aralık 2022)

Dependency confusion’ın teorik bir tehdit olmadığının kamuya açık, iyi belgelenmiş kanıtı PyTorch olayıdır. 25–30 Aralık 2022 tarihleri arasında PyTorch nightly build’ini indiren herkes etkilendi. Saldırgan, PyTorch’un kendi nightly paket index’inde barındırdığı torchtriton bağımlılığıyla aynı isimde bir paketi PyPI’a yükledi. pip’in çözümleme davranışı nedeniyle, resmi depodaki meşru sürüm yerine PyPI’daki kötü niyetli sürüm varsayılan olarak kuruldu.

Kötü niyetli yük sistem bilgisi ve dosyaları topluyor, ardından bunları *.h4ck[.]cfd alan adına şifreli DNS sorguları üzerinden dışarı sızdırıyordu — DNS exfiltration, çıkış (egress) firewall’larını atlatmak için sık kullanılan bir yöntemdir. PyTorch ekibi olaya iki hamleyle yanıt verdi: bağımlılık ismini pytorch-triton olarak değiştirdiler ve saldırının tekrarını önlemek için torchtriton ismini PyPI’da bir dummy (yer tutucu) paket olarak rezerve ettiler. Bu ikinci hamle, aşağıda anlatılan “namespace claim” savunmasının canlı bir örneğidir.

Komşu tehditler: typosquatting ve slopsquatting

Dependency confusion, isim tabanlı tedarik zinciri saldırılarının tek türü değildir. İki yakın akrabası aynı güven zafiyetini farklı vektörlerle istismar eder:

  • Typosquatting: Saldırgan popüler bir paketin yazım hatası varyantını yükler — requests yerine requets, colorama yerine colorama-py, selenium yerine selemium. Geliştiricinin parmak hatası veya kopyala-yapıştır yanlışı yeterlidir. Ekim 2024’te belgelenen MUT-8694 kampanyası, PyPI’daki larpexodus gibi typosquat paketlerle Windows binary’leri indiriyordu; aynı indirici npm paketlerinde de görüldü.
  • Slopsquatting: Yapay zeka kod asistanlarının halüsinasyonla uydurduğu, aslında var olmayan paket isimlerinin saldırganlarca gerçek kötü niyetli paketler olarak kaydedilmesidir. LLM’ler mevcut olmayan bir bağımlılık önerdiğinde ve saldırgan o ismi önceden kaydettiğinde, geliştirici tavsiyeye güvenerek zararlı paketi kurar.

Bu üç saldırı ortak bir kör noktayı paylaşır: yeni yüklenmiş kötü niyetli paketin bilinen bir CVE’si olmadığı için imza/CVE tabanlı taramalar onları göremez.

Karşılaştırma tablosu: saldırı türü × vektör × savunma

Saldırı türü Ana vektör Tetikleyici davranış Öncelikli savunma
Dependency confusion İç paket ismini public registry’ye yüksek sürümle yükleme Çoklu index + “en yüksek sürüm kazanır” + fallback Scoped namespace + isim rezervasyonu + tek-kaynak registry (--index-url)
Typosquatting Popüler paketin yazım varyantını yayınlama İnsan yazım/kopyalama hatası Lockfile + install script denetimi + allow-list
Slopsquatting LLM’in uydurduğu ismi gerçek pakete dönüştürme AI önerisine körü körüne güven Bağımlılığı manuel doğrulama + provenance kontrolü
Malicious maintainer / hijack Meşru paket veya hesabın ele geçirilmesi Güncelleme güveni, zayıf 2FA Sürüm pinleme + hash doğrulama + imzalı yayın (provenance)

Savunma: katmanlı bir model

Dependency confusion’a karşı tek bir sihirli ayar yoktur; savunma birbirini yedekleyen katmanlardan oluşur. Katmanları önem sırasına göre inceleyelim.

1. Scoped / namespace kullanımı (en güçlü yapısal önlem)

npm’de iç paketleri her zaman bir scope altında adlandırın: internal-auth-lib yerine @sirket/auth-lib. Ardından .npmrc ile o scope’u yalnızca özel registry’ye bağlayın. Böylece saldırgan public’te @sirket/auth-lib ismini alamaz (scope’lar sahibine kilitlenir) ve istemci o scope için asla public’e düşmez:

# .npmrc — scope'u özel registry'ye sabitle
@sirket:registry=https://nexus.sirket.local/npm/
//nexus.sirket.local/npm/:_authToken=${NPM_TOKEN}

2. İç paket isimlerini public registry’de rezerve et (namespace claim)

Scope kullanamadığınız durumlarda (ör. bazı Python/legacy kurulumlar), iç isimlerinizi public registry’de boş yer tutucu paket olarak kaydedin — tıpkı PyTorch’un torchtriton için yaptığı gibi. Saldırgan zaten kayıtlı bir ismi ele geçiremez. Bu ucuz ve etkili bir “önce sen al” stratejisidir.

3. Tek-kaynak çözümleme: fallback’i kapat

Microsoft’un “3 Ways to Mitigate Risk When Using Private Package Feeds” belgesinin merkez tavsiyesi: tek bir private feed’e referans verin ve public paketleri o feed üzerinden proxy’leyin. Python için --extra-index-url yerine --index-url (tekil) kullanın; özel registry’yi (Artifactory, Nexus, Verdaccio) PyPI’ı seçici olarak proxy’leyecek şekilde yapılandırın:

# pip.conf — TEK kaynak; public paketler registry üzerinden proxy'lenir
[global]
index-url = https://nexus.sirket.local/pypi/simple
# extra-index-url KULLANMAYIN

Registry tarafında pass-through (her isteği körü körüne PyPI’a ileten) yerine allow-list / merged-view politikası uygulayın: iç isim public’e sızsa bile registry o ismi dışarıya sormaz.

4. Lockfile + bütünlük (integrity) hash’leri

Lockfile’lar (package-lock.json, yarn.lock, poetry.lock, pip için --require-hashes) çözülen paketin tam sürümünü ve kriptografik hash’ini sabitler. Hash uyuşmazsa kurulum başarısız olur; böylece aynı isimle gelen farklı (kötü niyetli) bir tarball reddedilir:

# pip: yalnızca hash'i eşleşen paketleri kur
pip install -r requirements.txt --require-hashes

CI’da her zaman npm ci (lockfile’a birebir sadık) kullanın; npm install lockfile’ı güncelleyebildiği için CI’da tehlikelidir.

5. Install script’lerini dizginle

Zararın çoğu postinstall/preinstall script’lerinden gelir. Güvenilmeyen kurulumlarda bunları kapatın:

npm ci --ignore-scripts

6. SCA, provenance ve sürekli izleme

  • SCA (Software Composition Analysis) araçlarını yalnızca bilinen CVE’ler için değil, şüpheli davranış (yeni yayınlanmış paket, install script varlığı, ağ çağrısı) için de yapılandırın.
  • Provenance / imza: npm’in provenance özelliği ve Sigstore tabanlı imzalama, bir paketin gerçekten beklenen kaynaktan (ör. belirli bir GitHub Actions build’inden) geldiğini kanıtlar.
  • SBOM üretin ve saklayın; bir olay anında hangi build’in hangi sürümü çektiğini geriye dönük izleyebilmek olay müdahalesini kısaltır.

Kendi ortamını test et: red team bakışı

Savunmanın gerçekten çalıştığını doğrulamanın yolu, saldırıyı kontrollü biçimde taklit etmektir. Bir iç değerlendirmede izlenecek dürüst ve zararsız yaklaşım:

  1. Sızıntı keşfi: Public build çıktılarınızı, GitHub organizasyonunuzu ve çalışan fork’larını iç paket isimleri için tarayın (yukarıdaki grep ve GitHub dork’ları).
  2. Rezervasyon denetimi: Bulunan her scope’suz iç ismin public registry’de sizin adınıza kayıtlı olup olmadığını kontrol edin. Kayıtsız her isim bir boşluktur.
  3. Çözümleme testi: Kendi kontrolünüzdeki bir isim için, yalnızca zararsız bir “phone-home” (kendi sunucunuza HTTP çağrısı) içeren yüksek sürümlü bir test paketini kendi özel registry yapılandırmanıza karşı deneyin — public registry’yi kirletmeden, izole bir ortamda. İstemcinin public’e düşüp düşmediğini gözlemleyin.
  4. CI ayrıcalık denetimi: Runner’ların kimlik bilgilerini minimize edin; --ignore-scripts ve --require-hashes uygulanıyor mu doğrulayın.

Kritik dürüstlük kuralı: public registry’ye asla gerçek bir organizasyonun ismini taşıyan çalışan bir yük yüklemeyin. Bu, üçüncü tarafları riske atar ve etik/yasal sınırı aşar. Doğrulama daima izole, kendi kontrolünüzdeki altyapıda yapılır.

Özet

Dependency confusion, bir kod açığından değil, paket yöneticilerinin güven modelindeki bir boşluktan doğar: aynı isme sahip iki paket arasında yanlış olanı seçme eğilimi. Alex Birsan’ın 2021’deki 35+ şirketi kapsayan araştırması tekniğin sistemik olduğunu, PyTorch’un Aralık 2022 torchtriton olayı ise gerçek dünyada işletildiğini kanıtladı. Typosquatting ve slopsquatting aynı güven boşluğunu farklı vektörlerle istismar eder ve hepsi ortak bir kör noktayı paylaşır — yeni kötü niyetli paketlerin CVE’si yoktur.

Etkili savunma tek bir ayar değil, katmanlı bir mimaridir: scoped namespace ile isimleri yapısal olarak koru, sahip olamadığın isimleri public’te rezerve et, çözümlemeyi tek kaynağa (--index-url) indir, lockfile ve hash ile bütünlüğü kilitle, install script’lerini dizginle ve SCA + provenance ile sürekli izle. Bu katmanların her biri bir diğerinin başarısız olduğu senaryoyu yakalar; asıl güç, birinin değil, hepsinin birlikte uygulanmasındadır.


Kaynaklar

  • Alex Birsan — “Dependency Confusion: How I Hacked Into Apple, Microsoft and Dozens of Other Companies” (Medium, 2021)
  • PyTorch — “Compromised nightly dependency chain between December 25th and December 30th, 2022” (pytorch.org blog)
  • Wiz — “Malicious PyTorch dependency ‘torchtriton’ on PyPI: everything you need to know”
  • GitGuardian — “Dependency Confusion Attacks and Prevention: Register Your Private Package Names”
  • Microsoft — “3 Ways to Mitigate Risk When Using Private Package Feeds” (whitepaper)
  • Sonatype — “Dependency Hijacking Software Supply Chain Attack Hits More Than 35 Organizations”
  • pypa/pip Issue #9612 ve CVE-2018-20225 (NVD) — --extra-index-url çözümleme davranışı
  • Datadog Security Labs — “MUT-8694: An NPM and PyPI Malicious Campaign Targeting Windows Users” (2024)
  • Cloudsmith — “Typosquatting & Slopsquatting: Protecting Your Software Supply Chain”
AL
AltaySec
AltaySec Red Team
AltaySec Arşiv topluluğuna katkı veren yazar. Türkçe siber güvenlik bilgi tabanını birlikte büyütüyoruz.

Sen de yaz, arşivde yerini al

AltaySec Arşiv'e katkı ver — uzmanlığını Türkçe siber güvenlik literatürüne kat.

Yazar Ol