Merhaba! Önceki dersimizde, Java'nın ClassLoader mekanizmasının nasıl manipüle edilebileceğini ve Spring4Shell gibi zafiyetlerde bunun nasıl bir rol oynadığını derinlemesine inceledik. Bir uygulamanın temel davranışını, mevcut meşru bileşenleri kötüye kullanarak (örneğin loglama mekanizması) nasıl değiştirebileceğimizi gördük. Bu, çalışma zamanında kod akışını kontrol altına almanın sofistike bir yoluydu.
Bu dersimizde, Java ekosisteminde uzaktan kod çalıştırmaya (RCE) olanak tanıyan bir başka güçlü, ancak genellikle daha doğrudan bir saldırı vektörünü analiz edeceğiz. Bu dersin amacı, Java ekosisteminde Spring Expression Language (SpEL) ve OGNL enjeksiyonu desenlerini derinlemesine analiz etmektir. Bu ifade dilleri (Expression Languages - EL), geliştiricilere dinamik veri bağlama, konfigürasyon ve doğrulama gibi konularda inanılmaz bir esneklik sunar. Ancak bu esneklik, güvenilmeyen girdilerle birleştiğinde, uygulamanın tam kontrolünü ele geçirmeye olanak tanıyan kritik güvenlik açıklarına kapı aralar.
İfade Dilleri (Expression Languages): Güç ve Tehlike
İfade dilleri, tam teşekküllü programlama dilleri değildir. Bunlar, çalışma zamanında bir nesne grafiğini (object graph) sorgulamak, gezinmek ve manipüle etmek için tasarlanmış mini dillerdir. Bir string ifade içinde user.getAddress().getCity() gibi basit bir özellik erişiminden, metot çağırmaya, mantıksal operasyonlar yapmaya ve hatta yeni nesneler oluşturmaya kadar geniş bir yelpazede işlem yapabilirler.
En önemli özellikleri, bu string ifadelerin çalışma zamanında yorumlanmasıdır (interpreted). Geliştirici, bir string'i ifade motoruna verir ve motor bu string'i "çalıştırarak" bir sonuç üretir. Güvenlik açısından kritik nokta şudur: Eğer bu yorumlanan string, bir saldırgan tarafından kontrol edilebiliyorsa, saldırgan aslında uygulamanın içinde kendi kod parçacıklarını çalıştırmış olur.
Spring Expression Language (SpEL) Enjeksiyonu
Spring Framework, kendi güçlü ifade dili olan SpEL'i içerir. Genellikle Thymeleaf gibi şablon motorlarında (#{...}), konfigürasyonlarda (@Value("${...}")) veya güvenlik kurallarında (@PreAuthorize("hasRole('ADMIN')")) karşımıza çıkar.
SpEL'in gücü, sadece nesne özelliklerine erişmekle sınırlı değildir. En tehlikeli özelliği, T() operatörü aracılığıyla statik sınıflara ve metotlarına erişim sağlamasıdır. Bu özellik, bir saldırganın, ifadenin bağlamından (context) kaçarak Java'nın çekirdek kütüphanelerindeki herhangi bir sınıfa ulaşmasına olanak tanır. Bu da RCE için klasik bir yol açar.
En bilinen SpEL RCE payload'u şöyledir:T(java.lang.Runtime).getRuntime().exec('whoami')
Bu ifadenin analizi:
T(java.lang.Runtime):T()operatörü,java.lang.RuntimesınıfınınClassnesnesine erişim sağlar. Bu, statik metotları çağırmanın kapısını aralar..getRuntime():Runtimesınıfının singleton örneğini döndüren statik metot..exec('whoami'): Elde edilenRuntimenesnesi üzerinde işletim sistemi komutu çalıştıran metot.
SpEL Enjeksiyonunu Kodda Nasıl Tespit Ederiz?
Kod denetimi sırasında SpEL enjeksiyonu ararken temel desen, güvenilmeyen bir girdinin doğrudan bir SpEL yorumlayıcısına beslenmesidir. Gözetlemeniz gereken anahtar sınıflar ve metotlar şunlardır:
SpelExpressionParser: Bu sınıf, string bir ifadeyi ayrıştırarak birExpressionnesnesi oluşturur.Expression.getValue(): Bu metot, ayrıştırılmış ifadeyi belirli bir bağlamda (context) çalıştırır ve sonucu döndürür.
İşte tipik bir zafiyetli kod parçası:
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
public class SpelVulnerableService {
public String executeQuery(String userInput) {
ExpressionParser parser = new SpelExpressionParser();
// DİKKAT: Kullanıcı girdisi doğrudan ifadeye dahil ediliyor!
Expression exp = parser.parseExpression(userInput);
// İfade, varsayılan (ve tehlikeli) bağlamda çalıştırılıyor.
return exp.getValue(String.class);
}
}
Bu örnekte, userInput değişkeni doğrudan parseExpression metoduna veriliyor. Saldırgan, yukarıda bahsettiğimiz RCE payload'unu bu userInput olarak gönderdiğinde, sunucuda komut çalıştırabilir.
Savunma Stratejileri ve Hatalı Uygulamalar
En sağlam savunma, kullanıcı girdisini asla bir SpEL ifadesinin parçası yapmamaktır. Ancak bu her zaman mümkün olmayabilir. Spring bu durum için daha kısıtlı bir çalıştırma ortamı sunar: SimpleEvaluationContext.
StandardEvaluationContext: Varsayılan bağlamdır.T()operatörü, yeni nesne (new) oluşturma ve Spring bean'lerine (@...) referans gibi tüm güçlü özellikleri destekler. Bu, onu tehlikeli hale getirir.SimpleEvaluationContext: Sadece veri bağlama senaryoları için tasarlanmış, "sandbox" benzeri kısıtlı bir bağlamdır. Bu tehlikeli operatörleri kasıtlı olarak devre dışı bırakır.
Güvenli bir implementasyon şöyle görünürdü:
import org.springframework.expression.spel.support.SimpleEvaluationContext;
// ...
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();
String result = exp.getValue(context, String.class);
Ancak, bazen geliştiriciler bu güvenli yolu seçmek yerine, tehlikeli girdileri bir kara liste (blacklist) ile engellemeye çalışırlar. Aşağıdaki görsel, böyle bir hatalı savunma girişimini gösteriyor.

Bu yaklaşım neden zayıftır?
- Atlatılabilirlik: Saldırganlar, bu kalıpları atlatmak için sayısız yol bulabilirler. Örneğin, string birleştirme (
'java.l'+'ang.Runtime'), farklı sınıflar kullanma (java.lang.ProcessBuilder) veya reflection gibi teknikler bu kara listeyi etkisiz kılabilir. - Bakım Zorluğu: Her yeni atlatma tekniği için kara listeyi güncellemek sürdürülebilir bir çözüm değildir.
Kod denetçisi olarak göreviniz sadece parseExpression çağrısını bulmak değil, aynı zamanda uygulanan savunma mekanizmasının (SimpleEvaluationContext kullanımı gibi) gerçekten etkili olup olmadığını değerlendirmektir. Kara liste tabanlı bir savunma gördüğünüzde, bu her zaman bir şüphe işareti olmalıdır.
OGNL (Object-Graph Navigation Language) Enjeksiyonu
OGNL, SpEL'e kavramsal olarak çok benzeyen bir başka ifade dilidir ve en çok Apache Struts2 framework'ü ile anılır. Struts2, OGNL'i web katmanı (JSP'ler) ile backend action sınıfları arasında veri bağlamak için yoğun bir şekilde kullanır.
Tarihteki en meşhur OGNL zafiyetlerinden biri olan CVE-2017-5638, bu enjeksiyonların ne kadar dolaylı ve beklenmedik yollardan ortaya çıkabileceğini gösteren mükemmel bir örnektir. Zafiyet, bariz bir Ognl.getValue(untrusted) çağrısından kaynaklanmıyordu. Bunun yerine, bir hata işleme mekanizmasının yan etkisi olarak tetikleniyordu.
Aşağıdaki kaynağı inceleyerek bu saldırı zincirini adım adım takip edelim.
Apache Struts2 Zafiyeti | Netsparker Web Uygulaması Güvenlik ...
Bu makale, CVE-2017-5638 zafiyetinin kök nedenini adım adım açıklıyor. Saldırının, framework'ün hata işleme mantığındaki bir OGNL yorumlamasından nasıl kaynaklandığını anlamak için bu bölümleri okuyacağız.
Makaleye göz gezdirin ve "Zafiyet İncelemesi" bölümünü okuyun. Bu bölümde, saldırının geçersiz bir Content-Type başlığı gönderilerek başlatıldığı açıklanıyor. parse metodu hata verdiğinde, hatayı işlemek için buildErrorMessage çağrılıyor. buildErrorMessage içindeki LocalizedTextUtil.findText metodunun, gelen hata mesajını bir OGNL ifadesi olarak yorumladığına dikkat edin. İşte bu nokta, saldırganın kontrolündeki verinin tehlikeli bir sink'e (hedefe) ulaştığı yerdir.
Bu vaka, manuel kod denetiminin neden bu kadar önemli olduğunu gösterir. Otomatik bir araç, LocalizedTextUtil.findText metodunu tehlikeli bir sink olarak işaretlemeyebilir. Ancak bir denetçi, kullanıcıdan gelen verinin (HTTP başlığı) bu metoda nasıl ulaştığını ve bu metodun girdisini yorumladığını takip ederek zafiyeti ortaya çıkarabilir.
OGNL RCE Payload'u ve Koruma Mekanizmalarını Atlama
SpEL'den farklı olarak, modern Struts2 versiyonları OGNL'in tehlikeli özelliklerini kısıtlayan güvenlik mekanizmaları içerir (SecurityMemberAccess gibi). Bu nedenle, OGNL RCE payload'ları genellikle iki aşamalıdır:
- Önce güvenlik mekanizmasını devre dışı bırak.
- Sonra asıl komutu çalıştır.
Aşağıdaki kaynakta yer alan payload, bu süreci detaylı bir şekilde göstermektedir.
Apache Struts2 Zafiyeti | Netsparker Web Uygulaması Güvenlik ...
Bu bölümde, pratik bir OGNL RCE payload'unun yapısını ve Struts2'nin güvenlik mekanizmalarını nasıl etkisiz hale getirdiğini göreceksiniz.
Zafiyetin Sömürülmesi başlığı altında, önce header enjeksiyonu ile zafiyetin varlığının nasıl doğrulanabileceğine bakın. Ardından, asıl RCE'yi gerçekleştiren uzun Komut Enjeksiyonu payload'unu inceleyin. Bu payload'un içindeki getExcludedPackageNames().clear() ve getExcludedClasses().clear() gibi ifadelere dikkat edin. Bu ifadeler, OGNL'in güvenlik sanal alanını (sandbox) devre dışı bırakmak için kullanılır. Güvenlik mekanizması kaldırıldıktan sonra, payload new java.lang.ProcessBuilder(...) kullanarak komutu çalıştırır ve çıktısını HTTP yanıtına yazar.
Aşağıdaki görsel, bu karmaşık payload'u içeren gerçek bir HTTP isteğini göstermektedir. Kod denetiminde bulduğunuz bir zafiyetin, pratikte nasıl bir istismar aracına dönüştüğünü görmek önemlidir.

OGNL İçin Kod Denetimi Stratejisi
Struts2 gibi büyük bir framework'te OGNL enjeksiyonu ararken, sadece belirli metotları aramak yeterli olmayabilir. Odaklanmanız gerekenler:
- Veri Akışı: Kullanıcıdan gelen verilerin (HTTP başlıkları, parametreler, vb.) nereye gittiğini takip edin.
- Dolaylı Sink'ler: Hata işleme, loglama veya yerelleştirme (localization) gibi görünüşte zararsız mekanizmaların, aldıkları string'leri yorumlayıp yorumlamadığını sorgulayın.
LocalizedTextUtil.findText, bu tür gizli ve tehlikeli hedeflere mükemmel bir örnektir.
Özet ve Sonraki Adımlar
Bu dersimizde, Java ekosisteminin iki güçlü ifade dili olan SpEL ve OGNL'in, güvenilmeyen verileri işlediklerinde nasıl kritik RCE zafiyetlerine yol açtığını analiz ettik.
Anahtar Çıkarımlar:
- İfade Dilleri Tehlikelidir: Bir string'i çalışma zamanında kod gibi yorumlayan her mekanizma, potansiyel bir enjeksiyon vektörüdür.
- Saldırı Desenleri: SpEL'de
T()operatörü, OGNL'de ise güvenlik mekanizmalarını devre dışı bırakma adımı, RCE'nin anahtarıdır. - Dolaylı Veri Akışı: Enjeksiyon noktaları her zaman doğrudan değildir. Hata mesajları gibi dolaylı yollarla verinin tehlikeli bir sink'e ulaşabileceğini unutmayın.
- Etkili Savunma: En iyi savunma, kullanıcı girdisini asla bir ifadede yorumlamamaktır. Gerekliyse, Spring'in
SimpleEvaluationContextgibi kısıtlanmış "sandbox" ortamlarını kullanın. Kara listeler (blacklisting) ise güvenilmez ve kırılgan bir savunma yöntemidir.
Bu derste Java dünyasındaki dinamik kod çalıştırma ve ifade dili enjeksiyonlarını inceledik. Bir sonraki derste, .NET ekosistemine geçerek benzer bir "güvenilmeyen veriyi işleme" problemine odaklanacağız, ancak bu sefer farklı bir mekanizma üzerinden: ASP.NET ViewState deserialization. ViewState'in ne olduğunu, nasıl güvensiz bir şekilde deserialize edilebileceğini ve MAC (Message Authentication Code) korumasının nasıl atlatılabileceğini kod seviyesinde analiz edeceğiz.