目录
一.PHP的类与对象
1.类
2.对象
二.序列化与反序列化
1.序列化
1.1什么是序列化
1.2序列化格式
2.反序列化
三.Magic函数
1.常用的Magic函数
2.__sleep()函数的补充说明
四.反序列化漏洞利用
1.参数可控
2.存在Magic方法
3.涉及成员变量
五.__wakeup()函数的绕过
六.反序列化漏洞防御
在讲序列化与反序列化之前,我们可以先了解一下PHP的类与对象。(熟悉PHP的可以直接跳过)
定义了一件事物的抽象特点。类的定义包含了数据的形式以及对数据的操作。
name=$n; $this->weight=$w; $this->age=$a; $this->sex=$s; } //析构函数:当对象结束其生命周期时,系统会自动调用 function __destruct(){ print "销毁"; } function SetAge($a){ $this->age=$a; } function getAge(){ echo $this->age; } } ?>
是类的实例。
/*对象的创建*/ $person=new Person("张三",60,20,"男");//用new运算符来实例化该类的对象 /*调用成员函数*/ $person->setAge(18); $person->getAge();
序列化是将对象的状态信息转化为可以存储或传输的形式的过程。简单讲,就是将数据转化成一种可逆的数据结构。
序列化的作用就是便于传输对象和用作缓存。
在PHP中,使用serialize()函数进行序列化。
(以下使用的对象均为上述的例子)
"value1","index2"=>"value2");//数组 $str6=new Person("张三",60,20,"男");//对象 /*序列化*/ $str11=serialize($str1); $str22=serialize($str2); $str33=serialize($str3); $str44=serialize($str4); $str55=serialize($str5); $str66=serialize($str6); echo $str11.""; echo $str22.""; echo $str33.""; echo $str44.""; echo $str55.""; echo $str66.""; ?>
运行结果:
在序列化时,只会保存类名和属性值,并不会保存方法。
知道什么是序列化,反序列化就比较好理解了,将序列化后的字符串恢复成原始数据的逆向过程就叫做反序列化。
在PHP中,使用unserialize()函数进行反序列化。
"; echo var_dump($str222).""; echo var_dump($str333).""; echo var_dump($str444).""; echo var_dump($str555).""; echo var_dump($str666).""; ?>
运行结果:
在反序列化中:
1、如果传递的字符串不可以序列化,则返回 FALSE
2、如果对象没有预定义,反序列化得到的对象是__PHP_Incomplete_Class
__construct | 当一个对象创建时被调用 |
__destruct | 当一个对象销毁时被调用 |
__toString | 当一个对象被当作一个字符串使用 |
__serialize() | 调用serialize()前执行 |
__unserialize() | 调用unserialize()前执行 |
__sleep | 在对象被序列化之前运行 |
__wakeup | 在对象被反序列化之后被调用 |
__call() | 在对象上下文中调用不可访问的方法时触发 |
__callStatic() | 在静态上下文中调用不可访问的方法时触发 |
__get() | 用于从不可访问的属性读取数据 |
__set() | 用于将数据写入不可访问的属性 |
__isset() | 在不可访问的属性上调用isset()或empty()触发 |
__unset() | 在不可访问的属性上使用unset()时触发 |
__invoke() | 当脚本尝试将对象调用为函数时触发 |
name=$n; $this->weight=$w; $this->age=$a; $this->sex=$s; } function __sleep(){ return array('name','weight'); } } $person=new Person("张三",60,20,"男"); $str=serialize($person); echo $str; ?>
运行结果:
在类中,如果定义了__sleep()函数,则在序列化中,只会序列__sleep()函数数组里的属性,而其他并不会被序列化。
这里以BUUCTF在线评测 (buuoj.cn)为例来讲解。
(此处主要讲反序列化漏洞出现的原因,不讲如何找flag,当然,你学完本篇之后,这道题你就完全可以做了,我后面也会写一篇题解)
补上BUUCTF[极客大挑战 2019]PHP1题解-CSDN博客
我们先找到网站的源代码。
此处,我们可以看到这里用get提交方式获取select。
一个可序列化漏洞出现的原因必然有:unserialize()函数的参数可控,比如通过GET请求传参,此处也是漏洞触发点。
如果没有这个条件,我们就无法控制传进的数据,接下来的任何一步渗透就都无法执行。
在上述中讲过,有些Magic方法会在对象在被反序列化中被调用,因此,所涉及到的Magic方法就是获取信息的关键点。
由此,我们可以得出反序列化漏洞出现的另一原因:类中存在Magic函数,函数里面有向php文件做读写数据或者执行命令的操作。
常见的利用函数:
类别 | 函数 |
命令执行 | system(),passthru(),popen().exec() |
文件操作 | file_put_contents(),file_get_contents(),unlink() |
这个并不难理解,可以与第一点联系起来,参数可控就是为了传进我们所想要的对象成员变量,如果在Magic函数中并不会因为所传进的数据而有不同的执行结果,那就没有意义了。
因此,反序列化漏洞存在的又一原因:操作的内容需要有对象中的成员变量的值。
总结,对反序列化漏洞的利用就是:序列化一个对象,修改成员变量的值,达到操作其他
文件或者执行命令的目的。
由前面所学可以知道,__wakeup()函数在对象被反序列化之后被调用。在反序列化渗透中,我们往往需要绕过该函数。
绕过方式十分简单,在反序列化的时候,如果写的参数个数,与实际的参数个数不一致,就会绕过__wakeup()函数。
name=n; $this->age=a; } function __wakeup(){ echo "__wakeup()"; } } $person=new Person("张三",20); $str='O:6:"Person":3:{s:4:"name";s:1:"n";s:3:"age";s:1:"a";}'; $str1=unserialize($str); echo var_dump($str); ?>
运行结果:
明显绕过了__wakeup()函数。
既然我们知道了造成反序列化漏洞的原因,那我们就可以针对这些原因一一防御了。
1.针对unserialize()和Magic函数审计。
2.对用户输入的内容过滤。
3.设置白名单,限制反序列化的类;不能动态传参。
上一篇:nginx Rewrite