[SWPUCTF 2021 新生赛]ez_unserialize
刚开始不知道怎么操作,传入index.php直接报错,就先用御剑扫一遍
拿到题目代码
这是一个 PHP 代码片段,它使用了反序列化函数 unserialize() 来处理用户传递的参数。在代码中,它首先禁用了错误报告,然后通过 show_source() 函数显示了名为 “cl45s.php” 的文件的代码。接下来,代码定义了一个名为 wllm 的类,并在构造函数中设置了 $admin 和 $passwd 的初始值。在析构函数中,如果 admin的值为"admin",passwd 的值为 “ctf”,则包含名为 “flag.php” 的文件,并输出 $flag。否则,将输出 $admin 和 $passwd 的值,并显示 “Just a bit more!”
题目中提示要以get方式给p传参,并且会对传入参数进行反序列化操作
构造payload:?p=O:4:"wllm":2:{s:5:"admin";s:5:"admin";s:6:"passwd";s:3:"ctf";}
[SWPUCTF 2021 新生赛]no_wakeup
解析:
- 首先使用
header()
函数设置输出内容为 HTML 格式,并指定字符集为 UTF-8。 - 使用
error_reporting()
函数将错误报告级别设置为 0,即禁用错误报告。 - 调用
show_source()
函数来显示名为 “class.php” 的文件的源代码。
接下来是一个名为 “HaHaHa” 的类的定义:
- 类中定义了两个公共属性
$admin
和$passwd
,分别用于存储管理员和密码。 - 构造函数
__construct()
设置了默认的管理员用户名为 “user”,密码为 “123456”。 __wakeup()
方法在反序列化时被调用,将密码进行 SHA1 哈希操作。__destruct()
方法在对象被销毁时被调用,如果管理员用户名等于 “admin”,并且密码等于 “wllm”,则包含名为 “flag.php” 的文件并显示$flag
变量;否则则显示密码和 “No wake up”。
脚本的最后部分为:
- 通过
$_GET['p']
获取 URL 中名为 “p” 的参数并将其赋值给变量$Letmeseesee
。 - 使用
unserialize()
函数对$Letmeseesee
进行反序列化操作。
构造payload:O:6:"HaHaHa":2:{s:5:"admin";s:5:"admin";s:6:"passwd";s:4:"wllm";}
但是没有出现flag
绕过 __wakeup() 函数
当序列化字符串表示对象属性个数的值大于真实个数的属性时就会跳过__wakeup的执行
将对象中的变量个数改为大于2的数值,例如:
O:6:"HaHaHa":3:{s:5:"admin";s:5:"admin";s:6:"passwd";s:4:"wllm";}或
O:6:"HaHaHa":6:{s:5:"admin";s:5:"admin";s:6:"passwd";s:4:"wllm";}
就可以回显flag
笔记
解析O:6:"HaHaHa":6:{s:5:"admin";s:5:"admin";s:6:"passwd";s:4:"wllm";}
各个字符的含义的详细解析:
O:
:这是序列化字符串的类型标识,表示这是一个对象的序列化表示。6
:表示类名HaHaHa
的长度。:"HaHaHa":
:表示类名HaHaHa
。6
:表示对象的属性数量。{
:开始对象的定义。s:5:"admin";
:表示属性名为admin
,其中s:5
表示属性值长度为 5,"admin"
是属性值本身。s:6:"passwd";
:表示属性名为passwd
,其中s:6
表示属性值长度为 6,"passwd"
是属性值本身。s:4:"wllm";
:表示属性名为wllm
,其中s:4
表示属性值长度为 4,"wllm"
是属性值本身。}
:结束对象的定义。综合起来,这个反序列化字符串表示一个类名为
HaHaHa
的对象,该对象有三个属性:admin
、passwd
和wllm
,它们的值分别为"admin"
、"passwd"
和"wllm"
。
绕过 __wakeup() 函数
当序列化字符串表示对象属性个数的值大于真实个数的属性时就会跳过__wakeup的执行
常见的PHP魔术方法:
__construct: 在创建对象时候初始化对象,一般用于对变量赋初值。
__destruct: 和构造函数相反,当对象所在函数调用完毕后执行。
__toString:当对象被当做一个字符串使用时调用。
__sleep:序列化对象之前就调用此方法(其返回需要一个数组)
__wakeup:反序列化恢复对象之前调用该方法
__call:当调用对象中不存在的方法会自动调用该方法。
__get:在调用私有属性的时候会自动执行
__isset()在不可访问的属性上调用isset()或empty()触发
__unset()在不可访问的属性上使用unset()时触发
一、简介:
序列化就是将数据转化成一种可逆的字符串,字符串还原原来结构的过程叫做反序列化
序列化后,方便保存和传输(保留成员变量,不保留函数方法)
数据(对象)--------序列化---------->字符串-----------反序列化-------->数据(对象)
二、原理:
2.1、函数:
2.1.1、serialize()序列化
将一个对象转换成可以传输的一个字符串
序列化对象后,可以方便的将它传递到其他需要它的地方,且其类型和结构不会改变
eg:
class S{
public $test="pikachu";
}
$s=new S(); //创建一个对象
serialize($s); //把这个对象进行序列化
2.1.2、unserialize()反序列化
将序列化后的字符串还原成一个对象,或数组(即进行反序列化),并返回原始的对象结构
并在后面的代码中继续使用,加密后的字符串如下所示
对字符串代码进行分析:
$u=unserialize("O:1:"S":1:{s:4:"test";s:7:"pikachu";}");
echo $u->test; //得到的结果为pikachu
O:1:"S":1:{s:4:"test";s:7:"pikachu";} //这是序列化结果
O:代表object 1:代表对象名字长度为一个字符 S:对象名称 1:代表对象里面有一个变量 s:数据类型 4:变量名长度 test:变量名称 s:数据类型 7:变量值的长度 pikachu:变量值
三、常见的序列化格式:
二进制格式
字节数组
json字符串
xml字符串
……
布尔型(bool):b
整数型(int):i
字符串型(str):s
数组型(array):a
对象型(object):O
NULL型:N
四、产生的原因:
对用户的输入检测不严
4.1、无类:
当未检测出攻击者输入的序列化字符串中包含的恶意执行语句
攻击者从而达到控制反序列化过程,进而进行恶意代码的执行(好比SQL注入,目录遍历等操作)
4.2、有类:
当进行反序列化的时候就有可能会触发对象中的一些魔术方法
五、魔术方法(触发)
(前提:有可利用的类)
__construct() //创建对象时触发
__destruct() //对象销毁时触发
__call() //在对象中调用不可访问的方法时触发
__callStatic() //在静态中调用不可访问的方法时触发
__get() //用于从不可访问的属性读取数据
__set() //用于将数据写入不可访问的属性
__isset() //在不可访问的属性上调用isset()或empty()触发
__unset() //在不可访问的属性上使用unset()时触发
__invoke() //当脚本尝试将对象调用为函数时触发
__wakeup() //执行unserialize()时,先会调用这个函数
__sleep() //执行serialize()时,先会调用这个函数
参考:(37)【PHP反序列化】PHP反序列化原理、函数、利用过程_php反序列化函数有哪些_黑色地带(崛起)的博客-CSDN博客
[CTF]PHP反序列化总结_ctf php反序列化_Y4tacker的博客-CSDN博客
PHP常见函数漏洞_php函数漏洞_Elitewa的博客-CSDN博客