近期在做个基础的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)内部声明:,例如
(2)外部声明:,例如
DTD 实体声明:
(1)内部实体声明
示例:
]>
(2)外部实体声明
外部实体声明支持的部分协议如下:
]>
(3)参数实体声明
参数实体的引用只能在 DTD中使用
<!ENTITY % 实体名 “实体内容”>
<!ENTITY % 实体名 SYSTEM “URI”>
详情见:http://www.w3school.com.cn/dtd/index.asp
2.XXE攻击
xxe主要利用xml解析器对外部实体的解析去触发攻击,从上面支持的协议列表可以看出,可利用xxe去进行命令执行、读取文件、内网探测端口、作为跳板机攻击内网机器等攻击,故危害巨大。
xxe主要分为两种场景:有回显和无回显。有回显的场景中,可以直接查看到攻击执行的结果。无回显的场景下,只能通过其他手段查看攻击效果。这次主要实验了有回显的情况。
自己编写个简单的登录场景,登录的参数使用xml传输,后端对接收的xml参数进行解析,并且返回登录情况。制作个登录,当点击“登录”按钮时,使用ajax传输xml格式的登录信息,
传输的数据格式为:
后端使用dom4j去解析传入的xml参数,为了实现有回显的效果,我们将传入的username回传给前端,并在前端显示出来。
@Controller
@RequestMapping(value="api")
public class XXEController {
/*
aaa
adfasds
*/
@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文件
]>
&abc; fdsf
修改参数和回显效果如下:
成功读取win.ini文件并回显到前端。这里只做个测试,实际环境中,可能需要绕过各种限制才可行。
3.防御措施
关于防御措施,第一种可以关闭xml解析器外部解析功能,第二种进行过滤,例如过滤掉!、DOCTYPE、SYSTEM、ENTITY等关键字
尝试下第二种方案,过滤关键字。后端代码如下,将部分关键字过滤掉。
@Controller
@RequestMapping(value="api")
public class XXEController {
/*
aaa
adfasds
*/
@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,后续有待深入研究。