文件上传漏洞 笔记
Pikachu靶场
Client check
发现只能上传图片。
方法一
利用抓包软件Burpsuite。
先将要传入的phpinfo文件的后缀改为图片格式。
开启抓包软件
抓包成功后,将文件后缀名改回 .php
上传成功
验证一下
成功
方法二
首先查看网页源代码
发现这段代码限制了文件的上传
找到该函数并删掉
成功选择
MIME type
方法一
同 client check 利用抓包软件,修改文件名后缀
方法二
首先了解一下$_FILES函数 和 MIME 机制
*$_FILES[‘myFile’][‘name’] 客户端文件的原名称。 *
*$_FILES[‘myFile’][‘type’] 文件的 MIME 类型,需要浏览器提供该信息的支持,例如”image/gif”。 *
*$_FILES[‘myFile’][‘size’] 已上传文件的大小,单位为字节。 *
*$_FILES[‘myFile’][‘tmp_name’] 文件被上传后在服务端储存的临时文件名,一般是系统默认。可以在php.ini的upload_tmp_dir 指定,但 用 putenv() 函数设置是不起作用的。 *
*$_FILES[‘myFile’][‘error’] 和该文件上传相关的错误代码。[‘error’] 是在 PHP 4.2.0 版本中增加的。下面是它的说明:(它们在PHP3.0以后成了常量) *
MIME*机制 使允许邮件处理文本,图片等多个不同类型的数据。*
MIME* 来描述标记数据类型**,在MIME扩展中使用一种叫 *多部分对象集合 的方法 来容纳多份不同类型的数据
多部分对象集合的对象有:
multipart/from-data
该对象在Web 表单文件上传时使用.
multipart/byteranges
该对象在 状态码 206响应报文包含了多个范围时使用。
PS:
在HTTP报文中使用多部分对象集合时,需要在首部字段里加上Content-type。
使用 boundary 字符串来划分多部分对象集合指名的各类实体。
例:
开始做题
查看源代码
此处用到了一个自定义的 upload_sick 函数 ,转到定义
从此处 我们可以看到 该网页做判断的方法是 通过$_FILES 函数获取表单上传的 文件MIME类型 。
而该函数获取方式 是从报文中获取信息
所以利用抓包软件修改请求报文
上传成功
Getemagesize
getimagesize函数
getimagesize():它是php提供的,通过对目标文件的16进制进行读取,通过该文件的前面几个字符串,来判断文件类型。
getmagesize()返回结果中有文件大小和文件类型。
了解 文件头 和 文件包含漏洞 的一些相关知识
对于 不同格式的 图片文件,以二进制格式打开,其首部四个字节是用来标识文件格式
以 png 格式为例(引用了一下 胡宇轩 的笔记)
我们将一个png格式的图片文件用 010 editor打开后在它的文件头之后会有一个png文件的特有的数据块,每一个部分都有着它特殊的含义
而IHDR所有内容的表示按照顺序是这样表示的:
4字节宽度,4字节高度,1字节位深度,1字节颜色类型,1字节压缩方法,1字节滤波方法,1字节隔行扫描方法
我们首先看前面 89 50 4E 47 丨 0D 0A 1A 0A 这八个字节所表示的就是PNG文件特有的二进制下的文件头,有他就表示有PNG文件。
文件包含漏洞,在 PHP中,当使用include() 函数包含文件时,只有代码执行到 include() 函数时才将文件包含进来,发生错误时只给出一个警告,继续向下执行。
开始做题
这题 我们首先试试用抓包软件修改文件后缀名看看能不能过
发现行不通
看看源代码,一步步找
这时我们 可以** 伪造文件头**
通过系统自带的 copy指令
生成的图片文件会在末尾接上代码
上传成功
想要使代码运行,需要使用到 文件包含漏洞
在最下面发现代码成功运行。
DVWA
LOW
没什么好说的,可以直接上传.php文件
打开看一看,成功运行
MEDIUM
利用burpsuite 抓包改文件后缀名
成功上传
检查一下,没问题。
然后 又改了一下 content-type ,发现还是可上传,猜想是根据MIME来验证
See see 源代码
利用了文件的MIME类型进行判断。
High
直接抓包改后缀名显然不成功
看看源代码
利用和pikachhu的getemagesize的方法
先伪造木马头,上传后 利用 low 的文件包含漏洞打开运行代码
up-loadlabs
pass-01
直接在浏览器中将 onsubmit属性给删了
上传成功
上传文件保存路径通过响应头得到是在 upload-labs\upload
测试一下
成功
或者
利用抓包软件改文件后缀名也行
pass-02
用burpsuite抓下包,修改 首部content-type。
上传成功
pass-03
先提一嘴 黑名单验证
后缀名检测:判断文件后缀名的检测方式。
后缀名检测的两种方式:黑名单与白名单。
黑名单:定义上传的文件格式,如”php、jsp、zip、rar、tar等格式”,如果上传文件触发该规则,一律拦截不允许上传。
白名单: 定义上传的文件格式,如”jpg、jpeg、png、gif、ico等格式”,如果上传文件不符合该规则,同样一律拦截不允许上传。
look look源码
限制了后缀名,但是限制不严格
可以用 .phtml .phps .php5 .pht 来进行绕过
上传一个 .phps 文件
成功上传。
另外,想要解析.phps等这类文件。需要在 apache 的 httpd.conf 中加一行 AddType application/x-httpd-php .php .phtml .phps .php5 .pht,否则显示空白页
pass-04
先了解 htaccess 文件
htaccess 文件是Apache服务器中的一个配置文件,他负责相关目录下的网页配置。通过htaccess文件,可以帮助我们实现:网页301重定向,自定义404错误页面,改变文件扩展名,允许或组织特定的用户或者,目录的访问,禁止目录列表,配置默认文档等功能。
.htaccess文件只能用于apahce,不能用于iis和nginx等中间件
找到的一个参考网页apache .htaccess文件详解和配置技巧总结
开始做题
看看源代码
列了一大堆 黑名单。改后缀名 是行不通了。
面对这种情况,我们可以 上传一个 .htaccess配置文件,
新建一个记事本,写入内容
该段代码的意思是 当遇上文件名为 “1.jpg”的文件时,将其解析为.php文件
保存为.htaccess文件,上传该文件
上传成功
在上转保存为 .jpg 文件的php文件,打开即可
由于 版本问题,我的无法显示
pass-05
前置知识
类似于.htaccess可以覆盖apache的配置文件,而.user.ini则可以覆盖php.ini的配置.
Ps:
.user.ini只能用于Server API为FastCGI模式下,而正常情况下apache不是运行在此模块下的
.htaccess和.user.ini都只能用于访问本目录下的文件时进行覆盖。
然鹅 .user.ini 并不能覆盖所有php.ini 属性,php.ini 属性分为四类:
PHP_INI_USER 可在用户脚本(例如 ini_set())或 Windows 注册表(自 PHP 5.3 起)以及 .user.ini 中设定
PHP_INI_PERDIR 可在 php.ini,.htaccess 或 httpd.conf 中设定
PHP_INI_SYSTEM 可在 php.ini 或 httpd.conf 中设定
PHP_INI_ALL 可在任何地方设定
但是实际上,只要不是**PHP_INI_SYSTEM模式下的属性,均可以在.user.ini**中设置
第一个为后置,第二个为前置
开始做题
查看源代码
发现相较于pass4,把.htaccess屏蔽掉了。
但是我们可以转入 .user.ini 文件,在文件中写入 auto_prepend_file=1.gif
传入后,再转入木马文件,将文件名修改为 1.gif
注意: 在访问php文件时,才会自动包含1.gif
upload文件夹中自带了一个 readme.php 文件,打开该文件,代码成功运行
pass-06
大小写过滤不严格
利用burpsuite 抓包改后缀名
成功传入。
用php 7.3.4打不开,有大小写敏感,换到5.3.29就能打开了
pass-07
看看源代码,发现妹有 首尾去空。
缺少
于是利用burpsuite抓包,后缀名加空格
上传成功
测试一下
这个操作也只有PHP5才行的通
pass-08
看看源码
没有删除点的操作,缺少
于是利用burpsuite抓包,后缀名加点
上传成功
测试一下
成功
该操作也是只有在PHP5下行得通
pass-09(*)
了解一下:
如果文件名+“::$DATA”会把::$DATA之后的数据当成文件流处理,不会检测后缀名,且保持::$DATA之前的文件名,目的就是不检查后缀名
例如”webshell.php::$DATA”Windows会自动去掉末尾的::$DATA变成”webshell.php”
看看源码
发现没有去除字符串::$DATA
还是抓包改后缀名
上传成功
测试一下
成功,这个 PHP5 和PHP7 都行得通
pass-10
看看源码
该过滤的全都有
但是,都是单次过滤
于是抓包改文件名加 “. .“过滤后剩一个点
上传成功
测试一下
成功,但只有在PHP5过的去,PHP7 move_uploaded_file($temp_file, $img_path 函数过不去
pass-11
注意:str_ireplace() 函数 是从前往后找
看看源码
意思是 将 $deny_ext 中的敏感词全部过滤掉一遍,
所以 将后缀名 改为 “.pphphp”,写两遍
上传成功
测试一下
pass-12
先了解 %00截断
在url中%00表示ascll码中的0 ,而ascii中0作为特殊字符保留,表示字符串结束,所以当url中出现%00时就会认为读取已结束
再补充两个函数
strrpos() 函数:查找字符串在另一字符串中最后一次出现的位置。
例:查找 “php” 在字符串中最后一次出现的位置:
1 | <?php |
注意:strrpos() 函数对大小写敏感
substr() 函数:返回字符串的一部分。
从字符串中返回 “world”:
1 | <?php |
注意:如果 start 参数是负数且 length 小于或等于 start,则 length 为 0。
开始做题
先得把PHP版本调到5.3下,然后把magic_quotes_gpc关闭
看看源代码
看到是用get方法得到初始路径
利用burpsuite抓包
这样改
然后就上传成功了
基本思路是这样,我由于版本高了实现不了,不做展示
pass-13
看看源代码
思路与pass12思路一样
但由于是采用的是post方法,所以要将**%00进行url编码后**加到末尾
pass-14
利用文件头伪造知识
跟pikachu靶场的第三题差不多
开始做题,看看源代码,发现读取文件前两个字节来做类型判断。
于是利用系统自带的copy指令 合成一个木马图片。
转入木马图片
成功上传。
网站自带一个文件包含漏洞
于是在地址栏输入 include.php?file=upload/服务器中文件名
成功
pass-15
方法与 pass-14 相同,只是判断方式 变成了 用 getimagesize()函数,前面讲过,不再赘述。
pass-16
了解 exif_imagetype 函数
exif_imagetype
(PHP 4 >= 4.3.0, PHP 5, PHP 7, PHP 8)
exif_imagetype — 判断一个图像的类型
说明** **:
exif_imagetype(string $filename): int|false
exif_imagetype() 读取一个图像的第一个字节并检查其签名。
本函数可用来避免调用其它 exif 函数用到了不支持的文件类型上或和 $_SERVER[‘HTTP_ACCEPT’] 结合使用来检查浏览器是否可以显示某个指定的图像。
源码:
用这个函数需要先把php配置改一下
pass-17
首先了解** 二次渲染**
二次渲染原理
在我们上传文件后,网站会对图片进行二次处理(格式、尺寸要求等),服务器会把里面的内容进行替换更新,处理完成后,根据我们原有的图片生成一个新的图片并放到网站对应的标签进行显示。
由Gif文件或 URL 创建一个新图象。成功则返回一图像标识符/图像资源,失败则返回false,导致图片马的数据丢失,上传图片马失败
开始做题
看看源码
发现后端采用了二次渲染手段
我们尝试一下上一题的手段
哒咩!行不通。
于是把原图和服务器修改过的图片进行比较,看看哪个部分没有被修改(利用脚本)。将php代码放到没有被更改的部分,配合包含漏洞
对于GIF 的上传,只需要判断没有修改的位置,然后将php代码添加即可
对于PNG的上传,需要修改PLTE数据块或者修改IDAT数据块,
该文章介绍了几种不同格式图片的绕过方式:【文件上传绕过】——二次渲染漏洞
将增强 后的图片再进行上传,结合文件上传漏洞,即可成功
pass-18
先了解 条件竞争(爆破)
条件竞争是指一个系统的运行结果依赖于不受控制的事件的先后顺序。当这些不受控制的事件并没有按照开发者想要的方式运行时,就可能会出现bug。尤其在当前我们的系统中大量对资源进行共享,如果处理不当的话,就会产生条件竞争漏洞
就是如果并发处理不当或相关操作逻辑顺序设计的不合理,导致出现了漏洞
开始做题,看看源码
这里的move_uploaded_file()函数在外面,意思是先上传后判断,于是我们可以利用这个上传的间隙去执行php文件。
然后进行爆破
我是根据这篇文章里的步骤来的【文件上传绕过】——条件竞争漏洞
然后爆了好久出不来。。。
不过方法是会了
pass-19
和pass-18一样
但是做了文件头的检测,上传改为图片格式上传即可
pass-20
先看看源码
多了一个保存名字
方法一(版本低于5.3)
据源码,$img_path 是由$file_name组成,$flie_name又由 ‘savename’ 控制。
因此可以抓包使用 00截断
方法二
move_uploaded_file()有一个 忽略掉文件末尾的 /. 的特性。
抓包改 savename
成功上传
测试一下
成功
pass-21
先了解几个函数
explode() 函数
定义和用法
explode(separator,string,limit)
explode() 函数使用一个字符串分割另一个字符串,并返回由字符串组成的数组。
注释:“separator” 参数不能是一个空字符串。
注释:该函数是二进制安全的。
end() 函数:输出数组中最后一个元素的值
count() 函数****:返回数组中元素的数目
看看源码
利用了白名单验证方法,
检测了MIME类型 -》修改 content-type
然后是对svename 进行分割,使其成为数组来验证,又因为是把最后一个元素拿出检测(end($flie))
所以预先抓包将 savename 作为数组上传
上传成功
测试一下
成功