upload-labs靶场1-19关详解
upload-labs靶场下载地址
https://gitcode.net/mirrors/tj1ngwe1/upload-labs?utm_source=csdn_github_accelerator
需要新建一个upload文件夹,该靶场在php5.2.17版本下(除特殊说明的情况下)。
Pass-01(前端验证绕过)
先上传一个php文件看一下回显
然后上传一个正常文件,发现回显正常。查看了一下网页源代码,发现上传文件检查是通过前端进行验证的,那么有很多种方法继续绕过。
源码分析
function checkFile() {
var file = document.getElementsByName('upload_file')[0].value;
if (file == null || file == "") {
alert("请选择要上传的文件!");
return false;
}
//定义允许上传的文件类型
var allow_ext = ".jpg|.png|.gif";
//提取上传文件的类型
var ext_name = file.substring(file.lastIndexOf("."));
//判断上传文件类型是否允许上传
if (allow_ext.indexOf(ext_name + "|") == -1) {
var errMsg = "该文件不允许上传,请上传" + allow_ext + "类型的文件,当前文件类型为:" + ext_name;
alert(errMsg);
return false;
}
}
getElementsBy()
方法可以返回指定名称的对象的集合。
substring(起始索引,结束索引)
返回字符串的子字符串。
lastIndexOf()
方法可返回一个指定的字符串值最后出现的位置,在一个字符串中的指定位置从后向前搜索。
方法一
直接禁用js就可以上传了,以火狐浏览器为例,按f12,在‘调试器’面板最右边有个设置按钮,禁用js。
然后把写好的php文件再次上传,发现没有提示文件错误。
访问http://localhost/upload-labs-master/upload/1.php
,显示成功上传
不过实战中直接禁用js插件会导致一些页面无法正常显示,这种只能打打靶场就行了。
方法二
右键查看网页源代码 — 复制全部代码 — 将代码放在一个txt文件中 — 将后缀改为html — 用编辑器打开 — 找到js代码并删除(删除红框里的代码)
然后在在上面添加一段action
,地址是图片上传的地址,我们在这加上来
然后用浏览器打开这个html文件,直接进行上传操作即可。然后上传一个在原页面上传个正常的图片,按F12,然后随便上传一个图片,在"网络"模块中可以找到上传地址
访问对应所上传的php文件即可。
方法三
新建一个png文件,写入<?php phpinfo();?>
,然后burpsuite工具抓包,直接修改后缀名为.php然后点击Forward即可完成上传
访问上传地址即可。
Pass-02(Content-Type方式绕过)
源码分析
$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) { //判断是否点击上传按钮
if (file_exists($UPLOAD_ADDR)) { //判断UPLOAD_PATH文件路径是否存在 UPLOAD_PATH在config.php中定义为"../upload"
if (($_FILES['upload_file']['type'] == 'image/jpeg') || ($_FILES['upload_file']['type'] == 'image/png') || ($_FILES['upload_file']['type'] == 'image/gif')) { //判断上传文件的类型是否为image/jpeg、image/png、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检查。上传一个1.php文件,通过抓包修改文件类型进行绕过。
点Forward放包即可。
Pass-03(黑名单绕过)
源码分析
$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
if (file_exists($UPLOAD_ADDR)) {
$deny_ext = array('.asp','.aspx','.php','.jsp');
$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 = '不允许上传.asp,.aspx,.php,.jsp后缀文件!';
}
} else {
$msg = $UPLOAD_ADDR . '文件夹不存在,请手工创建!';
}
}
trim(string,charlist) 函数移除字符串两侧的空白字符或其他预定义字符。
deldot()函数,是作者自定义的函数,作用是删除文件名末尾的点。
strrchr(string,char)函数查找字符串在另一个字符串中最后一次出现的位置,并返回从该位置到字符串结尾的所有字符。
strtolower(string) 函数把字符串转换为小写。
str_ireplace(find,replace,string,count) 函数替换字符串中的一些字符(不区分大小写)
in_array(search,array,type) 函数搜索数组中是否存在指定的值。
这里可以知道黑名单array('.asp','.aspx','.php','.jsp')
,但是并没有过滤phtml,php3,php4, php5, pht
,可以通过这些后缀名进行黑名单绕过,但是要在httpd.conf配置文件添加下面这段
AddType application/x-httpd-php .php .phtml .php3 .php5
AddType application/x-httpd-php-source .phps
php版本为5.4.45,而不是5.4.45-nts
参考:https://blog.csdn.net/CSDN_caiqian/article/details/129493643
攻击方法
新建一个以.php5
为后缀的文件,写入一句话木马,直接上传即可。这里需要通过F12网络模式查看文件路径
访问即可。
Pass-04(.htaccess文件绕过)
源码分析
$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 . '文件夹不存在,请手工创建!';
}
}
黑名单有很多,很显然不能用黑名单绕过了,这里没有禁用.htaccess
,该文件可以把图片解析为php,需要在.htaccess
里写入
<FilesMatch "1.png"> //xxx由自己定义,定义什么则上传相同文件
SetHandler application/x-httpd-php
</FilesMatch>
攻击方法
先上传.htaccess
,再上传1.png
Pass-05(后缀大小写绕过)
源码分析
$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 . '文件夹不存在,请手工创建!';
}
}
黑名单把.htaccess
给禁了,对比第4关的源码发现,缺少了$file_ext = strtolower($file_ext);
将字符串转为小写。那么我们可以上传大写的文件后缀,创建1.Php
上传。
攻击方法
新建1.Php
,写入<?php phpinfo();?>
,上传,访问即可。
Pass-06 (文件后缀(空)绕过)
源码分析
$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
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 . '文件夹不存在,请手工创建!';
}
}
对比第4关,少了$file_ext = trim($file_ext);
去除首位空白字符,既然最后一步没有去空操作,那么我们可以创建一个文件1.php
,通过bp抓包修改文件名后缀包含空字符。
攻击方法
新建1.php
,通过bp抓包
放包即可。
Pass-07 (文件后缀(点)绕过)
源码分析
$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 . '文件夹不存在,请手工创建!';
}
}
比较第4关,少了$file_name = deldot($file_name);
删除文件名末尾的点,如1.php.
,由于没有删除末尾的点,传到file_ext
会是最后一个点后面的所以字符,那么就是空,空不在黑名单中,故可绕过。
攻击方法
新建一个1.php
,通过bp抓包修改文件后缀。
放包,访问路径即可。
Pass-08 (文件后缀(::$DATA)绕过)
源码分析
$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 = 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 . '文件夹不存在,请手工创建!';
}
}
比较第4关,少了$file_ext = str_ireplace('::$DATA', '', $file_ext);
将文件后缀中的::$DATA
字符串替换为空,这里利用了windows特性
在window的时候如果文件名+"::$DATA"会把::$DATA之后的数据当成文件流处理,不会检测后缀名,且保持::$DATA之前的文件名,他的目的就是不检查后缀名
例如:"phpinfo.php::$DATA"Windows会自动去掉末尾的::$DATA变成"phpinfo.php"
攻击方法
新建一个1.php
,通过bp抓包
放包,访问即可
Pass-09 (构造文件后缀绕过)
源码分析
$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 . '文件夹不存在,请手工创建!';
}
}
观察源码,比较第4关没有差别,仔细看下代码,发现没有循环验证,也就是说去除文件名末尾点只删除一次,那么我们是不是可以在后缀加两个点(两个点之间加个空格,因为deldot()函数是从文件名最后面开始判断是否为点,如果是则匹配下一个,如果两个点连在一起,则会一起被删除)进行绕过,显然是可以的。
附deldot()函数代码:
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);
}
}
}
攻击方法
新建一个1.php
,通过bp抓包
放包,访问即可
这里除了构造1.php. .
还有1.phP. .
、1.php. . .
都差不多。
Pass-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 . '文件夹不存在,请手工创建!';
}
}
这里少了很多函数,不过这一关主要考察双写绕过,关键函数在$file_name = str_ireplace($deny_ext,"", $file_name);
在file_name中查找deny_ext中的值,若找到则替换为空,很显然我们若构造1.pphphp
,这会把中间的php给替换掉,则剩下的字符自动拼接成php,即可进行绕过。
攻击方法
新建一个1.pphphp
,上传,然后访问地址即可。
Pass-11 (get传参%00截断绕过)
源码分析
$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类型文件!";
}
}
strrpos($_FILES['upload_file']['name'],".")函数查找文件名中最后一个点号(.)的位置。
substr($_FILES['upload_file']['name'],strrpos($_FILES['upload_file']['name'],".")+1) 是使用 substr() 函数,从文件名中截取最后一个点号之后(不包括点号)的所有字符,即得到文件的扩展名。
date("YmdHis") 是获取当前日期和时间的格式化字符串,即时间戳
rand(10, 99) 是生成一个介于 10 和 99 之间的随机整数。
这里可以看到上传路径是可控变量,$img_path = $_GET['save_path']."/".rand(10, 99).date("YmdHis").".".$file_ext;
,这里通过get传参给save_path赋值,但是我们可以通过抓包进行修改。在url中%00表示ascll码中的0 ,0x00是十六进制表示方法,也是ascii码为0的字符,在有些函数处理时,会把这个字符当做结束符。所以我们在save_path参数后面加%00截断后面的字符串,那么上传路径为/upload/1.php
,即可上传成php文件解析。
不过%00截断是旧时产物,需要使用条件
php版本小于5.3.4
php的magic_quotes_gpc为OFF状态
攻击方法
新建一个1.png
,写入<?php phpinfo();?>
,然后上传抓包。
放包,访问即可。
Pass-12 (post传参%00绕过)
源码分析
$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 = $_POST['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类型文件!";
}
}
这里唯一的不同就是save_path参数是使用post传参的形式,那么我们改成post传参即可。但有一点说明,gei传参会自动解码url编码后的字符,但是post传参不会,我们是传参时,需解码。
攻击方法
新建一个1.png
,写入<?php phpinfo();?>
,然后上传抓包。(选中%00右键点击Convert selection选中URL,有URLdecode解码)
这里我用的2022.6.1版的bp,所以解码后是空白的,不过不影响,直接放包即可。
Pass-13 (图片马绕过)
源码分析
function getReailFileType($filename){
$file = fopen($filename, "rb");
$bin = fread($file, 2); //只读2字节
fclose($file);
$strInfo = @unpack("C2chars", $bin);
$typeCode = intval($strInfo['chars1'].$strInfo['chars2']);
$fileType = '';
switch($typeCode){
case 255216:
$fileType = 'jpg';
break;
case 13780:
$fileType = 'png';
break;
case 7173:
$fileType = 'gif';
break;
default:
$fileType = 'unknown';
}
return $fileType;
}
$is_upload = false;
$msg = null;
if(isset($_POST['submit'])){
$temp_file = $_FILES['upload_file']['tmp_name'];
$file_type = getReailFileType($temp_file);
if($file_type == 'unknown'){
$msg = "文件未知,上传失败!";
}else{
$img_path = $UPLOAD_ADDR."/".rand(10, 99).date("YmdHis").".".$file_type;
if(move_uploaded_file($temp_file,$img_path)){
$is_upload = true;
}
else{
$msg = "上传失败";
}
}
}
@unpack("C2chars", $bin) 这行代码用于对二进制数据进行解包(unpack),将其转换为 PHP 的数据类型。
解包格式字符串 "C2chars" 的含义是:
"C" 表示解包为无符号字符(unsigned char)类型。
"2" 表示要解包两次,因为 2 是重复次数。
"chars" 是解包后的结果的键名。
intval($strInfo['chars1'].$strInfo['chars2']) 将解包后的无符号字符转换为整数类型
这里的意思就是上传的文件取内容的前2个字节进行判断,若是png、gif、jpg格式的则上传,否则不上传,那么我们需要使用图片🐎进行上传,在图片末尾写入一句话木马即可。
gif文件包含需在php5.4.45版本下
攻击方法
找到一个正常的图片和新建1.php
,使用如下命令
copy logo.png /b + 1.php /a shell.png
意思是将1.php中的代码追加到logo.png中并重新生成一个叫shell.png的图片🐎。
可以使用010工具打开,查看末尾是否有🐎。
然后上传这个文件,因为是文件包含漏洞,需要在根目录下添加include.php文件,用于将图片🐎包含。
在include.php文件写入下列代码
<?php
header("Content-Type:text/html;charset=utf-8");
$file = $_GET['file'];
if(isset($file)){
include $file;
}else{
show_source(__file__);
}
?>
然后用file参数访问
这里的图片名可通过F12中的网络模块获取。
Pass-14 (图片马绕过)
源码分析
function isImage($filename){
$types = '.jpeg|.png|.gif';
if(file_exists($filename)){ //检查指定文件是否存在
$info = getimagesize($filename); //返回图片信息的数组形式
$ext = image_type_to_extension($info[2]); //将图像类型的常量值转换为对应的文件扩展名
if(stripos($types,$ext)){
return $ext;
}else{
return false;
}
}else{
return false;
}
}
$is_upload = false;
$msg = null;
if(isset($_POST['submit'])){
$temp_file = $_FILES['upload_file']['tmp_name'];
$res = isImage($temp_file);
if(!$res){
$msg = "文件未知,上传失败!";
}else{
$img_path = $UPLOAD_ADDR."/".rand(10, 99).date("YmdHis").$res;
if(move_uploaded_file($temp_file,$img_path)){
$is_upload = true;
}
else{
$msg = "上传失败";
}
}
}
getimagesize() 是 PHP 中用于获取图像文件信息的内置函数。它接受一个参数:图像文件的路径和名称,并返回一个包含图像信息的数组。
$type 数组中包含了多个索引,其中最常用的是:
$type[0]:图像宽度(以像素为单位)
$type[1]:图像高度(以像素为单位)
$type[2]:图像类型的常量值(例如 IMAGETYPE_JPEG 表示 JPEG 类型)
$type['mime']:图像的 MIME 类型
image_type_to_extension($info[2]); 这行代码用于将图像类型的常量值转换为对应的文件扩展名。
关键函数是这个getimagesize,getimagesize函数会对目标的十六进制的前几个字符串进行读取。比如GIF的文件头问GIF89a,png的文件头为塒NG。所以这关和第十三关一样,新建个图片🐎即可。
攻击方式
和13关一样,这里不赘述了。
Pass-15 (图片马绕过)
源码分析
function isImage($filename){
//需要开启php_exif模块
$image_type = exif_imagetype($filename);
switch ($image_type) {
case IMAGETYPE_GIF:
return "gif";
break;
case IMAGETYPE_JPEG:
return "jpg";
break;
case IMAGETYPE_PNG:
return "png";
break;
default:
return false;
break;
}
}
$is_upload = false;
$msg = null;
if(isset($_POST['submit'])){
$temp_file = $_FILES['upload_file']['tmp_name'];
$res = isImage($temp_file);
if(!$res){
$msg = "文件未知,上传失败!";
}else{
$img_path = $UPLOAD_ADDR."/".rand(10, 99).date("YmdHis").".".$res;
if(move_uploaded_file($temp_file,$img_path)){
$is_upload = true;
}
else{
$msg = "上传失败";
}
}
}
exif_imagetype($filename) 函数用于获取指定图像文件的类型,并返回对应的常量值
只获取文件类型,和前2关一样,图片🐎绕过
攻击方式
步骤一样。
这里需要修改php.ini配置文件,将 extension=php_exif.dll
和 extension=php_mbstring.dll
前面的分号去掉。另外记得 extension=php_mbstring.dll
一定要放在 extension=php_exif.dll
的前面。
Pass-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); //生成一个完整的上传路径 basename($filename) 从 $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"; //随机文件名 strval()将其随机整数转化为字符串
$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的图片文件!";
}
}
$im = imagecreatefrompng($target_path);:会使用上传的图片生成新的图片,此时会删除最后末尾的一句话木马
imagecreatefromjpeg()函数,二次渲染是由Gif文件或 URL 创建一个新图象。成功则返回一图像标识符/图像资源,失败则返回false,导致图片马的数据丢失,上传图片马失败。
按照前几关的方式上传,可以上传,但是包含漏洞无法解析。原因就是二次渲染将图片马里面的php代码删了。接下来把原图和修改后的图片进行比较,看哪里没有被渲染,在这里插入php代码。为了方便大家测试,这里再推荐网上大佬的二次渲染专用图片https://wwe.lanzoui.com/iFSwwn53jaf
攻击方法
我们先上传没有修复过的图片马上去,然后复制文件地址下载上传后的图片。用010打开两个图片进行比较
原图片马:
渲染后的图片:
可以看到原来的图片红色部分已被渲染,我们可以在已被渲染部分前添加一句话木马
这里不可以在头部插入,会破坏gif文件头,验证图片失败,上传不了。可以保存一下,看看图片是否可以查看,若不行则该位置不能修改为一句话木马,需要找其他未被渲染部分修改。
修改后在上传,访问即可。
当然用我上面那个链接,大佬专门写的二次渲染图片,直接上传即可。
Pass-17 (条件竞争php)
源码分析
$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); //对文件进行重命名或移动
unlink($upload_file);
$is_upload = true;
}else{
$msg = "只允许上传.jpg|.png|.gif类型文件!";
unlink($upload_file); //删除指定文件
}
}else{
$msg = '上传失败!';
}
}
这里我一开始在想,既然move_uploaded_file($temp_file, $upload_file)
在in_array()函数前判断,那么不就直接上传1.php文件不就行了吗,然而后面else中有unlink()删除文件,那么这种想法是不行的。这样的话,是不是可以在删除之前访问他,这样就无法删除了,相当于打开一个文件,然后在删除这个文件的时候显示文件在使用无法删除,思路很正确。
攻击方法
上传1.php
,通过bp抓包,一直上传1.php
这个文件。
如图进行修改
start attack发包,然后用浏览器一直访问1.php,按F5一直刷新,如果在上传的瞬间访问到了,它就无法删除。
Pass-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" );
}
......
......
......
};
查看代码,发现和第17关的差距是,这关还检测了后缀名,不能直接上传php文件,所以这关要上传图片马,其他步骤和17关类似。访问图片时使用文件包含。
攻击方法
和第17关一样的操作
Pass-19 (move_uploaded_file()函数特性)
源码分析
$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","pht","jsp","jspa","jspx","jsw","jsv","jspf","jtml","asp","aspx","asa","asax","ascx","ashx","asmx","cer","swf","htaccess");
$file_name = $_POST['save_name'];
$file_ext = pathinfo($file_name,PATHINFO_EXTENSION); //获取文件扩展名
if(!in_array($file_ext,$deny_ext)) {
$img_path = $UPLOAD_ADDR . '/' .$file_name;
if (move_uploaded_file($_FILES['upload_file']['tmp_name'], $img_path)) {
$is_upload = true;
}else{
$msg = '上传失败!';
}
}else{
$msg = '禁止保存为该类型文件!';
}
} else {
$msg = $UPLOAD_ADDR . '文件夹不存在,请手工创建!';
}
}
这里没有大小写、点等禁用,而且只需要判断save_name的后缀是否在黑名单里,那么可以大小写绕过,点绕过,空字符绕过等。这一关主要不是考察这个知识点,是考察move_uploaded_file()
函数的特性,这个函数会忽略掉文件末尾的/.
,那么我们只需在文件末尾加上/.
即可绕过。
攻击方法
上传一个1.php
,通过bp抓包
放包,访问即可
注:move_uploaded_file()函数中的img_path是由post参数save_name控制的,所以这一关还可以在save_name利用%00截断,这个方法前面已经说过了不再赘述,注意php版本要小于5.3
总结
以上就是upload-labs靶场的所以通过技巧,我这里下的是老版本的upload靶场,新版本的有21关。新版本有很多配置问题我就没有通关了。实战中一般都是黑盒测试,这个靶场就是让大家了解有哪些方法去测试文件上传漏洞。希望能帮助大家的学习,才疏学浅,一般般。