Upload-labs

一点小建议:其中若是有文件传输上去但是蚁剑却连接不上去的可以试一下线上的靶场;因为本地自己搭建的靶场有时候会出现环境配置不符的问题;本人就是被这个环境配置的问题折磨了好久。
线上靶场地址:https://www.ctfer.vip/problem/11

文件上传漏洞攻击原理

上传攻击的原理:
在针对Web的攻击中,攻击者想要取得webshell最直接的方式就是将Web木马插入服务器端进行成功解析。那么如何理解成功解析?假设目标服务器为用PHP语言构建为Web系统那么针对上传点就需要利用PHP木马并且要求木马在服务器以后缀名.php进行保存,因此上传木马的过程就是在Web系统中新增一个页面。当木马上传成功后攻击者就可远程访问这个木马文件,也就相当于浏览一个页面只不过这个页面就是木马具备读取、修改文件内容、连接数据库等功能。在文件上传的功能处,若服务端脚本语言未对上传的文件进行严格证和过滤,导致恶意用户上传恶意的脚本文件时就有可能获取执行服务端命令的能力,这就是文件上传漏洞。

上传漏洞满足条件:
首先,上传的文件能够被web容器解释执行。所以文件上传后所在的目录要是web容器所覆盖到的路径。
其次,用户能够从web访问这个文件。如果文件上传了,但用户无法通过web访问,或者无法得到web容器解释这个脚本,那么也不能称之为漏洞。 最后,用户上传的文件若被安全检查、格式化、图片压缩等功能改变了内容,则也可能导致攻击不成功。
高危出发点:
相册、头像上传视频、照片分享附件上传(论坛发帖、邮箱)文件管理器。

哈哈哈哈哈哈哈我发现这种导图可以帮助我更好的理解;所以我就把它们copy进我的博客了

初探一句话木马原理&&蚁剑连接原理

最简单的一句话木马:(不知道为什么我写入一句话木马的时候,计算机会自动帮我查杀,导致本文章无法保存
;所以这里我就不写出来啦,咱们在做sqli-labs也已经了解过了)
@:错误控制符,即使出现错误,也无视出现的错误信息,继续执行下边的代码
eval()函数:把字符串按照 PHP 代码来计算,该字符串必须是合法的 PHP 代码,且必须以分号结尾。
yyy:这个名称可以随便定义,是接受菜刀或蚁剑通过 post 方式发送过来的代码数据,也可以称为菜刀或蚁剑连接一句话木马的密码,这里设置成什么,用菜刀或蚁剑连接的时候也要用什么。
蚁剑连接分析:
当我们使用蚁剑连接之后;蚁剑会自动执行很多命令,比如文件读取命令;然后在通过可视化代码回显出来。
接下来我来看看其运作过程:
配置好蚁剑的代理后使用bp进行抓包:可以看到已经抓到蚁剑通过 POST 方式提交的数据

我们尝试进行url解码看看:

蚁剑通过连接一句话木马,每个功能操作,都是通过POST方式传递不同的执行代码给一句话木马,当一句话木马接收到蚁剑提交过来的数据(也就是代码),通过 evel 函数在目标服务器上执行,从而实现了控制目标服务器。所以,不同功能操作,传递给一句话木马的数据(代码)是不一样的,因为实现的功能不一样。

文件上传基础防御函数的学习

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
$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
if (file_exists(UPLOAD_PATH)) {
$deny_ext = array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".pht",".pHp",".pHp5",".pHp4",".pHp3",".pHp2",".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",".htaccess",".ini");
$file_name = trim($_FILES['upload_file']['name']);
$file_name = deldot($file_name);//删除文件名末尾的点
$file_ext = strrchr($file_name, '.');
$file_ext = strtolower($file_ext); //转换为小写
$file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
$file_ext = trim($file_ext); //首尾去空

if (!in_array($file_ext, $deny_ext)) {
$temp_file = $_FILES['upload_file']['tmp_name'];
$img_path = UPLOAD_PATH.'/'.date("YmdHis").rand(1000,9999).$file_ext;
if (move_uploaded_file($temp_file, $img_path)) {
$is_upload = true;
} else {
$msg = '上传出错!';
}
} else {
$msg = '此文件类型不允许上传!';
}
} else {
$msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
}
}

$_FILES[“file”][“name”] – 被上传文件的名称
$_FILES[“file”][“type”] – 被上传文件的类型
$_FILES[“file”][“size”] – 被上传文件的大小,以字节计
$_FILES[“file”][“tmp_name”] – 存储在服务器的文件的临时副本的名称
$_FILES[“file”][“error”] – 由文件上传导致的错误代码

1.trim(string,charlist):
作用:移除字符串两侧的空白字符或其他预定义字符。
语法:

1
2
3
4
5
6
7
8
9
string:必需。规定要检查的字符串
charlist 可选。规定从字符串中删除哪些字符。如果省略,则移除以下所有字符
"\0" - NULL
"\t" - 制表符
"\n" - 换行
"\x0B" - 垂直制表符
"\r" - 回车
" " - 空格
设置此函数的作用是规定要被检查的字符串,以便接下来的过滤目标

2.deldot():注:这个函数是作者自定义的函数
源码

1
2
3
4
5
6
7
8
9
10
11
12
function deldot($s){
for($i = strlen($s)-1;$i>0;$i--){
$c = substr($s,$i,1);
if($i == strlen($s)-1 and $c != '.'){
return $s;
}

if($c != '.'){
return substr($s,0,$i+1);
}
}
}

看得出来只有截取的那一位不是.的时候才会返回值,所以该函数的作用为删除文件末尾的点。
设置此函数的作用是怕攻击者使用.绕过;因为在windows服务器上会自动删除. 使得攻击者可以使用双写点号绕

3.strrchr(string,char):
作用:函数查找字符串在另一个字符串中最后一次出现的位置,并返回从该位置到字符串结尾的所有字符。
语法:

1
2
3
4
string  必需。规定要搜索的字符串。
char 必需。规定要查找的字符。如果该参数是数字,则搜索匹配此数字的 ASCII 值的字符。
例如strrchr(“abc.php”,’.’)那么回返回点及其以后的字符,那么返回值为.php
设置此函数的作用是在删除点最后的点号后返回文件后缀

4.strtolower(string) 函数把字符串转换为小写
设置此函数的目的是windows服务器不区分大小写;攻击者可以使用大写绕过防御。

5.str_ireplace(find,replace,string,count):
作用:替换字符串中的一些字符(不区分大小写)
语法:

1
2
3
4
5
find    必需。规定要查找的值。
replace 必需。规定替换 find 中的值的值。
string 必需。规定被搜索的字符串。
count 可选。一个变量,对替换数进行计数。
设置此函数的作用是防止攻击者使用一些不对文件后缀名进行检查的字符;如::$DATA;在windows服务器,如果文件名+"::$DATA"会把::$DATA之后的数据当成文件流处理,不会检测后缀名,且保持::$DATA之前的文件名,他的目的就是不检查后缀名

6.in_array(search,array,type):
作用:搜索数组中是否存在指定的值。如果在数组中找到值则返回 TRUE,否则返回 FALSE。
语法:

1
2
3
search  必需。规定要在数组搜索的值。
array 必需。规定要搜索的数组。
type 可选。如果设置该参数为 true,则检查搜索的数据与数组的值的类型是否相同。

7.date(format,timestamp):
作用:把时间戳格式化为更易读的日期和时间。
语法:

1
2
3
4
5
6
7
8
9
10
11
12
13
format  必需。规定时间戳的格式。
timestamp 可选。规定时间戳。默认是当前时间和日期。

d - 表示月里的某天(01-31)
m - 表示月(01-12)
Y - 表示年(四位数)
1 - 表示周里的某天

H-带有首位零的 24 小时小时格式
h - 带有首位零的 12 小时小时格式
i - 带有首位零的分钟
s - 带有首位零的秒(00 -59)
a - 小写的午前和午后(am 或 pm)

8.rand(min,max):
作用:返回随机整数。
语法:

1
min,max   可选。规定随机数产生的范围。

以上两个函数的作用是对我们的文件名进行一个数字替换;让我们提交的文件名和实际传输上去的文件名不符

黑名单:明确哪些文件是不允许上传的,比如asp php jsp aspx cgi war等等。
白名单:明确可以上传的文件,比如jpg png zip rar gif

黑名单(1-11)

Pass-01(绕过前端验证)

查看源码发现是前端验证;有三种方法进行攻击

法一:禁用JS。此题起过滤使用的是JS代码,而php代码在js中被读取,因此我们可以在f12审查元素中删去JS代码,或者直接禁用JS调用就可以直接上传php文件了。

缺陷:禁用了js代码,如果在实战中,网站的一些正常功能可能无法显示。
法二:使用bp进行改后缀;在txt文本中写下一句话代码;将后缀改为jpg等图片格式的后缀(因为前端只支持jpg|.png|.gif);然后上传,在上传的时候抓包,在burp里面将后缀修改为php,使其在后面能够被运行;然后利用蚁剑连接一句话木马即可。


缺陷:如果js代码是在本地运行,很可能抓不到数据包。这种方法也是不太行
法三:第三种方法是将网站源码复制下来,放到本地,然后将js代码删除。右键–>查看网站源代码–>全部复制–>创建一个记事本–>将代码放进去–>把记事本后缀名改为.html–>用Sublime Text(其他的应该也行把,因为我是用它写博客的,所以我就用它了)打开–>找到js代码–>删除


在此处添加action=”上传文件的网址”

可以在此处找到上传地址;最后用浏览器打开我们的html文件,上传php文件即可。

Pass-02(后端校验绕过Content-type验证)

查看一下源码:

发现一个新函数move_uploaded_file():
语法:move_uploaded_file(file,newloc)
file:必需;规定要移动的文件。
newloc:必需;规定新文件的位置
文件类型:MIME信息
浏览器中的header中有content-type,这就是MIME信息
常用content-type字段:
image/jpeg :jpg图片格式
image/png :png图片格式
image/gif :gif图片格式
text/plain :纯文本格式
text/xml : XML格式
text/html : HTML格式
查看一下提示发现一个叫MIME:使用MIME (Multipurpose Internet Mail Extensions) 是描述消息内容类型的因特网标准,使用MIME类型可以设定某种扩展名的文件用一种应用程序来打开的方式类型,当该扩展名文件被访问的时候,浏览器会自动使用指定应用程序来打开。
所以我们知道会对我们上传的文件进行一个类型检查;只有符合image/jpeg、image/png、image/gif才可以通过
验证
我们先上传一张普通图片然后抓包试一试

发现Content-Type: image/jpeg符合上传要求;所以我们便想到了绕过方法:
法一:写入一句话木马;然后改为jpg形式;然后在bp里面在改为php;这样就可以绕过Content-Type验证咯

2、直接上传一句话木马;然后通过抓包改Content-Type;然后放包就可以咯

Pass-03(绕过黑名单验证)

黑名单:明确哪些文件是不允许上传的,比如asp php jsp aspx cgi war等等。
白名单:明确可以上传的文件,比如jpg png zip rar gif
先查看一下源码:

trim():删去字符串前后的空白符;空白符包括:空格、制表符 tab、换行符等其他空白符等。
deldot():即从字符串的尾部开始,从后向前删除点. ,直到该字符串的末尾字符不是.为止。
strrchr():截取文件后缀,搜素并窃取搜索点及以后的位置
trtolower():把所有字符转成小写
所以该源码的意思就是先定义了了一组后缀;当我们上传了文件之后;先删去该文件名前后的空白符;然后删除文件名末尾的点;接着截取文件名后缀;然后将文件名后缀转换成小写。
因为黑名单明确不能传php为后缀的文件;但是我们还有php4/php5/phtml/php3/pht可以利用;这是因为黑名单
定义不完整导致我们可以绕过。

Pass-04(分布式配置文件攻击.htaccess)

查看提示发现基本把能过滤的都过滤了,但是我们发现.htaccess没有被过滤

分布式配置文件:.htaccess
背景:当我们使用apache部署一个网站代码准备部署到网上的时候,我们手中的apache的httpd.conf大家肯定都知道。这是apache的配置文件,然而我们大多数的网站都是基于云服务器来部署的,还有就是团队协作开发的时候,我们很难直接修改公共的httpd.conf,这时.htaccess就是httpd.conf的衍生品,它起着和httpd.conf相同的作用。
.htaccess是Apache服务器的分布式配置文件,是一个纯文本文件,它里面存放着Apache服务器配置相关的指令。该配置文件会覆盖Apache服务器的全局配置,作用域是当前目录及其子目录。
如果一个WEB应用允许上传.htaccess文件,那就意味着攻击者可以更改Apache的配置,这是十分危险的。
攻击方式:
1.将.png文件当作PHP文件执行 2.文件名中含php关键字就以PHP程序执行 3.让指定文件名的文件以PHP程序执行
工作原理:.htaccess文件(或者”分布式配置文件”)提供了针对每个目录改变配置的方法,即在一个特定的目录中放置一个包含指令的文件,其中的指令作用于此目录及其所有子目录。
三种用法:
一、让.gif以php执行
1
AddType application/x-httpd-php .gif

二、让含php关键字就以php执行
1
AddHandler php5-script php

三、让指定文件名的文件以PHP程序执行
1
2
3
<FilesMatch "ywnn">
SetHandler application/x-httpd-php
</FilesMatch>

Pass-05(分布式配置文件攻击.user.ini)


查看提示,已知该目录下有一个php文件;那么便应该想到.user.ini(.user.ini作用是所有的php文件都自动包含jpg或png或gif文件【这要看你在.user.ini里面定义的内容】)
查看题目源码,与第四关基本相同,但相比第四关,这关过滤了htaccess,还可以利用ini配置文件;
.user.ini类似于.htaccess但是应用范围更广,只要是以fastcgi运行的php都可以用它来动态的局部修改php.ini中的配置。
auto_prepend_file = //包含在文件头
auto_append_file = //包含在文件尾
这两项配置的作用是和.user.ini在同一目录的所有php文件在运行时都会自动寻找并包含配置中所指定的文件。
所以我们可以先上传一个.user.ini文件上去;然后在传一个恶意文件上去。但是我在本地进行复现时发现不行,
很奇怪不知道为啥文件传上去了,但是却无法解析。
当我们上传文件内容为auto_prepend_file=file.jpg的.user.ini。
这行语句的作用:所有的php文件都自动包含file.jpg文件。.user.ini相当于一个用户自定义的php.ini。

Pass-06(大小写绕过)

查看源码,发现源码种没有$file_ext = strtolower($file_ext)这个转换大小写的函数;那么这就是一个漏
洞点;我们可以利用这个漏洞点进行一个大小写欺骗

Pass-07(空格绕过)

查看源码发现没有trim($file_ext)这个首尾去空的函数;所以我们可以在文件后缀后面添加一个空格.php
实现绕过
补充:windows服务器上,会自动去除后缀名后的空格。

Pass-08(点号绕过)

查看源码发现没有deldot($file_name)没有删除文件末尾这个点的函数;所以我们尝试构造.php.进行绕过
补充:在windows系统中,会自动把.给删除掉,所以可以加个.绕过过滤,然后文件上传到目录后,.会自动被删除

Pass-09(::$DATA绕过)

查看源码发现没有str_ireplace(‘::$DATA’, ‘’, $file_ext)去除文件末尾的::$DATA这个函数;所以我们可
以尝试构造.php::$DATA进行绕过
补充:在window的时候如果文件名+”::$DATA”会把::$DATA之后的数据当成文件流处理,不会检测后缀名,且保持::$DATA之前的文件名,他的目的就是不检查后缀名

Pass-10(点空格点绕过)

查看源码发现.htaccess和.ini都被过滤;所以无法像第四关和第五关一样使用分配式文件进行绕过,但是我们
又发现因为这关的防御只检测了一次;也就是说这些收尾去空,删除末尾的点,去除字符串::$DATA,转换为小写这些东西只是验证了一次。又因为在在windows系统中,会自动把.给删除掉,所以可以加个.绕过过滤,然后文件上传到目录后,.会自动被删除和windows服务器上,会自动去除后缀名后的空格。所以我们可以构造.php. .进行绕过。因为它的验证首先会删除文件名两端的空格,但是我们构造的后缀名的两端没有空格,所以此验证无效,接着它会删除我们后缀名中最后的一个点,然后又会过滤掉我们的一个空格,此时上传文件的后缀名为.php.不在黑名单里面,所以可以成功被上传,而且又因为windows操作系统的原因,文件后缀名最后的点也会被删除,所以我们就完成了绕过。

Pass-11(双写绕过)

查看源码,发现了一个熟悉的函数;但是和之前的不太一样;之前替换的是::$DATA;而这次替换的是数组里面的
后缀;此时想到sqli-labs里面的双写绕过即可;但是注意不可以使用.phphpp这样双写。

哈哈哈做到这里我发现其实上面的题目都可以使用点空格点进行绕过。

白名单(12-21)

Pass-12(GET型%00截断绕过)

查看一下源码发现了一个比较特殊的嵌套函数
$file_ext = substr($_FILES[‘upload_file’][‘name’],strrpos($_FILES[‘upload_file’][‘name’],”.”)+1);
substr(string,start,length):
作用:返回字符串的一部分。
语法:

1
2
3
4
5
6
7
8
string  必需。规定要返回其中一部分的字符串。
start 必需。规定在字符串的何处开始。
正数 - 在字符串的指定位置开始
负数 - 在从字符串结尾开始的指定位置开始
0 - 在字符串中的第一个字符处开始
length 可选。规定被返回字符串的长度。默认是直到字符串的结尾。
正数 - 从 start 参数所在的位置返回的长度
负数 - 从字符串末端返回的长度

strrpos(string,find,start):
作用:查找字符串在另一字符串中最后一次出现的位置。
语法:

1
2
3
string  必需。规定被搜索的字符串。
find 必需。规定要查找的字符。
start 可选。规定在何处开始搜索。

作者首先提取了文件的后缀名:从获得到名字的字符串里面,从中检索点最后一次出现的位置来进行截取后缀名

此时发现$img_path的路径时进行拼接的,所以我们可以考虑从拼接路径入手
截断上传:在url中%00表示ascll码中的0 ,而ascii中0作为特殊字符保留,表示字符串结束,所以当url中出现%00时就会认为读取已结束,而忽略后面上传的文件或图片,只上传截断前的文件或图片。
这个%00截断我们也在sqli-labs里面用过(当注释符被过滤的时候可以考虑)

其实在截断中是wann.jpg的内容是被wann.php给继承过去了

Pass-13(POST型%00截断)


查看源码,发现此时的拼接路径是以POST的方式进行提交的;当我们以GET的方式提交时,浏览器遇到%会自动对其后面的内容进行url解码;但是当我们以POST方式提交的时候并不会进行解码。但是我们有两种方法进行解决。
法一:对%00直接进行url解码

法二:是更改其十六进制进制码(注:php的十六进制码为70 68 70)

Pass-14(图片马绕过)

依旧是查看一下源码;发现了好多不认识的东西;那么我们便解读一下吧

1.fopen(filename,mode,include_path,context):
作用:打开文件或者 URL
语法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
filename  必需。规定要打开的文件或 URL。
mode 必需。规定要求到该文件/流的访问类型。可能的值见下表。
include_path 可选。如果也需要在 include_path 中检索文件的话,
可以将该参数设 为 1 或 TRUE。
context 可选。规定文件句柄的环境。Context 是可以修改流的行为的一套选项。

mode:
"r" 只读方式打开,将文件指针指向文件头。
"rb" 以二进制方式打开
"r+" 读写方式打开,将文件指针指向文件头。
"w" 写入方式打开,将文件指针指向文件头并将文件大小截为零。如果文件不存在则尝试创建之。
"w+" 读写方式打开,将文件指针指向文件头并将文件大小截为零。如果文件不存在则尝试创建之。
"a" 写入方式打开,将文件指针指向文件末尾。如果文件不存在则尝试创建之。
"a+" 读写方式打开,将文件指针指向文件末尾。如果文件不存在则尝试创建之。
"x" 创建并以写入方式打开,将文件指针指向文件头。如果文件已存在,则 fopen() 调用 失败并返回 FALSE,并生成一条 E_WARNING 级别的错误信息。如果文件不存在则尝试创建之。

2.fread(file,length):
作用:读取文件(可安全用于二进制文件)。
语法:

1
2
file  必需。规定要读取打开文件。
length 必需。规定要读取的最大字节数。

3.fclose(): 作用:关闭一个打开文件
4.unpack(format,data):
作用:从二进制字符串对数据进行解包。
语法:

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
format  必需。规定在解包数据时所使用的格式。
data 可选。规定被解包的二进制数据。

format 参数的可能值:

a - NUL-padded string
A - SPACE-padded string
h - Hex string, low nibble first
H - Hex string, high nibble first
c - signed char
C - unsigned char
s - signed short (always 16 bit, machine byte order)
S - unsigned short (always 16 bit, machine byte order)
n - unsigned short (always 16 bit, big endian byte order)
v - unsigned short (always 16 bit, little endian byte order)
i - signed integer (machine dependent size and byte order)
I - unsigned integer (machine dependent size and byte order)
l - signed long (always 32 bit, machine byte order)
L - unsigned long (always 32 bit, machine byte order)
N - unsigned long (always 32 bit, big endian byte order)
V - unsigned long (always 32 bit, little endian byte order)
f - float (machine dependent size and representation)
d - double (machine dependent size and representation)
x - NUL byte
X - Back up one byte
@ - NUL-fill to absolute position

5.ntval(mixed $var [, int $base = 10 ]):
作用:用于获取变量的整数值。

1
2
3
4
5
6
7
$var:要转换成 integer 的数量值。
$base:转化所使用的进制。

如果 base 是 0,通过检测 var 的格式来决定使用的进制:
如果字符串包括了 "0x" (或 "0X") 的前缀,使用 16 进制 (hex);否则,
如果字符串以 "0" 开始,使用 8 进制(octal);否则,
将使用 10 进制 (decimal)。

所以这段代码的意思就是以二进制打开我们传入的$filename然后赋给$file;按然后在读取$file的前两个字节赋值给$bin接着关闭文件。以二进制的形式对$bin进行解包然后赋值给$strinfo;接着将$strinfo转化为整数值,将这个整数值进行赋值给$typrcode;然后在对这个整数值进行一个判断。
有一个疑问就是为什么不用读取文件后缀便可以判断文件类型;翻看了学长的博客后;学长是这样解释的:答案是这样的,其实,文件对自身文件内容,有着自己的文件头标识,我们只需要文件转为16进制,然后看各个文件类型对文件头的定义,就可以知道文件的类型了,例如,jpeg图片格式的文件头(2byte)标识为:0xff, 0xd8,结尾(2byte)标识为:0xff,0xd9 而转化为二进制再转化为十进制也是一样的。我自己的理解就是每个不同文件的文件
头是不同的,所以可以只读两个字节进行判断(注意:因为应用程序为了识别是自己的文件,会在文件头加入一些辨识标志,所以只读两个字节便可以知道是什么类型。)。这个应该也是为什么我们无法使用抓包然后进行修改后缀
进行绕过的原因吧;因为你php文件和jpg或png等的文件头是不一样的,就算你修改了后缀,文件头还是原来那样也是无法进行绕过。
所以针对此题我们需要制作一个图片马进行上传:
图片马的制作:

1
copy 1.png/b+2.php/a 3.png

注:/a 表示 ASCII码文件 /b 表示二进制文件



制作之后我们发现一句话木马已经插入图片中了,并且成功上传;且可以连接蚁剑

Pass-15(getimagesize()图片马绕过)

查看源码,发现新函数:

1.getimagesize():
作用:用于获取图像大小及相关信息,成功返回一个数组,失败则返回 FALSE 并产生一条 E_WARNING 级的错误信息
语法:

1
2
3
4
5
6
7
索引 0 给出的是图像宽度的像素值
索引 1 给出的是图像高度的像素值
索引 2 给出的是图像的类型,返回的是数字,其中1 = GIF,2 = JPG,3 = PNG,4 = SWF,5 = PSD,6 = BMP,7 = TIFF(intel byte order),8 = TIFF(motorola byte order),9 = JPC,10 = JP2,11 = JPX,12 = JB2,13 = SWC,14 = IFF,15 = WBMP,16 = XBM
索引 3 给出的是一个宽度和高度的字符串,可以直接用于 HTML 的 <image> 标签
索引 bits 给出的是图像的每种颜色的位数,二进制格式
索引 channels 给出的是图像的通道值,RGB 图像默认是 3
索引 mime 给出的是图像的 MIME 信息,此信息可以用来在 HTTP Content-type 头信息中发送正确的信息,如: header("Content-type: image/jpeg");

2.image_type_to_extension(int $imagetype [, bool $include_dot = TRUE):
作用:根据指定的图像类型返回对应的后缀名。
3.stripos():
作用:查找字符串在另一字符串中第一次出现的位置(不区分大小写)。
所以此段源码的作用是先对我们传入的图像进行一个获取,然后返回这个图像的相关信息并将这些相关信息储存在一个数组内;然后将该数组赋值给$info;接下来根据$info[2]的内容返回改图片的后缀;最后查找该后缀在$t
ypes中出现的位置;若是有查找到便返回该后缀;所以此关我们也是必须上传一个图片马,使其通过前面的验证。

Pass-16(exif_imagetype()图片马绕过)

查看源码,又发现了一个新函数

exit_imagetupe():
作用:这个函数也是用来检查是否为图片类型的,函数读取一个图像的第一个字节并检查其签名,如果发现了恰当的签名则返回一个对应的常量,否则返回 FALSE。(返回值和 getimagesize() 返回的数组中的索引 2 的值是一样的)
所以还是图片马绕过

Pass-17(二次渲染绕过)

原理:在我们上传文件后,网站会对图片进行二次处理(格式、尺寸要求等),服务器会把里面的内容进行替换更新,处理完成后,根据我们原有的图片生成一个新的图片并放到网站对应的标签进行显示。这就意味着我们传入的一句话木马会被抹杀。我们将上传的图片保存下来发现木马确实是被查杀了。我们还是看看源码吧

源码:

1.basename(path,suffix):
作用:返回路径中的文件名部分。
语法:

1
2
path  必需。规定要检查的路径。
suffix 可选。规定文件扩展名。如果文件有 suffix,则不会输出这个扩展名。

2.magecreatefromjpeg(string $filename):
作用:由文件或 URL 创建一个新图象。成功后返回图象资源,失败后返回 FALSE 。

1
filename    JPEG 图像的路径。

3.unlink(filename,context):
作用:删除文件。若成功,则返回 true,失败则返回 false。
语法:

1
2
filename  必需。规定要删除的文件。
context 可选。规定文件句柄的环境。Context 是可修改流的行为的一套选项。

4.srand(seed):
作用:播下随机数发生器种子。
语法:

1
seed  可选。用 seed 播下随机数发生器种子。

5.strval():
作用:获取变量的字符串值。
6.imagejpeg(resource $image [, string $filename [, int $quality ]]):
作用:输出图象到浏览器或文件
语法:

1
2
3
4
image:由图象创建函数(例如imagecreatetruecolor())返回的图象资源。
filenam:文件保存的路径,如果未设置或为 NULL,将会直接输出原始图象流。如果要省略这个参数而提供
quality 参数,使用NULL。
quality:quality 为可选项,范围从 0(最差质量,文件更小)到 100(最佳质量,文件最大)。默认为 IJG 默认的质量值(大约 75)。
  1. imagecreatefromgif():创建一块画布,并从 GIF 文件或 URL 地址载入一副图像
    imagecreatefromjpeg():创建一块画布,并从 JPEG 文件或 URL 地址载入一副图像
    imagecreatefrompng():创建一块画布,并从 PNG 文件或 URL 地址载入一副图像
    imagecreatefromwbmp():创建一块画布,并从 WBMP 文件或 URL 地址载入一副图像
    imagecreatefromstring():创建一块画布,并从字符串中的图像流新建一副图像
    move_uploaded_file() 函数将上传的文件移动到新位置。
    所以此段源码的作用是:先获取上传文件的基本信息;然后通过basename函数返回文件名;接着通过substr()和strrchr()的嵌套返回该文件的后缀名;然后把这个后缀名赋值给$fileext;然通过(move_uploaded_file)将图片移到$target_path;接着通过imagecreatefromjpeg():创造一个新的图像;目的是用用户上传的图片进行二次渲染(通过变换图片,处理过滤掉一些恶意代码)后,将二次渲染后的图片上传到服务器。;然后如果该图片是.jpg或者.png或.gif的话;便使用strval(rand())给新文件命名;然后通过imagejpeg()输出新图像和路径;然后删除原路径。
    补充:由于gif文件在二次渲染之后会保留一段和渲染前相同的内容,而jpg与png则没有这段内容。因此我们选择gif文件;我们只要把一句话木马插到未被渲染的地方即可。
    通过010 Editor我们很容易发现一样的地方;在这插入一句话木马然后保存上传即可。


    上传成功:(皮卡丘好可爱当然要保存一下)

Pass-18(条件竞争删除文件的时间差绕过)

老规矩依旧是查看源码

1.rename(oldname,newname,context):
作用:重命名文件或目录。若成功,则该函数返回true。若失败,则返回 false。
语法:

1
2
3
oldname   必需。规定要重命名的文件或目录。
newname 必需。规定文件或目录的新名称。
context 可选。规定文件句柄的环境。context 是可修改流的行为的一套选项。

此段源码的意思:先对一些变量进行赋值;绕后通过substr($file_name,strrpos($file_name,”.”)+1)进行截取文件后缀;再次通过move_uploaded_file()进行文件转移;然后进行判断文件后缀是否在数组中;若是在数组中便进行重命名;若是不在,便将文件删除。
此时出现了存储和检查的这两个步骤;我们便需要想到条件竞争。
条件竞争:条件竞争漏洞是一种服务器端的漏洞,由于服务器端在处理不同用户的请求时是并发进行的,因此,如果并发处理不当或相关操作逻辑顺序设计的不合理时,将会导致此类问题的发生。

思路的突破点在于:他是先移动到服务器后在判断

解题思路:在上传以后储存在服务器,到检查不通过的这段时间如果我们可以利用起来,就算后续他将文件删除了也无所谓,那么这段时间太短了,如何将其放大?那就利用burp的intruder,随便弄一个peyload让他在那边跑就行,然后我们在浏览器中直接访问该文件。
法一:

1
2
3
4
5
6
7
8
9
import requests
url = "http://127.0.0.1/upload/attack.php"
while True:
html = requests.get(url)
if html.status_code == 200:
print("OK")
break
else:
print("NO")

运行脚本,然后同时发动攻击;当出现OK时就证明竞争通过



法二:在发动攻击的同时不断访问127.0.0.1/upload/attack.php将该网页刷出来就代表竞争通过

Pass-19(条件竞争删除文件的时间差绕过)

依旧是查看源码;依旧发现了这个传输和重命名。嘿嘿嘿那接下来该怎么样呢?

Pass-20(%00绕过或/.绕过)

查看源码,发现了一个新函数:

pathinfo(path,options):
作用:以数组的形式返回文件路径的信息。
语法:

1
2
3
4
5
6
path  必需。规定要检查的路径。
process_sections 可选。规定要返回的数组元素。默认是 all。
可能的值:
PATHINFO_DIRNAME - 只返回 dirname(所在路径)
PATHINFO_BASENAME - 只返回 basename(文件名)
PATHINFO_EXTENSION - 只返回 extension(后缀名)

这里move_uploaded_file()除了%00截断之外,还会忽略掉文件末尾的/.,这里我们可以利用这一点构建文件名
进行绕过

Upload-labs总结



文件上传漏洞的攻击方式:
1.禁用前端检测(我的理解是这样的它前段验证是在我们提交的时候进行一个验证,但是此时服务器还没有接受到我们提交的文件;所以我们此时使用bp在文件传输给服务器的时候进行一个改包;因为此时上传的后缀名符合前段检验所以可以通过;而前端检测和后端检测的区别在于检测的位置不同;后端检测更加的安全,因为它是在浏览器进行数据处理的时候进行的)
前端验证是在客户端浏览器中进行的验证。它可以使用 HTML5、JavaScript 或其他客户端技术来实现。前端验证通常用于提高用户体验,因为它可以在用户提交表单之前快速检测并反馈错误。
后端验证是在服务器端进行的验证。它通常使用服务器端编程语言(如 PHP、Java、Python 等)实现。后端验证可以防止恶意用户绕过前端验证并提交不符合要求的数据。

1
2
3
4
5
6
7
8
9
10
11
后端过滤一般用到的函数:
trim()去除字符串两边的空格
deldot()删除文件名末尾的点号
strrchr()反向截取字符串
strtolower()转化为小写
str_ireplace()替换字符串中的内容
substr()获取子字符串
strrpos()获取字符串的位置信息
fopen()打开文件
fread()读取文件
unpack()函数从二进制字符串对数据进行解包
1
2
3
前端校验
1、检测原理
如果浏览器页面没有刷新、或打开burp抓包没有抓到包、或者通过查看前端HTML代码等方式验证证明前端JS验证。

2.绕过MINE检测:
image/jpeg :jpg图片格式
image/png :png图片格式
image/gif :gif图片格式
text/plain :纯文本格式
text/xml : XML格式
text/html : HTML格式
3.绕过黑名单:
(1)扩展后缀:php4/php5/phtml/php3/pht (2)大小写绕过 (3)点或者空格绕过
(4)添加::$DATA使其不对后缀名进行验证 (5)双写绕过
(6).htaccess:
一、让.gif以php执行

1
AddType application/x-httpd-php .gif

二、让含php关键字就以php执行

1
AddHandler php5-script php

三、让指定文件名的文件以PHP程序执行

1
2
3
<FilesMatch "ywnn">
SetHandler application/x-httpd-php
</FilesMatch>

(7).user.ini:
所有的php文件都自动包含file.jpg文件。

1
auto_prepend_file=file.jpg的.user.ini

4.绕过白名单:
(1)图片马进行绕过:遇到对我们上传的文件进行十分严格的检查且规定一定是要.jpg.png.gif
getReailFileType()
getimagesize()
exif_imagetype():返回值和 getimagesize() 返回的数组中的索引 2 的值是一样的
注意:使用图片马时必须要有文件包含;不然蚁剑是无法对图马进行解析的
(2)二次渲染绕过:一般使用gif
(3)基于时间差的条件竞争绕过:有上传到服务器进行检查和删除时出现时间差
(4)%00截断绕过:路径为以POST或GET方式提交时可控
(5).截断绕过:遇到move_uploaded_file()且前面不进行点号过滤时


FINISH