İçeriğe atla
0 XP 0
12 dkderinleşmeyle 13 dk50 XP7 etkinlik

İşletim sistemi komutu enjeksiyonu

Bu derste
  • İşletim sistemi komutu enjeksiyonu, uygulamanın kullanıcı girdisini bir kabuk komutuna ekleyip çalıştırmasıyla ikinci bir komutu zincire ekler.
  • Kabuk ayırıcılarını tanır, süreç kaydından tespiti öğrenir, kabuksuz çağrı ve izin listesiyle kapatırsın.

Puan bütün etkinlikleri cevaplayınca hesaba yazılır. Bu dersi tamamlayınca +50 XP alacaksın. Giriş yapmadan ilerlersen puan yalnız bu tarayıcıda durur.

Bazı özellikler arka planda işletim sistemi komutu çalıştırır: adres doğrulama, resim küçültme, dosya sıkıştırma, PDF üretme. Uygulama, kullanıcı girdisini bu komutun metnine ekleyip bir kabuğa (shell) yolluyorsa, girdi artık ikinci bir komuta dönüşebilir. Bu aile, SQL enjeksiyonuyla aynı kök nedeni taşır; tek fark, girdiyi yorumlayan tarafın veritabanı değil işletim sistemi kabuğu olmasıdır.

Etkisi de daha ağırdır: başarılı bir komut enjeksiyonu doğrudan sunucuda çalışır. Bu derste kabuğun girdiyi neden ayrı bir komut sandığını, kör tekniğin bu ailedeki karşılığını, süreç kaydından tespiti ve kabuksuz çağrıyla kalıcı çözümü görürsün.

Kabuk neden ikinci komutu çalıştırır

Altay Kargo'nun kargo takip sayfasında bir adres doğrulama aracı, kullanıcının girdiği adresi doğrudan bir tanı komutuna ekliyor olsun. Uygulama bu dizeyi bir kabuğa yolluyorsa, kabuk yalnız tanı komutunu değil, dizedeki her özel karakteri de yorumlar. Noktalı virgül, boru işareti ve && işareti kabukta komut ayırıcı ya da bağlayıcı anlamına gelir.

Kullanıcı adres alanına 203.0.113.10 sonrasında noktalı virgül ile ikinci bir komut yazarsa, kabuk iki ayrı komut görür: önce tanı komutu çalışır, sonra ikinci komut kendi başına çalışır. Uygulama tek bir komut beklerken saldırgan zincire ikinci bir komut eklemiştir.

Kök neden SQL enjeksiyonuyla aynıdır: veri, komut dizesinin içine metin birleştirmeyle karışmıştır. Farkı yorumlayan taraf değişir; veritabanı motoru yerine işletim sistemi kabuğu çalışır. CWE bu sınıfı CWE-78 olarak kaydeder.

Kabuk; boşluk, tırnak ve özel karakterleri kendi söz dizimine göre okur. Aşağıdaki kısa çizelge, kabuğun bir komutu ötekine bağlamakta kullandığı işaretleri toplar. Bunları tanımak, hem logda enjeksiyonu ayırt etmek hem de girdiden hangi karakterleri kesinlikle dışlaman gerektiğini bilmek için gerekir.

Kabuğun bu davranışı bir hata değil, tasarımdır. Kabuk, insanların tek satırda birden çok komutu zincirlemesi için bu ayırıcıları sunar. Sorun, uygulamanın kullanıcı verisini bu güçlü ortama metin olarak taşımasıdır.

Unix kabuğu ile Windows komut yorumlayıcısı farklı ayırıcılar kullanır, ama ilke ortaktır: özel karakterler komutu böler. Bir de argüman enjeksiyonu vardır (CWE-88): saldırgan yeni bir komut eklemese bile, çalışan komuta fazladan bir bayrak geçirip davranışını değiştirebilir. Bu yüzden yalnız ayırıcıları değil, tire ile başlayan argümanları da denetlemek gerekir.

;      ilk komut biter, ikincisi her hâlde çalışır
|      ilk komutun çıktısı ikinci komuta akar
||     ilk komut başarısızsa ikincisi çalışır
&&     ilk komut başarılıysa ikincisi çalışır
$(...) parantez içindeki komutun çıktısı yerine yazılır
satır sonu   yeni bir komut satırı başlatır
Kabuk komut ayırıcı ve bağlayıcıları. Bir tanesi bile girdiye sızarsa ikinci komut çalışabilir.

Etkinlik 1 / 7

Kabuk komut ayırıcılarını, adres alanına yazılan sıradan metinlerden ayır.

Önce bir seçeneğe, sonra aşağıdaki uygun kutuya bas. İstersen seçeneği kutuya sürükleyip bırak. Yerini değiştirmek için aynı işlemi tekrarla.

0/5 seçenek yerleşti.

Ayırıcıları tanımak yetmez; her birinin komutları nasıl bağladığını da bilmen gerekir, çünkü sağlam bir savunma bu karakterlerin tümünü girdiden dışlar. Boru işareti çıktıyı ikinci komuta aktarır, && ilk komut başarılıysa ikincisini çalıştırır, noktalı virgül ise koşulsuz zincirler. Ters tırnak ve dolar-parantez ise içteki komutu çalıştırıp çıktısını satıra gömer; savunma tam da bu yüzden tek bir karakteri değil, ayırıcıların bütününü girdiden dışlamak zorundadır. Aşağıda her işareti yaptığı işle eşle.

Etkinlik 2 / 7

Her kabuk işaretini yaptığı işle eşle.

Soldan bir tane seç, sağdan eşini seç. Kullanılmış bir seçeneği yeniden seçersen yeni öğeye taşınır.

Kör komut enjeksiyonu ve zamana dayalı kanıt

Uygulama, komutun çıktısını her zaman ekrana basmaz. Adres doğrulama aracı yalnız geçerli ya da geçersiz döndürüyorsa, saldırgan komut çıktısını göremez. Bu durumda SQL enjeksiyonundaki kör mantığın aynısı işler. Saldırgan, yanıtı gözle görülür biçimde değiştiren bir sinyal arar.

En yaygın sinyal zamandır: eklenen ikinci komut bir bekleme çağrısıysa ve yanıt tam o kadar gecikiyorsa, komutun çalıştığı kanıtlanır. Yanıt süresi, çıktı hiç görünmese bile bir bit bilgi taşır. Savunmacı için bunun anlamı nettir: aynı uca gelen ve normalden belirgin daha uzun süren istekler, kör komut enjeksiyonunun izidir ve yanıt süresi de bir tespit ölçütü olarak izlenmelidir.

Zaman tek sinyal değildir. Uygulama hiçbir çıktı vermese ve gecikme ölçülemese bile, saldırgan eklediği komuta dışarıya bir istek yaptırabilir. Kendi izlediği bir sunucuya giden bir DNS ya da HTTP isteği görünürse komut çalışmıştır. Buna bant dışı (out-of-band) teknik denir.

Savunmacı için bu, giden trafiğin de izlenmesi gereken bir kanal olduğu anlamına gelir. Web sunucusunun beklenmedik bir dış adrese istek açması, komut enjeksiyonunun sessiz bir imzasıdır.

Etkinlik 3 / 7

Adres doğrulama aracı yalnız geçerli/geçersiz dönüyor, çıktıyı basmıyor. Saldırgan girdiye beş saniye bekleten bir ikinci komut ekliyor ve yanıt tam beş saniye gecikiyor. Bu ne anlama gelir?

Önce kodu oku, sonra çıktıyı seç.

girdi: 203.0.113.10 ; <beş saniye bekleten komut>
yanıt: "geçerli"  (yaklaşık 5 saniye sonra)

Süreç kaydından tespit

Bu saldırı işletim sistemi düzeyinde iz bırakır. Uygulamanın süreç kaydına bakarsan, tanı komutunun beklenmedik bir çocuk süreçle birlikte çalıştığını görürsün: dosya okuma, kimlik sorgulama ya da ağ indirme komutları web sunucusunun kendi kullanıcı hesabından başlar. Normal bir tanı komutu tek başına çalışır ve birkaç saniyede biter.

Web sunucusu hesabından açılan beklenmedik bir ikinci komut, komut enjeksiyonunun klasik imzasıdır. Linux'ta bu izi denetim altyapısıyla toplayabilirsin: auditd, execve çağrılarını kaydeder ve auditctl ile web sunucusunun çalıştırdığı her yeni süreci izleyen bir kural yazarsın. MITRE ATT&CK bu davranışı T1059 komut ve betik yorumlayıcı tekniği altında ele alır; web sunucusu hesabından çıkan kabuk süreci güçlü bir uyarıdır.

Bu davranışı yakalamak için tek kaynak süreç kaydı değildir. Uç nokta tespit ve yanıt (EDR) araçları, bir web sunucusu sürecinin çocuk olarak kabuk açmasını yüksek öncelikli bir olay sayar. MITRE ATT&CK bu adımları da haritalar: dışarıdan gelen bir uygulama açığının sömürülmesi ilk erişim tekniği T1190, saldırganın kalıcılık için bıraktığı web kabuğu ise T1505.003 altında yer alır. Süreç kaydı, EDR uyarısı ve ağ trafiği bir araya gelince, komut enjeksiyonu tek bir loga bağlı kalmadan görünür olur.

# Web sunucusunun çalıştırdığı her yeni süreci kaydet (Linux, 64 bit)
auditctl -a always,exit -F arch=b64 -S execve -F uid=33 -k web-komut
# uid 33 = www-data. Kayıtlar sonra sorgulanır:
ausearch -k web-komut
auditd kuralı, web sunucusu hesabından açılan her komutu yakalar. Beklenmedik çocuk süreç burada görünür.

Etkinlik 4 / 7

Sunucunun süreç kaydında komut enjeksiyonuna ait satırları işaretle.

Şüpheli satırlara tıkla. Birden fazla olabilir.

Kabuksuz çağrı ile kapatmak

Doğru çözüm kabuğu devre dışı bırakmaktır. Çoğu dil, komutu kabuğa yorumlatmadan doğrudan çalıştıran bir API sunar; bu API'ye komut adı ve argümanlar ayrı bir dizi olarak verilir. Kabuk hiç araya girmediği için noktalı virgül ya da boru işareti sıradan bir karakter olarak kalır, komut ayırıcı anlamı taşımaz.

Node.js'te exec komutu bir kabuk açar, execFile ise açmaz; Node belgeleri exec için açıkça uyarır: temizlenmemiş kullanıcı girdisini bu fonksiyona hiç verme. Python'da subprocess modülü varsayılan olarak kabuk açmaz; shell=False varsayılandır ve argümanlar liste olarak geçer. shell=True dersen kaçış sorumluluğu sana geçer ve enjeksiyon kapısı yeniden açılır.

Kabuğu kapatmanın dildeki karşılığını bilmek işini kolaylaştırır. Python'da subprocess.run çağrısına komutu bir liste olarak verirsin ve shell parametresine dokunmazsın; varsayılan kapalıdır. Zorunlu bir nedenle kabuk açman gerekirse, her argümanı shlex.quote ile tırnaklamak kaçış sorumluluğunu bir ölçüde üstlenir, ama liste biçimi her zaman daha güvenlidir.

Node.js tarafında execFile ve spawn, kabuk seçeneği kapalıyken argüman dizisi bekler. Kural tek cümleyle özetlenir: komutu ve argümanlarını ayrı ayrı ver, ikisini tek bir metinde birleştirme.

// Güvensiz: kabuk üzerinden, metin birleştirme
exec("ping -c 1 " + adres)

// Güvenli: kabuksuz çağrı, argüman dizisi
execFile("ping", ["-c", "1", adres])
execFile kabuğu hiç başlatmaz; adres içindeki noktalı virgül ping'e tek bir argüman olarak gider.

Etkinlik 5 / 7

Kabuk üzerinden çalışan ve enjeksiyona açık satırı bul.

Zafiyetli satıra tıkla.

İzin listesi ve kütüphaneyle kaçınma

Argüman ayrımı tek başına yetmeyebilir; bazı programlar kendi içinde argümanları yine bir yorumlayıcıya geçirir. Bu yüzden ikinci katman bir izin listesidir. Adres alanı yalnız IPv4 ya da alan adı biçimine uymalı, komut ayırıcı karakter hiç kabul edilmemelidir.

Uygulama, ihtiyacı yoksa dış komut çalıştırmaktan tümüyle kaçınır; adres çözümleme ve dosya sıkıştırma gibi birçok işlem artık kütüphane çağrısıyla, hiç kabuk açmadan yapılabilir. Kütüphane çağrısı ayrıca hatayı yapılandırılmış bir nesne olarak döner; ham kabuk çıktısını ayrıştırmaya da gerek kalmaz.

İzin listesini tasarlarken kabul edilen biçimi kesin tanımla. Bir adres alanı için bu, geçerli bir IPv4 adresi ya da alan adı deseninden ibarettir; noktalı virgül, boşluk, tırnak ve boru işareti bu desende hiç yer almaz. Girdiyi doğrulamadan önce tek biçime indir, sonra desene uydur.

Deseni olabildiğince dar tut; her fazladan izin verilen karakter, yarın bir kaçış yoluna dönüşebilir. En sağlam çözüm ise dış komuttan tümüyle kaçınmaktır: çoğu iş, kabuk açmayan bir kütüphane çağrısıyla yapılabilir ve o zaman enjeksiyon için bir yorumlayıcı hiç kalmaz. Ortadan kaldırılan bir yorumlayıcı en güvenilir savunmadır; olmayan bir kabuk hiçbir girdiyle kandırılamaz.

Etkinlik 6 / 7

adres.example hedefine, kabuk açmadan doğrudan çalıştırılabilen ping komutunu (tek paket) yaz.

ogrenci@lab

Aynı kök neden, resim işleme ya da PDF üretme gibi harici araç çağıran her özellikte tekrarlanır. Altay Kargo'nun fatura motoru, kullanıcı adını dosya adına ekleyip bir dönüştürme aracını kabuk üzerinden çağırıyorsa, dosya adı alanı da aynı zincire açık bir kapı olur. Yeni bir özellik eklerken kendine hep aynı soruyu sor: bu girdi, bir kabuğa ya da başka bir yorumlayıcıya metin olarak mı gidiyor?

Aynı disiplin kod incelemesinde de işine yarar. Bir kod tabanında dış komut çağıran her satırı ara; kabuk açan bir fonksiyona metin birleştirmeyle veri gidiyorsa, orada bir aday açık vardır. Arama terimlerin dile göre değişir ama örüntü aynıdır: komut çalıştıran bir çağrı, artı kullanıcıdan gelen bir değişken, artı metin birleştirme. Kod incelemesi, sızma testinden önce açığı kapatmanın en ucuz ve en erken yoludur.

Etkinlik 7 / 7

Fatura motoru, dosya adını kullanıcı girdisinden kurup bir dönüştürme aracını kabuk üzerinden çağırıyor. En kalıcı düzeltme hangisidir?

Derinleş:Windows'ta toplu dosya tuzağı

Kabuksuz çağrı güçlü bir savunmadır ama işletim sistemine göre ince ayrıntılar taşır. Node.js belgeleri, Windows'ta toplu iş dosyalarının (.bat, .cmd) tek başına çalışamadığını, ancak bir terminalle çağrılabildiğini söyler. 2024'te açıklanan CVE-2024-27980, tam bu noktadan doğdu: spawn ve spawnSync ile bir toplu dosya çalıştırıldığında, kabuk seçeneği kapalı olsa bile komut satırı argümanları kabuk kurallarıyla ayrıştırıldı ve kötü niyetli bir argüman komut çalıştırabildi.

Python belgeleri de aynı uyarıyı verir: Windows'ta toplu dosyalar, argümanlardan bağımsız olarak sistem kabuğunda başlatılabilir. Sonuç şudur: kabuksuz çağrı ilkesi doğru olsa da, platforma özgü kaçış kapılarını da bilmen gerekir. Güncel çalışma zamanı sürümünü izlemek bu tür açıkların yamalarını yakalar.

Bu derste bir hata mı gördün? Bildir