1 puan yazan GN⁺ 6 시간 전 | 1 yorum | WhatsApp'ta paylaş
  • Git’teki --, genel amaçlı bir seçenek sonlandırıcı değil, revizyonlarla yol belirtimlerini ayırdığı için, güvenilmeyen bir revizyonu güvenli biçimde geçirmek adına Git 2.24.0’dan beri desteklenen --end-of-options gerekir
  • git log --end-of-options "$rev" -- "$path" içinde baştaki işaret seçeneklerle revizyonları, sondaki -- ise revizyonlarla yolları ayırır; birbirlerinin yerine kullanılamazlar
  • Shell kullanmadan argv dizisini doğrudan çalıştırsanız bile, tireyle başlayan girdi --upload-pack, core.sshCommand, ProxyCommand gibi seçenekler olarak yorumlanırsa CWE-88 argüman enjeksiyonu oluşabilir
  • İncelenen 19 paket yöneticisinin 17’si varsayılan ya da tek yöntem olarak Git ikilisini çalıştırıyor; ancak --end-of-options kullanan araç yalnızca Go’nun cmd/go aracıydı
  • Temel çözüm, alt komuta göre asgari Git sürümünü 2.24.0·2.30.0·2.43.1’e yükseltmeyi gerektiren bir uyumluluk maliyeti getirir; Git kütüphaneleri ise argüman enjeksiyonu sınırını ortadan kaldırırken upstream’in checkout güvenliği düzeltmelerini doğrudan izlemek zorundadır

-- ile --end-of-options arasındaki fark

  • Genel Unix araçlarında --, seçenek yorumlamanın bittiğini belirtir; bu yüzden rm -- -f, -f değerini zorla silme seçeneği değil dosya adı olarak ele alır
  • Git ise erken dönemlerden beri -- işaretini revizyonlarla yol belirtimleri (pathspec) arasında ayırıcı olarak kullanır
    • git log foo, foo adlı bir branch’i mi yoksa dosyayı mı kastettiği konusunda belirsizdir
    • git log main -- README.md, main üzerindeki commit’lerden README.md dosyasına dokunanları ifade eder
  • Bu tasarım nedeniyle revizyon konumunda seçenek sonlandırma işareti yoktu; git log "$rev" içinde $rev tireyle başlıyorsa Git bunu seçenek olarak yorumlar
  • --end-of-optionsı getiren commit, mevcut -- zaten revizyonlarla yol belirtimlerini ayırdığı için seçeneklerle revizyonları ayıracak ayrı bir işarete ihtiyaç olduğunu belirtir
  • --end-of-options, gitcli(7) içinde belgelendi ve Git 2.24.0’ın yayımlandığı Kasım 2019’da eklendi

Komuta göre doğru kullanım

  • git clone -- "$url" komutunda, clone POSIX geleneğini izlediği için URL’nin önündeki -- seçenek yorumlamayı sonlandırır
  • git checkout "$ref" -- içindeki sondaki --, $ref değerini dosya adı değil revizyon olarak işaretler; ancak $ref değerinin önce seçenek olarak yorumlanmasını engelleyemez
  • Güvenilmeyen revizyonu ve yolu birlikte güvenli biçimde geçirmek için git log --end-of-options "$rev" -- "$path" gibi iki işareti de kullanmak gerekir
    • --end-of-options, seçeneklerle revizyonları ayırır
    • --, revizyonlarla yolları ayırır
  • İki işaretin birbirinin yerine kullanılabileceğini varsaymak, tireyle başlayan girdileri engelleyemez

Alt komutlara göre farklı destek zamanları

  • --end-of-options desteği tüm Git komutlarına tek seferde uygulanmadı; alt komut bazında eklendi
  • git rev-parse, kendi argüman ayrıştırıcısını kullandığı için ilk eklenişinden 1 yıl sonra Git 2.30.0’da desteklemeye başladı
  • git checkout ve git reset, -- işaretini kendileri yorumlar; ilk uygulama --end-of-optionsı argüman listesinde bıraktığından bu komutlar onu reddetti
    • Bu sorun, Git 2.43.1’in yayımlandığı Şubat 2024’te çözüldü

Shell olmadan da oluşan argüman enjeksiyonu

  • Git, Mercurial ve SSH’te çağıranın belirlediği komutu çalıştıran seçenekler resmi özellik olarak bulunur
    • git clone --upload-pack=<cmd>, sunucu tarafındaki ikiliyi belirtir
    • Tüm Git çağrılarındaki -c core.sshCommand=<cmd>, bağlantı komutunu değiştirir
    • Mercurial’daki --config=alias.<subcmd>=!<shell>, çalıştırılacak alt komutu isteğe bağlı bir shell betiğiyle yeniden tanımlar
    • SSH’teki -oProxyCommand=<cmd>, proxy komutunu belirtir
  • Bir sarmalayıcı program güvenilmeyen bir dizgeyi argüman listesine koyarsa bu özellikler saldırı aracına dönüşebilir
  • Bu hata türü CWE-88 argüman enjeksiyonu (argument injection) kapsamına girer ve shell komut enjeksiyonundan farklıdır
    • Program system() yerine argv dizisi ve exec kullansa da oluşur
    • Dizi bozulmadan Git’e aktarılır; ancak Git, tireyle başlayan argümanı seçenek olarak yorumlar
  • docker builddeki CVE-2019-13139, Go’nun os/exec ve argv dizisini kullanıp shell’den geçmeyen bir örnektir
    • Git context URL’sindeki #ref:dir parçası git fetch origin <ref> komutuna aktarılırken <ref>, --upload-pack=<cmd> olarak yorumlandı

Birden fazla sürüm kontrol sisteminde tekrarlanan açıklar

  • Ağustos 2017’de aynı gün dört sürüm kontrol sisteminde aynı kalıp açıklandı
  • Dört sistemin tamamı URL’deki host adını SSH argümanı olarak aktarıyordu; -oProxyCommand= ile başlayan host adı SSH seçeneği olarak işlendi
  • Phabricator olay sonrası analizine göre, o sırada aktif olarak bakımı yapılan üç araçtan yalnızca Subversion host adının önüne -- ekledi
    • Git ve Mercurial, tüm SSH uygulamalarının -- desteklememesi gibi nedenlerle host adı biçimini doğruladı
  • -- eksik olan kod da argüman tireyle başlayana kadar normal görünür ve çalışır; bu yüzden bu mekanizma temelde güvenli değildir

Paket yöneticilerinin maruz kaldığı yollar

  • Paket yöneticileri manifest, lock dosyası ve geçişli bağımlılık metadata’sından Git URL’si ya da ref alıp alt süreçlere aktarır
    • Gemfile’da gem 'foo', git: '...'
    • package.json içinde github:user/repo#ref
    • pyproject.toml, Cargo.toml, mix.exs, Package.swift, pubspec.yaml, conanfile.py, go.mod içindeki eşdeğer ayarlar
  • Temmuz 2026’daki HEAD baz alınarak incelenen 19 paket yöneticisinin 17’si, Git ikilisini varsayılan ya da tek yol olarak çalıştırıyor
  • Kalan ikisi varsayılan olarak kütüphane kullanıyor
  • Nix, yerel depoyu okurken libgit2 kullanır; ancak libgit2 git-credential yardımcılarını desteklemediği için fetch için Git süreci çalıştırır
  • İncelenenler Bundler, Cargo, CocoaPods, Composer, Conan, Go, Helm, Homebrew, Mix, Nix, npm, pip, pnpm, Poetry, Pub, SwiftPM, uv, vcpkg ve Yarn’dır

Paket yöneticilerinde doğrulanan CVE’ler

Gerçek savunma durumu ve Go’nun düzeltmesi

  • Git süreci çalıştıran 17 paket yöneticisi içinde --end-of-options kullanan araç yalnızca Go’nun cmd/go aracıydı
  • Go, Haziran 2019’da genel bir savunma güçlendirmesi olarak depo URL’sinin önüne -- ekledi
  • Ocak 2026’da yalnızca -- kullanmanın yeterli olmadığı ortaya çıktı ve CVE-2025-68119 düzeltmesi kapsamında --end-of-options genel olarak eklendi
  • Aynı düzeltme HGPLAIN=+strictflags ayarını da içeriyordu
    • Bu ayar, Mercurial 4.4.2’nin yayımlandığı 2017’den beri Mercurial’ın ilk seçenek yorumlamasını sınırlar
  • Go düzeltme commit’i, aynı sorunun yeniden eklenmesini zorlaştırmak için daha yapısal bir değişiklik gerekebileceğini, ancak şimdilik mevcut sorunu çözdüğünü belirtir

Savunmaların çoğu açıklar duyurulduktan sonra eklendi

  • Kalan paket yöneticileri argüman listesini korusalar bile çoğunlukla -- ya da girdide baştaki tireyi reddetme yöntemini kullanır
  • Bundler’ın git clone URL’si önündeki --, CVE-2021-43809 yamasıyla eklendi
  • cocoapods-downloader’ın baştaki tireyi reddetmesi, CVE-2022-21223’ün açıklanma dönemiyle çakışan Mart 2022’deki on gün içinde üç commit ile uygulandı
  • Poetry’nin savunması Eylül 2021’de eklendi; bir yıl sonra CVE atandı ve altı ay sonra dulwich’e geçildi
  • vcpkg, istisnai olarak Git registry desteğinin yazıldığı ilk günden beri -- kullandı

Asgari Git sürümünün yarattığı uyumluluk kısıtları

  • Composer’ın CVE-2022-24828 duyurusu, --end-of-optionsı doğru düzeltme olarak işaret eder; ancak daha eski Git sürümlerini de desteklemek gerektiği için tireyle başlayan branch adlarını reddetme yolunu seçti
  • vcpkg’nin Git entegrasyonu asgari sürümü Git 2.7.4 olarak belirtir; Homebrew’ün Linux için HOMEBREW_MINIMUM_GIT_VERSION değeri ise 2018’de belirlenen 2.7.0’dır
  • Git 2.14.3 sağlayan Amazon Linux 2’nin Haziran 2026’da ömrünü tamamlamasıyla, bu alt sınırların takip ettiği dağıtımlar ancak şimdi destek kapsamından çıkıyor
  • Ubuntu’nun uzun süreli destek durumu da toplu geçişi zorlaştırıyor
    • Ubuntu 18.04, Git 2.17.0 sağlar ve 2028’e kadar uzatılmış destek kapsamındadır
    • Ubuntu 20.04, Git 2.25.1 sağlar ve 2030’a kadar uzatılmış destek kapsamındadır
    • Git 2.25.1, git fetch için --end-of-optionsı kabul eder ancak git rev-parse içinde reddeder
  • --end-of-optionsa dayanmak için çoğu alt komutta asgari sürüm olarak Git 2.24.0, rev-parse için 2.30.0, checkout ve reset için 2.43.1 istemek gerekir
  • Asgari sürümü yükseltmek, dağıtımlarla gelen eski Git’i kullanan kullanıcıları destekleyememek anlamına gelir

Süreç çalıştırmak yerine Git kütüphanesi kullanmak

  • libgit2, gitoxide, go-git, JGit ve dulwich, clone ve fetch için gereken Git aktarım protokollerini süreç içinde uygular
  • Ayrı bir argv sınırı olmadığı için argüman listesine enjekte edilecek hedefin kendisi yoktur
  • Jujutsu, Git entegrasyonu için gitoxide kullanır ve argüman enjeksiyonu türünde kamuya açık bir CVE’si yoktur
    • Bugüne kadarki iki duyuru, path traversal ve kütüphaneden devralınan SHA-1 çakışma kontrolü eksikliğidir
  • go-git’in CVE-2025-21613 açığı file:// aktarımıyla sınırlıdır
    • Bu yol, go-git içinde Git ikilisinin çalıştırıldığı tek kod yoludur
  • Kendi Git uygulamanızı dahil etmek, upstream Git’in yayımladığı tüm checkout güvenliği düzeltmelerini izlemeyi gerektirir; libgit2 ve JGit’te bununla ilgili düzeltmelerin tekrarlandığı olmuştur
  • Bu maliyet gerçektir; ancak her çağrı noktasında kalıcı olarak argüman kontrolünü hatırlamak yerine mesele, somut upstream yama akışını uygulamaya dönüşür

Homebrew’e önerilen değişiklik kapsamı

  • Homebrew PR’ı, asgari Git sürümünü 2.30.0’a yükseltir ve şu konumlara --end-of-options ekler
    • clone, remote set-url, ls-remote içinde URL’nin önüne
    • rev-parse içinde ref’in önüne
  • checkout ve reset çağrıları değiştirilmez
    • Bu iki komutu da korumak için Şubat 2024’te yayımlanan Git 2.43.1 gerekir
    • Bu sürüm, şu anda desteklenen birçok dağıtımın sağladığı Git’ten daha yenidir

1 yorum

 
GN⁺ 6 시간 전
Lobste.rs görüşleri
  • Son zamanlarda giderek jj’ye kapılıyorum. Git’in bu dağınıklığıyla kıyaslayınca insanın içi rahatlıyor; hâlâ öğrenme aşamasındayım ama niyeti iyi yansıtıyor ve istediğiniz çalışma biçimini de kolayca anlamanızı sağlıyor
    Özellikle bir jj komutunda öğrendiğiniz bilgi diğer komutlara da doğal biçimde uygulanıyor. git log belgeleri, çıktı seçeneklerine göre bayrakların patlamasının yanı sıra commit aralığı sözdizimi olan “özel gösterim” ve ek filtre bayraklarıyla da iç içe geçmiş durumda; diğer Git komutlarına aktarılan bilgi de az
    Buna karşılık jj log belgeleri, tek sayfada bile boş yer kalacak kadar sade. Git’in ıvır zıvır türü karmaşıklığını revizyon kümeleri, dosya kümeleri ve çıktı şablonu DSL’i olmak üzere üç şeyle değiştiriyor; bunlar jj genelinde tutarlı biçimde kullanıldığı için çok daha basit, birleştirilebilir ve sezgisel. Git’in 20 yıldır fazlalık biriktirdiğini hesaba katmak gerekir ama jj bunu önlemek için çok daha elverişli görünüyor
  • Komutlarda kullanıcı tarafından sağlanan argümanlardan önce genelde -- gerektiğini biliyorum ama burada bir de kendine özgü dönüşüm kuralları istemek, gereksiz derecede tehlikeli bir tuzak oluyor
  • Bu, aracı aşırı karmaşık hâle getirmenin dürüst bir sonucu. Git yeni ortaya çıkmış bir Bashō gibi görünüyor
  • Başta dosya argümanlarındaki belirsizliği gidermek için neden -- seçildiğini merak ediyorum. Dosyaları açık bir argüman olarak alan basit bir yaklaşım yerine bunun seçilmesinde görünmeyen bir tasarım ödünleşimi olup olmadığını sorguluyorum
    • Zaten yerleşik olan UNIX seçenek ayrıştırma geleneğini tamamen başka bir amaç için kullanmak, beklendiği gibi aptalca ve son derece Git’e özgü bir seçim
  • UNIX’in her şey metindir felsefesi yine sorun çıkarıyor. Komut satırı aslında yapılandırılmış veriye kusursuz bir örnek ama bu çukurdan çıkmak için gereken ortak eşgüdüm maliyeti fazla büyük görünüyor
    1990’lardaki Tcl ve Scheme tartışmalarına dönüp bakınca, bu durumda “daha kötü olan daha iyidir” yaklaşımı belki de doğruydu. JSON ve XML dâhil S-ifadesi ailesi UNIX ekosisteminde neredeyse hiç yer edinemezken, Tcl dizgelerin içine alt dilleri katmanlandıran nispeten ilkeli bir yaklaşıma sahipti