XXE 漏洞简单研究

近期在做个基础的 web 常见漏洞的 ppt,主要参考 OWASP TOP 10 2017RC2,此版本中增加了 XXE 攻击,所以自己简单的研究下 XXE 攻击。XXE(XML External Entity)XML 外部实体,当前端和后端通信数据采用 xml,可传入 xml 外部实体,利用后端 xml 解析器漏洞,使 xml 解析器去访问攻击者指定的资源。首先需要了解一些 xml 的基础知识。

1.xml 基础知识

XML 用于标记电子文件使其具有结构性的标记语言,可以用来标记数据、定义数据类型,是一种允许用户对自己的标记语言进行定义的源语言。XML 文档结构包括 XML 声明、DTD 文档类型定义(可选)、文档元素。

XML 具有一些特性:

(1).XML 是一种标记语言,很类似 HTML

(2).XML 被设计用来传输和存储数据,而 HTML 被设计用来显示数据

(3).XML 标签没有被预定义,需要用户自行定义标签

(4).XML 具有自我描述性

XML 的语法规则:

(1).XML 必须有一个根元素

(2).XML 必须有关闭标签

(3).XML 标签对大小写敏感

(4).XML 元素必须正确的嵌套

(5).XML 属性值必须加引号

XML DTD(文档类型定义)的作用是定义 XML 文档的合法构建模块。它使用一系列的合法元素来定义文档结构。DTD 可以在 XML 文档内声明,也可以外部引用。

(1)内部声明:<!DOCTYPE 根元素 [元素声明]>,例如 <!DOCTYPE note aaa>

(2)外部声明:<!DOCTYPE 根元素 SYSTEM "文件名">,例如 <!DOCTYPE root SYSTEM "test.dtd">

DTD 实体声明:

(1)内部实体声明

<!ENTITY 实体名称 "实体的值">

示例:

<!DOCTYPE foo [

<!ELEMENT foo ANY> <!-- 声明元素 -->

<!ENTITY test "abcdefg"> <!-- 声明内部实体 -->

<!ENTITY xxe "Thinking"> <!-- 声明内部实体 -->

]>

<foo>&xxe;&test;</foo>

(2)外部实体声明

<!ENTITY 实体名称 SYSTEM "URI/URL">

外部实体声明支持的部分协议如下:

<!DOCTYPE foo [

<!ELEMENT foo ANY >

<!ENTITY xxe SYSTEM "file:///c:/windows/win.ini"> <!-- 声明外部实体 -->

]>

<foo>&xxe;</foo>

(3)参数实体声明

参数实体的引用只能在 DTD 中使用

<!ENTITY % 实体名 "实体内容">

<!ENTITY % 实体名 SYSTEM "URI">

详情见:http://www.w3school.com.cn/dtd/index.asp

2.XXE 攻击

xxe 主要利用 xml 解析器对外部实体的解析去触发攻击,从上面支持的协议列表可以看出,可利用 xxe 去进行命令执行、读取文件、内网探测端口、作为跳板机攻击内网机器等攻击,故危害巨大。

xxe 主要分为两种场景:有回显和无回显。有回显的场景中,可以直接查看到攻击执行的结果。无回显的场景下,只能通过其他手段查看攻击效果。这次主要实验了有回显的情况。

自己编写个简单的登录场景,登录的参数使用 xml 传输,后端对接收的 xml 参数进行解析,并且返回登录情况。制作个登录,当点击 "登录" 按钮时,使用 ajax 传输 xml 格式的登录信息,

传输的数据格式为:<login><username > 用户名 </username><password > 密码 </password></login>

后端使用 dom4j 去解析传入的 xml 参数,为了实现有回显的效果,我们将传入的 username 回传给前端,并在前端显示出来。

复制代码
@Controller
@RequestMapping(value="api")
public class XXEController {
    /*
        <login>
            <username>aaa</username>
            <password>adfasds</password>
        </login>
    */
    @ResponseBody
    @RequestMapping(value="loginXml.json"/*,method= RequestMethod.POST*/)
    public AjaxResponse testXXE(String xml){
        AjaxResponse ajaxResponse = new AjaxResponse();
        try {
            
            Document document = DocumentHelper.parseText(xml);
            Element rootEle = document.getRootElement();
            String username = rootEle.elementTextTrim("username");
            //String password = rootEle.elementTextTrim("password");
            ajaxResponse.setCode(210);
            ajaxResponse.setMsg("登录失败");
            ajaxResponse.setContent(username);
        }catch (Exception e){
            e.printStackTrace();
            ajaxResponse.setCode(211);
            ajaxResponse.setMsg("数据处理失败");
        }
        
        return ajaxResponse;
    }
    
}

正常登陆时,传输的参数和回显效果如下:

复制代码
xml=%3Clogin%3E%3Cusername%3Eadmin%3C%2Fusername%3E%3Cpassword%3Etest123%3C%2Fpassword%3E%3C%2Flogin%3E

使用 xxe 攻击时,攻击示例代码如下,此代码读取 c:/windows/win.ini 文件

复制代码
<!DOCTYPE foo 
[<!ELEMENT foo ANY>
<!ENTITY abc SYSTEM "file:///c:/windows/win.ini">]>
<login><username>&abc;</username><password>fdsf</password></login>

修改参数和回显效果如下:

3. 防御措施

关于防御措施,第一种可以关闭 xml 解析器外部解析功能,第二种进行过滤,例如过滤掉!、DOCTYPE、SYSTEM、ENTITY 等关键字

尝试下第二种方案,过滤关键字。后端代码如下,将部分关键字过滤掉。

复制代码
@Controller
@RequestMapping(value="api")
public class XXEController {
    /*
        <login>
            <username>aaa</username>
            <password>adfasds</password>
        </login>
    */
    @ResponseBody
    @RequestMapping(value="loginXml.json"/*,method= RequestMethod.POST*/)
    public AjaxResponse testXXE(String xml){
        AjaxResponse ajaxResponse = new AjaxResponse();
        try {
            
            //过滤部分关键字,防止XXE
            String newXml = xml.replace("!","")
                    .replace("DOCTYPE","")
                    .replace("ELEMENT","")
                    .replace("ENTITY","");
            
            Document document = DocumentHelper.parseText(newXml);
            Element rootEle = document.getRootElement();
            String username = rootEle.elementTextTrim("username");
            //String password = rootEle.elementTextTrim("password");
            ajaxResponse.setCode(210);
            ajaxResponse.setMsg("登录失败");
            ajaxResponse.setContent(username);
        }catch (Exception e){
            e.printStackTrace();
            ajaxResponse.setCode(211);
            ajaxResponse.setMsg("数据处理失败");
        }
        
        return ajaxResponse;
    }
    
}

可以看到,将关键字过滤(替换成 ""),会导致传入的数据不符合 xml 的格式,后端在 xml 解析器在解析数据时,直接出错并跳转到异常处理代码段,证明这样过滤是有效的。以上为一次简单的研究 xxe,后续有待深入研究。

相关推荐
用户962377954485 小时前
DVWA 靶场实验报告 (High Level)
安全
数据智能老司机8 小时前
用于进攻性网络安全的智能体 AI——在 n8n 中构建你的第一个 AI 工作流
人工智能·安全·agent
数据智能老司机8 小时前
用于进攻性网络安全的智能体 AI——智能体 AI 入门
人工智能·安全·agent
用户9623779544810 小时前
DVWA 靶场实验报告 (Medium Level)
安全
red1giant_star10 小时前
S2-067 漏洞复现:Struts2 S2-067 文件上传路径穿越漏洞
安全
用户9623779544813 小时前
DVWA Weak Session IDs High 的 Cookie dvwaSession 为什么刷新不出来?
安全
cipher2 天前
ERC-4626 通胀攻击:DeFi 金库的"捐款陷阱"
前端·后端·安全
一次旅行5 天前
网络安全总结
安全·web安全
red1giant_star5 天前
手把手教你用Vulhub复现ecshop collection_list-sqli漏洞(附完整POC)
安全
ZeroNews内网穿透5 天前
谷歌封杀OpenClaw背后:本地部署或是出路
运维·服务器·数据库·安全