問題:XMLDecoder注入
針對 xml 解碼器的注入攻擊
反序列化用戶控制的 XML ,程序沒有進行驗證, 會讓攻擊者有機會在服務器上執行惡意代
碼。
例:下面代碼片段中, XMLDecoder 處理不可信的輸入。
...
XMLDecoder decoder = new XMLDecoder(new InputSource(new
InputStreamReader(request.getInputStream(), "UTF-8")));
Object object = decoder.readObject();
decoder.close();
...
如果輸入以下 XML 文檔,會實例化 ProcessBuilder 對象,并調用其靜態 start() 方法以運行 Windows
計算器。
<java>
<object class="java.lang.ProcessBuilder">
<array class="java.lang.String" length="1" >
<void index="0">
<string>c:\\windows\\system32\\calc.exe</string>
</void>
</array>
<void method="start"/>
</object>
</java>
修復:XMLDecoder注入
盡量避免將 XMLDecoder 與由用戶控制的數據一起使用。如程序中不能避免,需要驗證輸入的 XML 。
問題:LDAP 注入
LDAP ,輕量目錄訪問協議
|dn : 一條記錄的位置 |
dc : 一條記錄所屬區域 |
|ou : 一條記錄所屬組織 |
|cn/uid: 一條記錄的名字 /ID|
此處我更喜歡把 LDAP 和 數據庫類比起來,我是直接把 LDAP 看成是一個主要用于查詢的數據庫。
數據庫用 “ 表 ” 來存數據, LDAP 用 “ 樹 ” 來存數據。數據庫主要是三個 DB,TABLE,ROW 來定位一條記錄,而 LDAP
首先要說明是哪一棵樹 dc ,然后是從樹根到目的所經過的所有 “ 分叉 ” , ou ( group ),最后就是目標的名
字,例如 UID 等等。
具體到如何定義如下:
dn:cn=honglv,ou=bei,ou=xi,ou=dong,dc=waibo,dc=com
其中樹根是 dc=waibo,dc=com ,分叉 ou=bei,ou=xi,ou=dong ,目標 cn=honglv
注意要把 “cn=stan,ou=linux,ou=computer,dc=ourschool,dc=org” 看成是一個整體,它只是屬性 dn 的
值
具體的一條記錄如下:
dn:cn=stan,ou=linux,ou=computer,dc=ourschool,dc=org
objectClass : organizationalPerson
cn:stan
cn: 小刀
sn: 小刀
description:a good boy
(保存成 LDIF 文件,可以導入到 LDAP 數據庫中) LDAP 基本語法
=( 等于 )
查找 “ 名 “ 屬性為 “ LDAP 注入是指客戶端發送查詢請求時,輸入的字符串中含有一些特殊字符,導致
修改了 LDAP 本來的查詢結構,從而使得可以訪問更多的未授權數據的一種攻擊方式。
例如:以下代碼動態構造一個 LDAP 查詢,并對其加以執行,該查詢可以檢索所有報告給指定
經理的雇員記錄。該經理的名字是從 HTTP 請求中讀取的,因此不可信任。
...
DirContext ctx = new InitialDirContext(env);
String managerName = request.getParameter("managerName");
//retrieve all of the employees who report to a manager
String filter = "(manager=" + managerName + ")";
NamingEnumeration employees = ctx.search("ou=People,dc=example,dc=com",filter);
...
在正常情況下,諸如搜索向 John Smith 經理報告的雇員,該代碼執行的篩選器如下:
(manager=Smith, John” 的所有對象,可以使用: )
(givenName=John)
這會返回 “ 名 ” 屬性為 “John” 的所有對象。圓括號是必需的,以便強調 LDAP 語句的開始和結束。
&( 邏輯與 )
如果具有多個條件并且希望全部條件都得到滿足,則可使用此語法。例如,如果希望查找居住在
Dallas 并且 “ 名 ” 為 “John” 的所有人員,可以使用:
(&(givenName=John)(l=Dallas))
請注意,每個參數都被屬于其自己的圓括號括起來。整個 LDAP 語句必須包括在一對主圓括號中。
操作符 & 表明,只有每個參數都為真,才會將此篩選條件應用到要查詢的對象。
!( 邏輯非 )
此操作符用來排除具有特定屬性的對象。假定您需要查找 “ 名 ” 為 “John” 的對象以外的所有對象。則應
使用如下語句:
(!givenName=John)
此語句將查找 “ 名 ” 不為 “John” 的所有對象。請注意: ! 操作符緊鄰參數的前面,并且位于參數的圓括
號內。由于本語句只有一個參數,因此使用圓括號將其括起以示說明
* (通配符)
可使用通配符表示值可以等于任何值。使用它的情況可能是:您希望查找具有職務頭銜的所有對象。
為此,可以使用:
(title=*)
這會返回 “title” 屬性包含內容的所有對象。另一個例子是:您知道某個對象的 “ 名 ” 屬性的開頭兩個字
母是 “Jo” 。那么,可以使用如下語法進行查找:
(givenName=Jo*)
這會返回 “ 名 ” 以 “Jo” 開頭的所有對象。
高級用法 eg:
您需要一個篩選條件,用來查找居住在 Dallas 或 Austin ,并且名為 “John” 的所有對象。使用的語法
應當是:
(&(givenName=John)(|(l=Dallas)(l=Austin)))
LDAP 注入
LDAP 注入攻擊和 SQL 注入攻擊相似,因此接下來的想法是利用用戶引入的參數生成 LDAP 查詢。一
個安全的 Web 應用在構造和將查詢發送給服務器前應該凈化用戶傳入的參數。在有漏洞的環境中,這些參
數沒有得到合適的過濾,因而攻擊者可以注入任意惡意代碼。
使用得最廣泛的 LDAP : ADAM 和 OpenLDAP ,下面的結論將會致代碼注入:
引入
(attribute=value)
如果過濾器用于構造查詢單缺少邏輯操作符,如 value)(injected_filter ,瞬間導致產生了兩個過濾器,
(attribute=value)(injected\_filter)
通常,在 OpenLDAP 實施中,第二個過濾器會被忽略,只有第一個會被執行。
而在 ADAM 中,有兩個過濾器的查詢是不被允許的,因而這個注入毫無用處。
(|(attribute=value)(second_filter)) or (&(attribute=value)(second_filter))
如果第一個用于構造查詢的過濾器有邏輯操作符,形如 value)(injected_filter) 的注入會變成如下過濾
器:
(&(attribute=value)(injected_filter)) (second_filter) 。
雖然過濾器語法上并不正確, OpenLDAP 還是會從左到右進行處理,忽略第一個過濾器閉合后的任
何字符。
但是有的瀏覽器會進行檢查,檢查過濾器是否正確,這種情況下 value)(injected_filter))(&(1=0 ,于是
就出現了下述 payload
(&(attribute=value)(injected_filter))(&(1=0)(second_filter))
既然第二個過濾器會被 LDAP 服務器忽略,有些部分便不允許有兩個過濾器的查詢。這種情況下,只
能構建一個特殊的注入以獲得單個過濾器的 LDAP 查詢,如 value)(injected_filter 得到
(&(attribute=value)(injected_filter)(second_filter))
AND 注入
這種情況,應用會構造由 ”&” 操作符和用戶引入的的參數組成的正常查詢在 LDAP 目錄中搜索,例如:
(&(parameter1=value1)(parameter2=value2))
這里 Value1 和 value2 是在 LDAP 目錄中搜索的值,攻擊者可以注入代碼,維持正確的過濾器結構但
能使用查詢實現他自己的目標。
繞過訪問控制
一個登陸頁有兩個文本框用于輸入用戶名和密碼,過濾器如下:
(&(USER=Uname)(PASSWORD=Pwd))
如果攻擊者輸入一個有效地用戶名,如 r00tgrok ,然后再這個名字后面注入恰當的語句, password
檢查就會被繞過。輸入 Uname=slisberger)(&)) ,得到如下
(&(USER= slisberger)(&)(PASSWORD=Pwd))
LDAP 服務器只處理第一個過濾器,即僅查詢 (&(USER=slidberger)(&)) 得到了處理。這個查詢永真 , 故
成功繞過
權限提升
現假設下面的查詢會向用戶列舉出所有可見的低安全等級文檔:
(&(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=*))
結果就是,所有安全等級的可用文檔都會列舉給攻擊者
OR 注入
這種情況,應用會構造由 ”|” 操作符和用戶引入的的參數組成的正常查詢在 LDAP 目錄中搜索,例如:
(|(parameter1=value1)(parameter2=value2))
這里 Value1 和 value2 是在 LDAP 目錄中搜索的值,攻擊者可以注入代碼,維持正確的過濾器結構但
能使用查詢實現他自己的目標。
具體的注入方式和 AND 差不太多,不予詳述。
AND 盲注
假設一個 Web 應用想從一個 LDAP 目錄列出所有可用的 Epson 打印機,錯誤信息不會返回,應用發
送如下的過濾器:
(&(objectClass=printer)(type=Epson*))
正確的過濾器為:
(&(objectClass=printer)(type=Epson*))
而當注入 )(objectClass=))(&(objectClass=void 時得到
(&(objectClass=*)(objectClass=*))(&(objectClass=void)(type=Epson*))
執行第一個,過濾器 objectClass=* 總是返回一個對象。當圖標被顯示時響應為真,否則為假。
這樣我們就可以猜第二個括號的 objectclass 字段有些什么內容了。
LDAP 盲注技術讓攻擊者使用基于 TRUE/FALSE 的技術訪問所有的信息。
盲注深入
攻擊者可以使用字母、數字搜索提取屬性的值,這個想法的關鍵在于將一個復雜的值轉化為
TRUE/FALSE 列表。這個機制,通常稱為 booleanization ,大意是二值化吧,圖十二概括了該機制,可用于
不同的方式。
假設攻擊者想知道 department 屬性的值,處理如下:
(&(idprinter=HPLaserJet2100)(department=a*))(object=printer))
(&(idprinter=HPLaserJet2100)(department=f*))(object=printer))
(&(idprinter=HPLaserJet2100)(department=fa*))(object=printer))
如此根據返回的不同結果猜解是否正確,和 MYSQL 盲注類似。
同樣,攻擊者可以使用字符集削減技術減少獲得信息所需的請求數,為完成這一點,他使用通配符
測試給定的字符在值中是否為 anywhere :
(&(idprinter=HPLaserJet2100)(department=*b*))(object=printer))
(&(idprinter=HPLaserJet2100)(department=*n*))(object=printer))
這樣子可以看 department 中是否有 b 和 n ,巧用可以加速猜解過程,當然一般肯定都是寫腳本猜解
但是,由于篩選器是通過連接一個常數基本查詢串和一個用戶輸入串動態構造而成的,因此,該查詢只在
managerName 不包含任何 LDAP 元字符時才能正常運行。如果攻擊者為 managerName 輸入字符串
Hacker, Wiley)(|(objectclass=*) ,則該查詢會變成:
(manager=Hacker, Wiley)(|(objectclass=*))
根據執行查詢的權限,增加 |(objectclass=*) 條件會導致篩選器與目錄中的所有輸入都匹配,而
且會使攻擊者檢索到有關用戶輸入池的信息。根據執行 LDAP 查詢的權限大小,此次攻擊的影響范圍可能
會有所差異,但是如果攻擊者能夠控制查詢的命令結構,那么這樣的攻擊至少會影響執行 LDAP 查詢的用
戶可以訪問的所有記錄。
修復:LDAP 注入
LDAP 注入的根本原因是攻擊者提供了可以改變 LDAP 查詢含義的 LDAP 元字符。構造 LDAP 篩選器
時,程序員應清楚哪些字符應作為命令解析,而哪些字符應作為數據解析。為了防止攻擊者侵犯程序員的
各種預設情況,應使用白名單的方法,確保 LDAP 查詢中由用戶控制的數值完全來自于預定的字符集合,
應不包含任何 LDAP 元字符。如果由用戶控制的數值范圍要求必須包含 LDAP 元字符,則應使用相應的編
碼機制轉義這些元字符在 LDAP 查詢中的意義。
例如:以下代碼片段中使用 Spring 框架 EqualsFilter 類來構造一個編碼得當的篩選器字符串。不
論請求參數中是否存在 LDAP 元字符,這一字符串都可以保護指令的命令結構。
...
DirContext ctx = new InitialDirContext(env);
String managerName = request.getParameter("managerName");
//retrieve all of the employees who report to a manager
EqualsFilter filter = new EqualsFilter("manager", managerName);
NamingEnumeration employees = ctx.search("ou=People,dc=example,dc=com",filter.toString())