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
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.jsonveyarequirements.txtyeterlidir. - Docker imaj katmanları: Public imajlardaki
COPY package.jsonveyapip 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 —
requestsyerinerequets,coloramayerinecolorama-py,seleniumyerineselemium. Geliştiricinin parmak hatası veya kopyala-yapıştır yanlışı yeterlidir. Ekim 2024’te belgelenen MUT-8694 kampanyası, PyPI’dakilarpexodusgibi 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:
- 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
grepve GitHub dork’ları). - 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.
- Çö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.
- CI ayrıcalık denetimi: Runner’ların kimlik bilgilerini minimize edin;
--ignore-scriptsve--require-hashesuygulanı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”
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