文件上传与upload-labs

文件上传

漏洞成因: 具备上传文件功能的Web等应用,未对用户选择上传的文件进行校验,使得非法用户可通过上传可执行脚本而获取应用的控制权限。
防护与绕过: 通过upload-labs靶场,了解更多的防护与绕过手段。

upload-labs

Pass-01(前端验证)
  1. 上传php一句话,弹窗提示。F12发现表单提交时的js验证。
    在这里插入图片描述
  2. 类型不是.jpg|.png|.gif时,验证失败,返回false,直接修改表单处,相当于返回true。
    修改前:
    在这里插入图片描述
    修改后:
    在这里插入图片描述
  3. 上传成功,使用蚁剑连接。
    在这里插入图片描述
    在这里插入图片描述
Pass-02(MIME验证)
  1. 上传php一句话,网页加载(有发送请求)后,提示如下。
    在这里插入图片描述
  2. 使用BurpSuite抓包,抓取上传图片的包和上传木马的包,比较后发现Content-Type的差别。
    在这里插入图片描述
    在这里插入图片描述
  3. 修改上传木马包的Content-Typeimage/jpeg后放包,上传成功,使用蚁剑连接。
Pass-03(黑名单验证,特殊后缀)
  1. 上传一句话,网页加载(有发送请求)后,提示如下,推测为黑名单验证。
    在这里插入图片描述
  2. 黑名单不全,可以使用大小写、‘.’号(如:1.php.)、特殊字符(如:1.php::$DATA)、Apache文件后缀解析(1.php.xxx为1.php)等方式绕过,但查看源码后,发现都给过滤掉了。在这里插入图片描述
  3. 使用一些特殊的后缀。
    php:php3、php4、phtml
    jsp:jspx、jspf
    asp:asa、cer
    使用特殊后缀之后,发现还是不行,返回结果为空,测试<?php phpinfo();?>也读不出来。看wp和百度后,是由于环境是phpstudy搭建的,里面把后缀给限制了,所以改一下httpd.conf文件里的#AddType application/x-httpd-php .php .phtmlAddType application/x-httpd-php .php .phtml .php3 .php4
    记得去掉#号。
    此处没有过滤.htaccess,可以先上传内容为:SetHandler application/x-httpd-php的.htaccess文件,含义:将所有文件解析为php。然后上传个jpg格式的一句话也可以绕过了
  4. 上传1.php4,成功后查看图像地址,然后再使用蚁剑连接。在这里插入图片描述
Pass-04(黑名单验证,.htaccess)
  1. 与Pass-03相似,还是黑名单,过滤得更多。
  2. 上传.htaccess文件后,上传任意后缀包含一句话的马,连接上。
Pass-05(黑名单验证,.user.ini.)
  1. 与前两关相似,再过滤了.htaccess,但是还有个ini配置文件可以利用。
  2. 先上传一个以auto_prepend_file=1.gif为内容的.user.ini文件,然后再上传一个内容为php的一句话的脚本,命名为1.gif.user.ini文件里的意思是:所有的php文件都自动包含1.gif文件。.user.ini相当于一个用户自定义的php.ini。
  3. 提示是:存在readme.php这个文件。
    在这里插入图片描述
  4. 复制图像地址后,将文件名改为readme.php,然后密码设置为一句话的密码,蚁剑连接成功。
    注:这里一开始我用的是phpstudy的2016版本,但是两个文件上传上去后1.gif还是没有给包含在readme.php里(使用蚁剑连接不上),然后后来换了个v8.1版本的,上传后可以成功连上。后来要手动配置2016版的php扩展设置,在选项那里输入1.gif后,给包含,但相当于服务端改了php.ini文件,利用方法不现实。为什么2016版的.user.ini没有给php.ini扫描到呢?希望得到解决。
    在这里插入图片描述
Pass-06(黑名单验证,大小写绕过)
  • 去除对文件后缀名的转为小写设定,直接选择后缀名为.Php等非黑名单中存在的格式上传。
Pass-07(黑名单验证,空格绕过)
  • 去除了对文件后缀名的空格过滤,选择后缀为.php的一句话上传,抓包后在后面增加空格,成功绕过。(空格不明显)在这里插入图片描述
Pass-08(黑名单验证,点号绕过)
  • 去除了对文件后缀名的点号过滤,选择选择后缀为.php的一句话上传,抓包后在后面增加点,成功绕过。在这里插入图片描述
Pass-09(黑名单验证,特殊字符::$DATA绕过)
  • 去除了对字符串::$DATA的过滤,选择选择后缀为.php的一句话上传,抓包后在后面增加::$DATA,上传成功。
    在这里插入图片描述
    注:复制图像地址时,会附带::$DATA,要去掉后再连接,否则找不到文件。
Pass-10(黑名单)
  1. 查看源码后,发现其路径拼接的是$file_name而不是$file_ext,而$file_name只处理了文件名末尾的点。在这里插入图片描述
  2. 选择后缀为.php的一句话上传,抓包后构造文件后缀为php. .,成功绕过,再使用蚁剑连接。在这里插入图片描述
Pass-11(黑名单验证,双写绕过)
  • 查看源码后,发现其对存在黑名单中的字符进行替换,但str_ireplace()函数只替换一次,因此修改文件名为1.pphphp后成功绕过。在这里插入图片描述
Pass-12(白名单验证,0x00截断)
  1. 查看源码和提示,上传路径可控,并且是最终文件的存放位置是以拼接的方式,可以使用%00截断,但需要php版本<5.3.4,并且magic_quotes_gpc关闭。原理是:php的一些函数的底层是C语言,而move_uploaded_file就是其中之一,遇到0x00会截断,0x表示16进制,URL中%00解码成16进制就是0x00。
    在这里插入图片描述
  2. 修改一句话的后缀名为.jpg后上传,抓包后修改URL,成功上传,复制图像地址后去掉php后的部分,然后用蚁剑连接。
    在这里插入图片描述
Pass-13(白名单验证,0x00截断)
  1. 与Pass-12的区别是这里使用POST传地址,POST不会对里面的数据自动解码,需要在Hex中修改。
    在这里插入图片描述
  2. 修改一句话的后缀名为.jpg后上传,抓包后,修改路径处,增加1.php,然后选择Hex后,找到增加的1.php位置,将p后的0d(不同字符不同)修改为00后放包上传。同样复制图像地址后去掉php后的部分,然后用蚁剑连接。
    在这里插入图片描述在这里插入图片描述在这里插入图片描述
Pass-14(白名单验证,图片马)
  1. 上传图片马,查看源码后,意思是:读取上传文件的前两个字节内容,unpack解码后,使用intval转换为10进制,默认为10进制,根据转换后的结果判断图片类型。
    在这里插入图片描述
  2. 制作图片马,copy 1.jpg/b + 1.jpg/a 2.jpg,2.jpg就是生成的图片马。
  3. 上传成功后,利用文件包含解析图片马里的php脚本,file为我们的图片马位置。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
Pass-15(白名单验证,图片马)
  • 与Pass-14相同,上传同个图片马就可以。相关函数说明:
getimagesize(string $filename [,array &$imageinfo])//获取图像信息,返回一个数组
/*
返回的数组中,索引0:图像宽度像素值
			 索引1:图像高度像素值
			 索引2:图像类型,1=GIF,2=JPG,3=PNG,4=SWF,5=PSD,6=BMP,7=TIFF_II,8=TIFF_MM,9=JPC,10=JP2,11=JPX,12=JB2,13=SWC,14=IFF,15=WBMP,16=XBM,17=ICO,18=COUNT
			 索引3:图像宽度和高度的字符串
			 索引bits:图像的每种颜色的位数,二进制格式
			 索引channels:图像的通道值
			 索引mime:图像的MIME信息
*/
image_type_to_extension(int $imagetype [,bool $include_dot = TRUE])//获取图像类型的文件扩展名
/*
include_dot是否在扩展名前加点。默认为TRUE
*/
Pass-16(白名单验证,图片马)
  • 与Pass-14相同,上传同个图片马就可以。相关函数说明:
    此函数需要开启php_exif模块
exif_imagetype(string $filename)//读取一个图像的第一个字节并检查其签名
Pass-17(白名单验证,图片马+二次渲染)
  1. 验证过程:判断后缀与MIME类型是否符合要求,符合后生成新图像(内容不正确会失败,返回false,相当于多了一次验证),生成新图像失败就unlink删除,成功就根据系统时间给文件命名,再通过imagejpeg类似函数使用原图像资源创建新图像(二次渲染)。相关函数说明:
basename(string $path [,string $suffix]) //返回路径中的文件名部分
imagecreatefromjpeg(string $filename)
imagecreatefrompng(string $filename) 
imagecreatefromgif(string $filename) //由文件或URL创建一个新图像,内容不对则失败返回false,成功后返回图像资源
srand([int $seed ]) //用seed播下随机数发生器种子
strval(mixed $var) //返回字符串类型的var
imagejpeg(resource $image [,string $filename [,int $quality]])//从image图像以filename为文件名创建一个JPEG图像
imagepng(resource $image [,string $filename]) //从 image 图像以filename为文件名创建一个PNG图像或文件
imagegif(resource $image [,string $filename]) //从 image 图像以filename为文件名创建一个GIF图像或文件
  1. 绕过过程:这里用gif,容易绕过二次渲染,上传图片马2.gifcopy 1.gif/b + 1.php/a 2.gif,然后另存上传的图片马,使用查看被渲染后哪里保持不变,将一句话插入到不变的位置中去,保存图片马,再次上传,复制新上传的图像位置,使用蚁剑连接成功。在这里插入图片描述
Pass-18(白名单验证,条件竞争)
  1. 验证过程:服务器先将上传的文件保存在临时目录中,然后再对后缀名进行白名单验证,并重命名。
rename(string $oldname,string $newname [,resource $context])//把oldname重命名为newname 
  1. 绕过过程:不断上传文件,在文件还没被删除前去读取文件,若上传内容为<?php fputs(fopen('2.php','w'),'<?php @eval($_POST["pass"])?>');?>,则还没被删除前去读取文件,解析之后会写入一个内容为<?php @eval($_POST["pass"])?>2.php文件。使用BurpSuite的Intruder不断上传文件并不断访问所上传的文件。
    注:"pass"一定要双引号,不然单引号之间乱了。
  2. 观察到有200的响应包,用蚁剑连接。2.php
    在这里插入图片描述
Pass-19(白名单验证,条件竞争)
  1. 验证过程:依次检查文件是否存在、文件名是否可写、检查后缀(白名单)、检查文件大小、检查临时文件存在、保存到临时目录里、然后再重命名。与Pass-18存在同样的条件竞争。不同的是这里先检查了后缀,所以要上传符合白名单里的才能进行。
  2. 任务是上传webshell,但是不知道怎么做,可以通过之前的文件包含和Pass-18的条件竞争,上传一个图片马,然后不断访问,写入一个webshell。
  3. 怎么才能直接上传webshell???
Pass-20(黑名单验证,点号绕过)
  1. 同样是上传路径可控,可以使用和Pass-13同样的方式绕过。不同的是这里的黑名单,可以文件名称保存的时候,加上.,最末的.号使得pathinfo()获取到的PATHINFO_EXTENSION为空,从而绕过黑名单。
pathinfo(string $path [,int $options = PATHINFO_DIRNAME | PATHINFO_BASENAME | PATHINFO_EXTENSION | PATHINFO_FILENAME]) 
/*
返回一个关联数组包含有path的信息。返回关联数组还是字符串取决于options
PATHINFO_DIRNAME:文件所在目录
PATHINFO_BASENAME:文件+后缀名
PATHINFO_EXTENSION:后缀名
PATHINFO_FILENAME:文件名
*/
  1. 上传,再用蚁剑连接。
    在这里插入图片描述
    在这里插入图片描述
Pass-21(白名单验证,数组绕过)
  1. 验证过程:先检查MIME,通过后检查文件名,保存名称为空的就用上传的文件名。再判断文件名是否是array数组,不是的话就用explode()函数通过.号分割成数组。然后获取最后一个,也就是后缀名,进行白名单验证。不符合就报错,符合就拼接数组的第一个和最后一个作为文件名,保存。
explode(string $delimiter , string $string [, int $limit])//返回由字符串组成的数组,每个元素都是string的一个子串,它们被字符串delimiter作为边界点分割出来 
reset(array &$array)//将数组的内部指针指向第一个单元
  1. 绕过过程:绕过MIMIE,改一下包的Content-Type,为了绕过explode()函数,需要传入数组,绕过白名单,由于取的是end()也就是数组最后一个,需要传入数组的最后一个为jpg|png|gif,最后是拼接文件名,取的是reset()第一个,即索引为0,和索引count()-1(数组内元素个数-1)。所以令索引0为1.php,索引2为jpg(只要是索引1之后都可),这样数组元素个数为2,拼接的就是索引0和索引1,也就是1.php和空,结果还是1.php,这样就可以使得拼接后的文件名为1.php。如下:在这里插入图片描述在这里插入图片描述
    在这里插入图片描述
用到的部分函数说明
$_FILES['file']['name'] //客户端上传的文件原名称,含扩展名
$_FILES['file']['type'] //上传的文件类型
$_FILES['file']['tmp_name'] //文件上传后,在服务器端存储的临时文件名
stripos(string $haystack,string $needle [,int $offset = 0])// 查找字符串首次出现的位置(不区分大小写)
strrpos(string $haystack,string $needle [,int $offset = 0])//计算指定字符串在目标字符串中最后一次出现的位置
move_uploaded_file(string $filename,string $destination) //文件上传后先存储于服务器的临时目录中,使用该函数移动文件位置
substr(string $string,int $start [,int $length]) //返回字符串string中从位置start处后的长度为length部分
strrchr(string $haystack,mixed $needle) //返回haystack字符串中的一部分,这部分以needle的最后出现位置开始,直到haystack末尾
in_array(mixed $needle,array $haystack[,bool $strict = FALSE])//在数组haystack中搜索是否存在值needle,strict若设置TRUE,则类型也会匹配

总结

防御
  1. 黑白名单;
  2. 对上传的文件重命名,不易被猜测;
  3. 对上传的内容进行读取检查;
  4. 不要暴露上传文件的位置;
  5. 禁用上传文件的执行权限;

不同系统有不同的需求,根据系统需求制定特定的防御手段。

  • 16
    点赞
  • 43
    收藏
    觉得还不错? 一键收藏
  • 13
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值