portswigger 文件上传漏洞

知识储备

文件上传漏洞的原理

知其然,知其所以然
简单的说,文件上传漏洞发生在具有文件上传功能的模块,文件上传后总是会被保存在服务器的某个地方,当文件上传操作与保存操作处理不当时,文件上传漏洞就由此产生。
例如,上传的文件可以是一个可执行文件,该文件通过各种手段被上传后再想办法执行,攻击者则会获得webshell从而操作 web 服务。

文件上传漏洞的危害

  • 服务器被控制
  • 客户端被劫持
  • 敏感信息泄露
  • 恶意文件托管
  • 未经授权操作
  • 拒绝服务攻击

简单记忆串联:一旦通过文件上传漏洞上传了可执行文件并执行从而获得 webshell 后,hack 可以控制服务器,从而未经授权进行操作获取敏感信息、甚至托管恶意文件
更不用说访问服务器的客户端会被劫持,而最简单的,hack 可以让该服务器挂掉拒绝服务

常见的文件上传漏洞检测方法与绕过手段

参考文章

客户端检测

  • 前端验证

原理:在网页上写一段Javascript脚本,校验上传文件的后缀名,有白名单形式也有黑名单形式。

绕过:如果是使用 js 验证的可以尝试禁用 js,或者先修改文件类型然后通过 burp 抓包再把文件类型改回来

服务器端检测

  • MIME类型检测

原理:检测Content-Type
:修改Content-Type,或者上传.png(允许的类型),然后再抓包修改 filename 改为原来需要的后缀

  • 目录路径检测

原理:检测 path 参数相关内容判断路径是否合法等
绕过:使用0x00截断((但需要php5.3.4以下,以及magic_qoute_gpc为off)

  • 文件内容检测

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

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

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

绕过

方法1,上传一句话,捉包,加上图片类型的文件头;
方法2,上传图片马,改后缀,或者直接在可上传的图片内容后加上一句话木马。

文件加载检测(代码注入)

原理
一种是对上传的图片文件进行渲染/加载测试;
还有是对上传的图片文件进行二次渲染。

绕过
对于第一种:使用代码注入的方法,使用winhex在不破坏文件本身的渲染情况下找一个空白区进行填充代码,一般会是图片的注释区;
对于第二种:对文件加载器进行攻击,常见的就是溢出攻击;上传自己的恶意文件后,服务器上的文件加载器会主动进行加载测试,加载测试时被溢出攻击执行 shellcode,比如 access/mdb 溢出。

  • 文件扩展名检测

原理
文件拓展名检测主要分为黑名单检测和白名单检测的方式。
黑名单,即在程序中定义了特定的文件后缀名为不可上传的;
白名单,即在程序中定义了允许上传的后缀名。

绕过
文件名大小写绕过
如:. Php…;Jsp;*.aSP *.AsP…

名单列表绕过
如:
php2,php3,php5,phtml; aspx、ascx、
ashx、cer、asa;

特殊文件名绕过
windows文件名最后不能有.或空格,可设为 *.php. 或 *.php+

0x00截断绕过
如:*.php(0x00).jpg 或 *.php%00.jpg

利用文件包含漏洞
由于PHP包含的所有文件都会以PHP文件的形式来执行,所以当网站同时存在文件包含漏洞和文件上传功能时,即使上传处不存在上传漏洞,依旧可以结合包含漏洞获得webshell。

利用服务器解析漏洞
Apache
Apache(1.x,2.x)解析文件的原则:Apache在解析文件名的时候是从右向左读,如果遇到不能识别的扩展名则跳过,rar、gif等扩展名是Apache不能识别的,因此就会直接将类型识别为php,从而达到了注入php代码的目的。

IIS
IIS6.0下主要是有两个解析漏洞:

一个是目录解析,如果网站目录中有一个 *.asp/ 的文件夹,那么该文件夹下面的一切内容都会被 IIS 当作 asp 脚本来执行,如/xx.asp/xx.jpg;

另一个就是文件解析,IIS在解析文件名的时候会将分号后面的内容丢弃,如xx.asp;.jpg,可通过此漏洞上传shell,对服务器危害较大。

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

IIS 7.0/7.5/Nginx < 8.03,默认 Fast-CGI 开启。如果直接在 url 中图片地址(.jpg)后面输入/.php,会把正常图片解析为 php 文件。本质:PHP CGI的漏洞,在PHP的配置文件中有一个关键的选项cgi.fix_pathinfo在本机中位于C:wampbinphpphp5.3.10php.ini,默认是开启的,当URL中有不存在的文件,PHP就会向前递归解析。

Nginx
空字节漏洞,影响版本:0.5、0.6、0.7<=0.7.65、0.8<= 0.8.37

也就是当Fast-CGI执行php时,http://127.0.0.1/1.jpg.php 会把1.jpg文件(木马文件)当做php文件来执行。

.htaccess文件攻击
.htaccess文件
.htaccess文件(或者"分布式配置文件"),全称是Hypertext Access(超文本入口)。提供了针对目录改变配置的方法, 即,在一个特定的文档目录中放置一个包含一个或多个指令的文件, 以作用于此目录及其所有子目录。网页301重定向、自定义404错误页面、改变文件扩展名、允许/阻止特定的用户或者目录的访问、禁止目录列表、配置默认文档等功能。

若是如下的.htaccess配置,则可以令所有的文件都以PHP文件来执行:

<FileMatch "a.jpg"> SetHandler application/x-httpd-php </FilesMatch>

其他检测机制

  • 先上传后检测

原理
一开始允许任意上传文件,上传后再检测,若是恶意文件则删除;

绕过
条件竞争绕过,通过上传一个内容为生成一句话木马的脚本,由于服务器检测出文件为恶意文件并删除存在极短的时间差,利用burpsuite或其他工具不断的上传和访问该文件,若是竞争成功,则会生成一句话木马从而躲避了服务器的检测。

文件上传漏洞防御方案

  • 文件类型、后缀、大小、路径检测
    • 前端验证:js 验证文件扩展名、大小
    • 后端检测:MIME 类型,设置白名单、重命名上传文件
  • 文件内容解析
  • 文件上传目录修改权限
    • 无执行权限
    • 读写权限分离
  • 最小权限运行服务器
  • 优化php.ini配置

靶场通关

靶场链接

第一关

可以看到第一关的描述是上传一个 php 文件然后获取/home/carlos/secret中的通关密码

  • 首先找到文件上传点

点击 My account 使用改题目提供的账号密码 wiener:peter 登录即可找到上传点

  • 上传一个 php 文件试试看
<?php echo file_get_contents('/home/carlos/secret'); ?>
// file_get_contents() 把整个文件读入一个字符串中

发现上传成功,说明目前上传的文件没有被限制

  • 访问(执行)刚刚上传的文件

由于刚刚上传的是个「头像」,所以我们回到个人主页看看这个「头像」保存在哪里
使用 F12 大法轻松找到

访问该链接

可以发现访问成功,说明文件确实是成功上传并且可以利用的

  • 提交

复制ofpXe8nN1aQxTJa77y3in64iinYKNMyD
点击 submit solution 并提交

第二关

第二题的要求和第一题的一样,但是从题目上看就知道对「文件上传」加了限制,需要修改文件类型

  • 首先我们测试一下 php 文件能不能上传成功

依旧是先登录 wiener:peter 然后上传文件:

<?php echo file_get_contents('/home/carlos/secret'); ?>
// file_get_contents() 把整个文件读入一个字符串中


发现不行,并且提示文件类型不符合(白名单规则)

  • 使用 burp 抓包修改请求头信息

我们回到上传页面,再次 upload,通过 burp 的拦截功能修改请求头信息

通过修改 Content-Type 属性的值,将其改为白名单内的类型:image/png
可以发现上传成功了

  • 和题 1 一样访问(执行)上传的 php 文件并提交答案


第三关

题目提示是需要通过目录遍历来完成的

  • 上传 php 文件

和 1、2 关一样,我们先上传文件:

<?php echo file_get_contents('/home/carlos/secret'); ?>
// file_get_contents() 把整个文件读入一个字符串中

可以发现文件轻松上传了

  • 找到文件所在处并执行该文件

按照前面两关的思路,我们先找到该图片显示的地方,试图找到一些路径的线索
但当我们通过 F12 找到路径并访问时,却发现上传的文件以文本的形式显示了,并没有被执行

说明访问该目录下的文件,回显信息是以文本的形式展现的,并不会直接执行该文件
因此我们考虑把文件不上传到这而是上传到别处,比如它的父级目录

+使用 burp 修改文件上传目录

先把上传文件的 POST 请求包抓到并送去 repeater

在 Content-Disposition 中找到 filename,并将其值添加../
发现似乎路径并没有被修改,../被吞掉了一样

对/进行 URL 编码
发现../显示了出来

我们再按照前几关的方法,通过 F12 找到路径,并且访问一下该文件

ok,php 文件执行成功,可以提交答案了

第四关

题目描述信息是通过绕过黑名单进行文件上传

  • 先直接上传 php 文件测试一下

可以发现,我上传的 bpup04.php 文件上传失败,并提示不能上传 php 文件

  • 使用 burp 尝试绕过文件类型限制,再上传.htaceess使该文件以 php 的形式执行

我们将刚刚发送的 POST 请求发送到 repeater

修改 Content-Disposition,把filename值改为 bpup04.png
修改 Content-Type,把 text/php 改为 image/png
发送

上传成功

上传一个.htaccess文件(命名就是.htaccess)

<FilesMatch "bpup04.png">
SetHandler application/x-httpd-php   
AddHandler php5-script .png
</FilesMatch>
  • 访问该文件

可以提交答案了

第五关

通过混淆文件扩展名上传

  • 尝试一下

直接上传 php 文件,不行
修改成Content-Type: image/png 不行
上传.htaccess文件,不行
初步判断这里使用了白名单

  • 使用%00截断绕过

如图修改参数

上传成功

访问一下

可以提交了

第六关

这关检测的是上传文件开头,查看是否符合要求

  • 尝试一下
    直接上传个 php 文件,不行

同时这里提到了not a valid image
尽管我们把文件后缀改成.png,修改Content-Type也是无效的

说明这里确实是检测了上传文件
我们再尝试一下把普通的 png 图片修改后缀为 php 文件再上传

发现上传成功,也就是说这里并没有其他的限制,只是单纯地检测文件上传内容,而且应该使用了文件幻数检测

  • 伪造含有图片开头信息的 php 文件并上传
    在命令行使用该命令将一张普通的图片和我们需要的 php 文件输出为含有图片文件开头信息标志的 php 文件
cat aaa.png bpup06.php > bpupp06.php

我们把生成的文件上传

上传成功

  • 访问文件
    最后面一小串就是我们需要的答案了

第七关

题目描述是使用条件竞争绕过,通过上传一个内容为生成一句话木马的脚本,由于服务器检测出文件为恶意文件并删除存在极短的时间差,利用burpsuite或其他工具不断的上传和访问该文件,若是竞争成功,则会生成一句话木马从而躲避了服务器的检测

<?php echo file_get_contents('/home/carlos/secret'); ?>

上传肯定是失败的了
我们再随便上传一个 png 图片,再访问这个图片
由此我们得到 POST 请求和 GET 请求
我们先在 Burp 的 BAPP下载 Turbo Intruder

然后把上传 php 文件的 POST 请求发送到 Turbo Intruder

我们再在 Turbo Intruder 下方构造脚本,并把 POST 请求和 GET 请求内容粘贴过来
需要注意的是 POST 请求为上传的php文件请求,GET 为访问该 php 文件的请求(修改头部为GET /files/avatars/bpup07.php HTTP/1.1)

def queueRequests(target, wordlists):
    engine = RequestEngine(endpoint=target.endpoint, concurrentConnections=10,)

    request1 = '''你的POST请求,不需要把三引号去掉,最后一行需要有一行空行'''

    request2 = '''你的GET请求,不需要把三引号去掉,最后一行需要有一行空行'''

    # the 'gate' argument blocks the final byte of each request until openGate is invoked
    engine.queue(request1, gate='race1')
    for x in range(5):
        engine.queue(request2, gate='race1')

    # wait until every 'race1' tagged request is ready
    # then send the final byte of each request
    # (this method is non-blocking, just like queue)
    engine.openGate('race1')

    engine.complete(timeout=60)


def handleResponse(req, interesting):
    table.add(req)

点击底部的Attack

状态为 200 的就是答案了

posted @   游标卡卡尺  阅读(347)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 零经验选手,Compose 一天开发一款小游戏!
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!
点击右上角即可分享
微信分享提示