Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 43

Güvensiz Dinamik Kod Çalıştırma Zafiyetleri

Recommended resources

Bir önceki dersimizde, Node.js olay döngüsünü (event loop) bloke etmenin bir uygulamayı nasıl hizmet dışı bırakabileceğini inceledik. Bu, uygulamanın kullanılabilirliğini hedef alan bir zafiyet sınıfıydı. Bugünkü dersimizde ise, saldırganın amacının sadece hizmeti durdurmak değil, sunucunun veya istemcinin kontrolünü tamamen ele geçirmek olduğu çok daha kritik bir zafiyet kategorisine dalış yapıyoruz: dinamik kod çalıştırma.

Bu dersin hedefi, JavaScript'te eval, new Function gibi fonksiyonların güvensiz kullanımıyla dinamik kod çalıştırma zafiyetlerini tespit etmektir. Bu fonksiyonlar, JavaScript'in esnekliğinin ve dinamizminin bir parçası olsalar da, bir güvenlik denetçisinin gözünde en tehlikeli "sink" (hedef) olarak kabul edilirler. Kullanıcıdan gelen veya dış bir kaynaktan alınan verinin bu fonksiyonlara ulaşması, genellikle Uzaktan Kod Çalıştırma (RCE) veya kalıcı Siteler Arası Komut Dosyası Çalıştırma (XSS) ile sonuçlanır. Bu, bir uygulamanın karşılaşabileceği en yıkıcı zafiyetlerden biridir.

"Şeytani" eval: Dinamik Kod Çalıştırmanın Kaynağı

eval() fonksiyonu, kendisine parametre olarak verilen bir metin (string) parçasını alır ve onu o anki yetki ve kapsam (scope) dahilinde JavaScript kodu olarak yorumlayıp çalıştırır. Bu özellik, geliştiricilere çalışma zamanında dinamik olarak kod üretme ve yürütme imkanı tanır. Ancak bu güç, büyük bir sorumlulukla gelir. Eğer eval() içine giren metnin herhangi bir kısmı saldırgan tarafından kontrol edilebiliyorsa, bu durum saldırgana uygulamanın bağlamında dilediği kodu çalıştırma yetkisi verir.

Bu tehlikeyi daha net anlamak için, konuya giriş yapan aşağıdaki kaynağı inceleyerek başlayalım.

[PDF] 'node-serialize' Remote Code Execution – Web Shell - Exploit DB

Bu Exploit-DB makalesi, eval fonksiyonunun tehlikesini basit ve etkili bir şekilde ortaya koyarak başlıyor.

Lütfen "2.1. eval() fonksiyonu" başlığı altındaki bölümü okuyun. Özellikle, yazarın fonksiyonun tehlikesine dair yaptığı girişi ve bir Node.js kabuğunda eval ile basit bir komutun nasıl çalıştırıldığını gösteren örneği inceleyin. Bu bölüm, zafiyetin temelindeki "neden" sorusunu yanıtlamaktadır.

Gördüğünüz gibi, eval('1+1') masum görünse de, eğer parantez içindeki ifade bir kullanıcıdan geliyorsa, eval("require('child_process').execSync('rm -rf /')") gibi felaket senaryolarına kapı aralar. Bir kod denetçisi olarak ilk göreviniz, bu tür eval çağrılarını bulmaktır. Ancak asıl uzmanlık, verinin bu tehlikeli hedefe nasıl ulaştığını, yani "taint flow" (kirli veri akışı) patikasını ortaya çıkarmaktır.


Sunucu Tarafı RCE: node-serialize Kütüphanesinin Analizi

Teoriyi bir kenara bırakıp, sizin gibi deneyimli bir uzmanın ilgisini çekecek gerçek bir vaka analizi yapalım. Çoğu zaman, dinamik kod çalıştırma zafiyetleri, eval(kullanici_girdisi) gibi bariz bir şekilde karşımıza çıkmaz. Genellikle üçüncü parti kütüphanelerin içinde, belirli koşullar altında tetiklenen gizli mekanizmalar olarak bulunurlar. node-serialize kütüphanesindeki zafiyet, bunun mükemmel bir örneğidir.

Adım 1: Zafiyetin Etkisini Anlamak - eval'dan RCE'ye

eval, JavaScript kodu çalıştırır. Saldırganın nihai hedefi ise genellikle işletim sistemi üzerinde komut çalıştırmaktır. Node.js ortamında bu köprüyü kuran en yaygın modül child_process'tir. eval aracılığıyla bu modülü require edip exec veya execSync fonksiyonlarını çağırmak, tam teşekküllü bir RCE zafiyeti anlamına gelir.

Aşağıdaki kaynak, exec metodunun bu gücünü açıklamaktadır.

[PDF] 'node-serialize' Remote Code Execution – Web Shell - Exploit DB

Aynı makalenin devamında, RCE'nin ikinci kritik bileşeni olan exec metodu tanıtılıyor.

Lütfen "2.4. exec() Metodu" başlığına göz atın. Bu bölümde, <span data-type="resource_reading_textrange" data-resource-subitem-id="55d7c3c3" data-range-start="exec ve eval fonksiyonlarının tehlikeli olduğundan" data-range-end="küçük projeden görelim.">exec metodunun işletim sistemi seviyesinde kod çalıştırma yeteneği vurgulanmakta ve basit bir örnekle gösterilmektedir. Bu, eval ile elde edilen kod çalıştırma yetkisinin neden bu kadar tehlikeli olduğunu bağlamsallaştırır.

Adım 2: Kod Analizi - Şeytan Ayrıntıda Gizlidir

Şimdi en kritik kısma, yani güvenli kod denetimine geliyoruz. node-serialize kütüphanesinin kaynak kodunu incelediğimizde, doğrudan ve koşulsuz bir eval çağrısı görmeyiz. Zafiyet, kütüphanenin deserialize ettiği nesneleri işlerken kullandığı özel bir mantıkta yatmaktadır.

Aşağıdaki bölümü okurken, bir denetçi olarak nasıl bir düşünce süreci izlemeniz gerektiğine odaklanın: "Kullanıcıdan gelen veri bu koda nasıl giriyor ve hangi koşullar altında tehlikeli bir fonksiyona ulaşıyor?"

[PDF] 'node-serialize' Remote Code Execution – Web Shell - Exploit DB

Bu bölüm, zafiyetin kök neden analizinin yapıldığı yerdir. Gerçek bir kod denetimi simülasyonu sunar.

Lütfen "4. Debugging" başlıklı bölümü dikkatle inceleyin. Yazarın, payload'daki _$$ND_FUNC$$_ ifadesinden şüphelenerek kütüphane kodunu nasıl incelemeye başladığına dikkat edin. Özellikle şu kısımlara odaklanın: FUNCFLAG değişkeninin _$$ND_FUNC$$_ olarak tanımlandığı kod bloğu. Zafiyetin kalbi olan if bloğu. Bu blok, kullanıcıdan gelen bir string'in _$$ND_FUNC$$_ ile başlayıp başlamadığını kontrol ediyor. Eğer başlıyorsa, bu özel ifadenin geri kalan kısmını substring ile alıp doğrudan eval fonksiyonuna gönderiyor. Bu, dolaylı ve koşullu bir eval kullanımıdır ve otomatik tarayıcıların gözünden kolayca kaçabilir.

Bu analiz, bir kod denetçisinin değerini ortaya koyar: Sadece "eval kullanılmış mı?" diye sormak yerine, "Kullanıcı kontrolündeki bir veri, hangi dönüşümlerden veya kontrollerden geçerek eval'a ulaşabilir?" sorusunu sormak.

Adım 3: Bulguyu İstismara Dönüştürmek

Zafiyeti bulduktan sonra, nasıl istismar edildiğini anlamak, riskin boyutunu kavramak için önemlidir. Makalenin devamı, bu eval zafiyetini kullanarak hedef sisteme bir "web shell" (web kabuğu) yükleyen bir payload'un nasıl oluşturulduğunu gösterir. Bu payload, _$$ND_FUNC$$_ ön ekini takiben, child_process kullanarak yeni bir HTTP sunucusu başlatan ve gelen isteklerdeki komutları çalıştıran bir JavaScript fonksiyonu içerir.


İstemci Tarafı Kod Çalıştırma: DOM ve eval

eval'in tehlikesi sadece sunucu tarafıyla sınırlı değildir. Tarayıcıda (istemci tarafında) çalıştığında, genellikle DOM tabanlı XSS zafiyetlerine yol açar. Sunucu tarafı kod denetiminden farklı olarak, burada taint kaynağı genellikle URL (location.search), document.referrer veya servisler arası iletişimde kullanılan postMessage gibi DOM API'leri olur.

PwnLabMe'deki bu makale, eval'ın istemci tarafındaki tehlikelerini pratik bir örnekle açıklıyor.

DOM XSS ile Fuzzing - PwnLabMe

Bu kaynak, eval'ın istemci tarafındaki rolünü ve nasıl tespit edilebileceğini gösteriyor.

Lütfen şu bölümlere odaklanın: "Dom Xss Neden Kaynaklanır?" başlığı altında, eval(), document.write veya innerHTML gibi metotların zararlı kullanımının DOM XSS'e nasıl yol açtığını anlatan paragrafı okuyun. "2) Firinge Range / [PostMessage – eval – Dom XSS]" başlığı altındaki pratik örneği inceleyin. Yazarın, Google DevTools kullanarak postMessage ile gelen verinin doğrudan eval fonksiyonuna gittiğini nasıl tespit ettiğini adım adım takip edin. Özellikle, kaynak kod analizi ve debugger ile msg.data'nın içeriğini manipüle etme süreci, manuel analizde kullanacağınız temel teknikleri göstermektedir.

Bu örnek, node-serialize vakasıyla aynı temel deseni paylaşır: Dış bir kaynaktan (postMessage) gelen veri, bir sink'e (eval) ulaşarak kod çalıştırılmasına neden olur.


eval'ın Akrabaları: new Function, setTimeout, setInterval

Kod denetimi yaparken aramanız gereken tek tehlikeli fonksiyon eval değildir.

  • new Function(...): new Function('arg1', 'arg2', '...kod...') şeklinde kullanılır. eval'a çok benzer şekilde, son argüman olarak verilen string'i bir fonksiyon gövdesi olarak derler ve çalıştırır. eval'dan temel farkı, oluşturulan fonksiyonun eval'ın çağrıldığı yerel kapsama (local scope) değil, yalnızca global kapsama erişiminin olmasıdır. Bu onu bir nebze daha "güvenli" yapsa da, RCE veya XSS bağlamında neredeyse aynı derecede tehlikelidir, çünkü hala child_process gibi global modüllere veya document gibi global nesnelere erişebilir.
  • setTimeout(code, delay) ve setInterval(code, delay): Bu fonksiyonların ilk parametre olarak bir string alabilen eski bir kullanımı vardır. setTimeout("alert(1)", 1000) gibi bir kullanım, eval ile eşdeğerdir ve kesinlikle kaçınılması gerekir. Modern ve güvenli kullanım, her zaman bir fonksiyon referansı (callback) vermektir: setTimeout(() => alert(1), 1000).

Tespit ve Önleme Stratejileri

Bir denetçi olarak göreviniz bu zafiyetleri bulmak ve ortadan kaldırılması için doğru yolu göstermektir.

  • Tespit Yöntemleri:

    1. Anahtar Kelime Avı: Kod tabanında eval(, new Function(, setTimeout(, setInterval( gibi anahtar kelimeleri arayarak işe başlayın.
    2. Veri Akış Analizi (Taint Analysis): Bu anahtar kelimeleri bulduktan sonra, bu fonksiyonlara parametre olarak gelen verinin kaynağını izleyin. Veri, bir HTTP isteğinden, bir veritabanı sorgusundan, bir dosyadan veya bir postMessage olayından mı geliyor? Eğer öyleyse, bir zafiyet bulmuş olabilirsiniz. node-serialize örneğindeki gibi, verinin tehlikeli hedefe ulaşmadan önce geçtiği if bloğu gibi mantıksal yolları da analiz etmelisiniz.
    3. Bağımlılık Denetimi: Güvenlik açıkları genellikle projenin kendi kodunda değil, node-serialize gibi bağımlılıklarda gizlenir. package.json dosyasındaki kütüphaneleri, özellikle de veri işleme, serileştirme/deserileştirme veya şablonlama (templating) yapanları şüpheyle inceleyin.
  • Önleme Yöntemleri:

    1. Kural #1: eval ve benzerlerini kullanmaktan kaçının. Modern JavaScript'te, eval gerektiren neredeyse hiçbir meşru kullanım durumu yoktur. Dinamik olarak bir nesnenin özelliğine erişmek için eval('obj.' + propName) yerine obj[propName] kullanılabilir.
    2. Güvenli Alternatifleri Kullanın: Eğer yapılandırılmış veri (JSON gibi) ayrıştırmanız gerekiyorsa, eval yerine JSON.parse() kullanın. JSON.parse() yalnızca geçerli JSON sözdizimini ayrıştırmak için tasarlanmıştır ve asla rastgele JavaScript kodu çalıştırmaz.
    3. Kaçınılmazsa (Kesinlikle Önerilmez): Eğer eval veya new Function kullanımı bir mimari zorunluluksa, fonksiyona giren verinin son derece katı bir beyaz liste (whitelist) filtresinden geçirilmesi ve sadece beklenen, zararsız karakter setlerini içermesi sağlanmalıdır. Ancak bu, kırılgan bir çözümdür ve doğru çözüm her zaman refaktör ederek bu fonksiyonlardan kurtulmaktır.

Özet ve Sonraki Adımlar

Bugünkü dersimizde, JavaScript'in en tehlikeli fonksiyonlarından olan eval ve benzerlerinin hem sunucu hem de istemci tarafında nasıl kritik güvenlik zafiyetlerine yol açabileceğini derinlemesine inceledik.

Anahtar Çıkarımlar:

  • Kullanıcı kontrolündeki verinin eval, new Function, setTimeout(string), setInterval(string) gibi "sink"lere ulaşması, genellikle RCE veya XSS ile sonuçlanan kritik bir zafiyettir.
  • Bu zafiyetler genellikle üçüncü parti kütüphanelerde, node-serialize örneğindeki gibi dolaylı ve koşullu mantıkların arkasında gizlenir.
  • Etkili bir kod denetimi, basit bir anahtar kelime aramasının ötesine geçerek, kirli verinin (taint) kaynaktan hedefe olan akışını manuel olarak takip etmeyi gerektirir.
  • JSON.parse() gibi güvenli alternatifler, eval'ın veri ayrıştırma amacıyla tehlikeli kullanımının yerini almalıdır.

Uygulamanın kontrolünü ele geçirmeye yönelik bu doğrudan saldırılardan sonra, bir sonraki dersimizde JavaScript ortamını daha dolaylı ama bir o kadar da etkili bir şekilde manipüle eden bir tekniğe odaklanacağız. Bir sonraki konumuz, frontend JavaScript kodunda global scope'u ve DOM nesnelerini manipüle eden DOM clobbering zafiyetlerini belirlemek olacak.