根据红日安全写的文章,学习PHP代码审计的第三节内容,题目均来自PHP SECURITY CALENDAR 2017,讲完相关知识点,会用一道CTF题目来加深巩固。之前分别学习讲解了in_array函数缺陷和filter_var函数缺陷,有兴趣的可以去看看:
PHP代码审计01之in_array()函数缺陷
PHP代码审计02之filter_var()函数缺陷
下面我们看第一题,代码如下:
<?php function __autoload($className) { include $className; } $controllerName = $_GET['c']; $data = $_GET['d']; if (class_exists($controllerName)) { $controller = new $controllerName($data['t'], $data['v']); $controller->render(); } else { echo 'There is no page with this name'; } class HomeController { private $template; private $variables; public function __construct($template, $variables) { $this->template = $template; $this->variables = $variables; } public function render() { if ($this->variables['new']) { echo 'controller rendering new response'; } else { echo 'controller rendering old response'; } } } ?>这段代码有两处漏洞,第一处是文件包含漏洞,现在看代码第八行,这里用到了class_exists()函数来判断用户传过来的控制器是否存在。现在看一下PHP手册对这个函数的解释。
通过看上面的解释,我们知道,如果不指定第二个参数,默认情况下,如果本程序存在__autoload()函数,如果检查的类不存在,那么class_exists()函数就会去调用它。这道题的文件包含漏洞,就出现在这里。如果PHP版本在5~5.3之间,就可以使用路径穿越来包含任意文件,比如类名为../../../../../etc/passwd的查找,那么将查看passwd的内容。
第二处漏洞是在上面代码的第10行,我们发现实例化的类名和传入的参数都是我们可以控制的,所以我们可以通过这个漏洞调用PHP代码库的任意构造构造函数。比如可以使用PHP内置类SimpleXMLElement来进行XXE攻击,看一下PHP手册对这个函数的解释:
功能就是用来表示XML文档中的元素。详细的请看下面,重点是第六条,下面要用到它。
SimpleXMLElement::addAttribute-向SimpleXML元素添加属性
SimpleXMLElement::addChild-向XML节点添加子元素
SimpleXMLElement::asXML-基于SimpleXML元素返回格式良好的XML字符串
SimpleXMLElement::attributes-标识元素的属性
SimpleXMLElement::children-查找给定节点的子节点
SimpleXMLElement::__construct-创建新的SimpleXMLElement对象
SimpleXMLElement::count-计算元素的子级
ExtSimpleNamespaces::GetDocElement-在文档命名空间中声明
SimpleXMLElement::getName-获取XML元素的名称
SimpleXMLElement::getNamespaces-返回文档中使用的命名空间
SimpleXMLElement::registerXPathNamespace-为下一个XPath查询创建前缀/ns上下文
SimpleXMLElement::saveXML-别名SimpleXMLElement::asXML
SimpleXMLElement::__toString -返回字符串内容
SimpleXMLElement::xpath-对XML数据运行XPath查询
为了便于理解,用一小段代码来说明:
<?php $xml = <<<EOF <?xml version = "1.0" encoding="utf-8"?> <!DOCTYPE ANY [ <!ENTITY xxe SYSTEM "file:///C:Windows/win.ini"> ]> <x>&xxe;</x> EOF; $xml_class= new SimpleXMLElement($xml,LIBXML_NOENT); var_dump($xml_class); ?>查看系统win.ini文件,效果如下图:
通过上面的学习分析,是不是对实例化漏洞和XXE漏洞有了一点点的理解呢?下面我们来做一道CTF题目来练习一下吧,这道题考察的就是实例化漏洞和XXE漏洞。现在我们看具体代码:
<?php class NotFound{ function __construct() { die('404'); } } spl_autoload_register( function ($class){ new NotFound(); } ); $classname = isset($_GET['name']) ? $_GET['name']:null; $param = isset($_GET['param']) ? $_GET['param'] : null; $param2 = isset($_GET['param2']) ? $_GET['param2'] : null; if (class_exists($classname)){ $newclass = new $classname($param,$param2); var_dump($newclass); foreach ($newclass as $key=>$value) echo $key.'$value'.'<br>'; } ?>我们把注意力放在class_exists()函数这里,上面我们说过了,这个函数它会去检查类是否定义,如果不存在的话,就会调用程序中的 __autoload 函数。我们发现上面没有__autoload 函数,是用spl_autoload_register注册了和__autoload()差不多的,这里输出404信息。
我们仔细看上面的代码第12~16行,我们发现这里的类和类里面的参数都是我们可以控制的,满足了上面咱们提到的实例化漏洞。也就是说,我们可以调用PHP的内置类来完成我们的攻击。
先用GlobIterator类来寻找flag文件的名字,PHP手册对这个类的构造函数定义如下:
通过上图,我们知道,第一个参数是必须的的,也就是搜索的文件名,第二个参数为选择文件的哪个信息作为键名。咱们先搜一下.txt文件。我们构造payload如下: ?name=GlobIterator¶m=*.txt