XXE初探
XXE,即XML外部实体注入,应用程序解析用户可控的XML输入时,未正确处理外部实体引用,导致可加载恶意外部文件和代码,造成任意文件读取、命令执行、内网端口扫描、攻击内网网站、发起Dos攻击等。
前置知识
关于XML和DTD 可以参看:https://xz.aliyun.com/news/6483
XML的两个关键概念:
XML
可扩展标记语言,设计宗旨是传输数据。
基本格式:
1 | <!--xml文件的声明--> |
称为XML prolog,用于声明XML版本和编码 可选 必须放在文档开头;standalone未yes代表DTD仅用于验证文档结构(即外部实体会被禁用),不过默认为no且一些解析器会忽略这一项。
基本语法:
所有 XML 元素都须有关闭标签。
XML 标签对大小写敏感。
XML 必须正确地嵌套。
XML 文档必须有根元素。
XML 的属性值须加引号。
若多个字符都需要转义:
DTD
用于控制XML的格式规范 为XML定义语义约束,DTD可以内联或者被外部引用。
基本语法:
1 |
|
用于告知xml解析器此文档遵循的结构规则、定义的实体、哪些元素和属性合法
内联DTD在后面的实体部分有很多体现 这里贴一下外部的DTD引用
1 |
|
这样即可引用外部的DTD,SYSTEM代表引入本地的 PUBLIC代表引用网络上的
1 | <!DOCTYPE 根元素 PUBLIC "DTD名称" "DTD文档的URL"> |
此外,实体必须在DTD中定义,而 DTD 必须通过 <!DOCTYPE> 引入。
实体
实体是用于定义引用普通文本或特殊字符的快捷方式的变量。
实体引用是对实体的引用。
实体可在内部或外部进行声明。
相当于变量 可以被引用 是XXE能够触发的核心
内部实体
内容直接写在DTD中:
例子:
1 |
|
解析结果等价于:
1 | <note> |
外部实体
外部实体的内容来自外部 URI(如文件系统、HTTP、FTP 等),由 SYSTEM 或 PUBLIC 关键字引入。
内部实体 → 值在 DTD 里写死
外部实体 → 值从 URI 动态加载
例如:
1 |
|
可以用于读取本地的/etc/passwd文件。
参数实体
可以理解为
DTD 内部的“宏”或“变量”
普通实体(通用实体)的“DTD 专用版本”
定义方法:
1 | <!ENTITY % 实体名 "实体值"> |
只能在DTD内使用%实体名;的方式进行引用
实体引用
XML预定义五个实体引用,即用< > & ' " 替换 < > & ‘ “
用
&实体名的方式,即可引用实体
攻击手法
任意文件读取
1 |
|
这里最好是加上这句 含义是让 XML 解析器接受 <info> 元素包含任意内容(包括文本、子元素等),可以提高攻击成功概率。
有时file协议被禁用 可以尝试php://filter协议读取:
php://filter/read=convert.base64-encode/resource=
命令执行
只有在安装了expect扩展的PHP环境里才行,php默认不安装这个。
1 |
|
SSRF与内网探测
1 |
|
可以用于探测内网服务、端口是否开放
Blind XXE
无回显xxe
blind xxe的本质是通过xml注入 让靶机把内部文件信息发送到我们指定的攻击机上(vps)
首先,构造一个实体 读取本地文件;然后把这个文件的内容作为data拼接给攻击机的url,然后发送请求。这样攻击机上的日志就会记录我们需要的值。
然而,内联DTD不允许我们用参数实体引用动态构造新的实体,XML 规范要求 DTD 必须在解析时是静态、完整的结构;不允许通过参数实体展开后“再解析”为新的 DTD 声明。
因此,我们需要从外部引入一个DTD,只有在外部的DTD中才会对参数实体展开后的结果,重新解析成DTD声明,从而实现动态构造实体。所以,在上文的把这个文件的内容作为data拼接给攻击机的url这一步,需要通过外部DTD实现url的拼接。
流程:
先创建外部DTD:
1 |
|
构造XML Payload:
1 |
代码流程:
1读取本地文件;
2进入外部dtd,在外部dtd里将读取结果拼接到url里
3执行url