概述
LDAP(Lightweight Directory Access Protocol):即輕量級目錄訪問協議。是一種運行於TCP/IP之上的在線目錄訪問協議,主要用於目錄中資源的搜索和查詢。使用最廣泛的LDAP服務如微軟的ADAM(Active Directory Application Mode)和OpenLDAP。
而LDAP 注入是利用用戶引入的參數生成惡意 LDAP 查詢,通過構造 LDAP 過濾器來繞過訪問控制、用戶權限提升。在維持正常過濾器的情況下構造出 AND、OR 操作注入來獲得敏感信息。
目錄數據庫結構
LDAP數據庫,是樹結構的,數據存儲在葉子節點上。
- dn:一條記錄的位置
- dc:一條記錄所屬的區域
- ou:一條記錄所屬的組織
- cn/uid:一條記錄的名字/ID
首先要說明是哪一棵樹(dc),然后是從樹根到目標所經過的所有分叉(ou),最后就是目標的名字(cn/uid),借用一張圖來表明結構如下:

條目&對象類&屬性
- 條目(entry):是目錄中存儲的基本信息單元,上圖每一個方框代表一個entry。一個entry有若干個屬性和若干個值,有些entry還能包含子entry
- 對象類(obejectclass):對象類封裝了可選/必選屬性,同時對象類也是支持繼承的。一個entry必須包含一個objectClass,且需要賦予至少一個值。而且objectClass有着嚴格的等級之分,最頂層是top和alias。例如,organizationalPerson這個objectClass就隸屬於person,而person又隸屬於top
- 屬性(atrribute):顧名思義,用來存儲字段值。被封裝在objectclass里的,每個屬性(attribute)也會分配唯一的OID號碼

基本的LDAP語法
- = 等於
- & 邏輯和
- | 邏輯或
- ! 邏輯不
- * 通配符
邏輯操作符(AND、OR、NOT)和關系操作符(=、>=、<=、~=)
除使用邏輯操作符外,RFC4256還允許使用下面的單獨符號作為兩個特殊常量:
(&) ->Absolute TRUE
(|) ->Absolute FALSE
對象定義:
objectclass: top
objectclass: person
對象類定義:
objectclass: person
objectclasses=( 2.5.6.6 NAME 'person' DESC 'Defines entries that generically represent people.' SUP 'top' STRUCTURAL MUST ( cn $ sn ) MAY ( userPassword $ telephoneNumber $ seeAlso $ description )
屬性定義:
attributetypes=( 2.5.4.4 NAME ( 'sn' 'surName' ) DESC 'This is the X.500 surname attribute, which contains the family name of a person.' SUP 2.5.4.41 EQUALITY 2.5.13.2 ORDERING 2.5.13.3 SUBSTR 2.5.13.4 USAGE userApplications )
搜索語法:
主要根據屬性和值進行搜索
LDAP查詢語句
一個圓括號內的判斷語句又稱為一個過濾器filter。
默認情況下,LDAP的DN和所有屬性都不區分大小寫。
( "&" or "|" (filter1) (filter2) (filter3) ...) ("!" (filter))
LDAP注入
無邏輯操作符的注入
后端代碼如果是這樣寫的:
我們構造輸入語句:
$input=value)(injected_filter
完整的語句就成下面這樣了:
(attribute=value)(injected_filter)
由於一個括號內代表一個過濾器,在OpenLDAP實施中,第二個過濾器會被忽略,只有第一個會被執行。而在ADAM中,有兩個過濾器的查詢是不被允許的。因而這類情況僅對於OpenLDAP有一定的影響。
例如我們要想查詢一個字段是否存在某值時,可以用$input=x*進行推移,利用頁面響應不同判斷x*是否查詢成功。
帶有邏輯操作符的注入
(|(attribute=$input)(second_filter))
(&(attribute=$input)(second_filter))
此時帶有邏輯操作符的括號相當於一個過濾器。此時形如value)(injected_filter)的注入會變成如下過濾器結構
(&(attribute=value)(injected_filter))(second_filter)
雖然過濾器語法上並不正確,OpenLDAP還是會從左到右進行處理,忽略第一個過濾器閉合后的任何字符。一些LDAP客戶端Web組成會忽略第二個過濾器,將ADAM和OpenLDAP發送給第一個完成的過濾器,因而存在注入。
案例分享
萬能用戶名案例
驗證登陸的查詢語句是這樣:
(&(USER=$username)(PASSWORD=$pwd))
輸入$username = admin)(&)(使查詢語句變為:
(&(USER=admin)(&))((PASSWORD=$pwd))
即可讓后面的password過濾器失效,執行第一個過濾器而返回true,達到萬能密碼的效果。
權限提升案例
現假設下面的查詢會向用戶列舉出所有可見的低安全等級文檔:
(&(directory=document)(security_level=low))
這里第一個參數”document”是用戶入口,low是第二個參數的值。如果攻擊者想列舉出所有可見的高安全等級的文檔,他可以利用如下的注入:
document)(security_level=*))(&(directory=documents
生成的過濾器為:
(&(directory=documents)(security_level=*))(&(direcroty=documents)(security_level=low))
###
LDAP服務器僅會處理第一個過濾器而忽略第二個,因而只有下面的查詢會被處理:(&(directory=documents)(security_level=*)),而(&(direcroty=documents)(security_level=low))則會被忽略。結果就是,所有安全等級的可用文檔都會列舉給攻擊者,盡管他沒有權限看它們。
總結
LDAP注入本質就是在OpenLDAP實施中,由於一個括號內代表一個過濾器,第二個過濾器會被忽略,只有第一個會被執行。當查詢語句帶有邏輯操作符時,可以通過注入惡意的LDAP語句去達到不同的目的。