upload-labs靶机练习
一、后缀黑名单绕过
1.前端 JS 绕过
可以直接更改 js 源码,允许上传 PHP 文件
也可以直接删除
2.只验证 Content-type
if (file_exists(UPLOAD_PATH)) {
if (($_FILES['upload_file']['type'] == 'image/jpeg') || ($_FILES['upload_file']['type'] == 'image/png') || ($_FILES['upload_file']['type'] == 'image/gif')) {
$temp_file = $_FILES['upload_file']['tmp_name'];
前端没有过滤,burp 抓包修改 content-type,绕过即可。
3.后缀名-黑名单绕过
源码
if (isset($_POST['submit'])) {
if (file_exists(UPLOAD_PATH)) {
$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)) {
$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 = '不允许上传.asp,.aspx,.php,.jsp后缀文件!';
}
} else {
$msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
不得不承认,这波黑名单搞得我有点头痛。。
新知识学习
用黑名单不允许上传.asp,.aspx,.php,.jsp后缀的文件
但可以上传.phtml .phps .php5 .pht
前提是apache的httpd.conf中有如下配置代码
AddType application/x-httpd-php .php .phtml .phps .php5 .pht
可以在 apache 的配置文件中没有找到呀,但是好像也可以上传,但是访问上传文件时,自动将 php 脚本进行了 html 注释,没有任何显示
4.htaccess 绕过
源码
if (isset($_POST['submit'])) {
if (file_exists(UPLOAD_PATH)) {
$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");
$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); //收尾去空
过滤是真的多。。。
没有过滤.htaccess,可以尝试下
SetHandler application/x-httpd-php
创建一个.htaccess 文件,写入代码(内容为将 4.jpg 当做 php 文件解析)
上传.htaccess 文件,再上传 4.jpg 图片马文件(马文件是通过图片和 php 结合的)
5..user.ini 绕过黑名单
依然过滤了很多,过滤了.htaccess 后缀,但是没有过滤点.int 文件
所以可以上传.user.ini 文件
内容是
auto_prepend_file=test.jpg
让所有 php 文件都“自动”包含 test.jpg 文件
6.后缀名大小写绕过
没有对后缀名镜大小写转化。
7.空格绕过
源码
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 = $_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
相比前面的题,这道没有后缀名去空处理,加空格即可绕过。
注意这里仅仅是空格还是会有问题,需要在空格后面加上'
8.点(.)绕过黑名单
提示
后缀无法解析....
源码
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_ext = strrchr($file_name, '.');
$file_ext = strtolower($file_ext); //转换为小写
$file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
$file_ext = trim($file_ext); //首尾去空
相比前面的题,没有了$file_name = deldot($file_name);//删除文件名末尾的点
,点号绕过
9.::$DATA
绕过
源码
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_ext = strrchr($file_name, '.');
$file_ext = strtolower($file_ext); //转换为小写
$file_ext = trim($file_ext); //首尾去空
相比前面的题,没有了$file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
,
没有对后缀名中的’::$DATA’进行过滤
在php+windows的情况下
文件名+"::$DATA"会把::$DATA之后的数据当成文件流处理,不会检测后缀名
且保持"::$DATA"之前的文件名
10.点空点绕过
源码
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.'/'.$file_name;
if (move_uploaded_file($temp_file, $img_path)) {
$is_upload = true;
主要区别是图片的存储路径是文件名,保存文件的时候没有重命名而使用的原始的文件名
可以使用1.php. .
绕过
11.双写绕过
用 str_ireplace 把黑名单里的后缀全替换为空
但是这个函数只进行一次替换$file_name = str_ireplace($deny_ext,"", $file_name);
二、后缀白名单
12.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类型文件!";
}
}
可以看到 get 方法的 save_path 是可以控制的,可以使用%00 截断
我们可以抓包修改 get 的参数,然后通过 file_ext 无效,这样就可以上传 PHP 文件
%00 截断的概念和原理:
在URL中%00表示ASCII码中的0,而0作为特殊字符保留,表示字符结束
当url中出现%00时就会认为读取已结束,而忽略后面上传的文件或图片,只上传截断前的文件或图片
要求:php版本小于5.3.4,php的magic_quotes_gpc为OFF状态
13.POST%00 截断
和上题的差距只是--由 GET 变成 POST 型
还是利用 00 截断
但是 POST 不会像 GET 对%00 进行自动解码
需要在二进制中进行修改
将 70 68 70 后面的 2b 改为 00
三、检查内容
14.图片马绕过
题目要求发生了变化
源码
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_PATH."/".rand(10, 99).date("YmdHis").".".$file_type;
if(move_uploaded_file($temp_file,$img_path)){
$is_upload = true;
} else {
$msg = "上传出错!";
}
}
}
rb 是读取二进制文件
后面的 fread 函数只读两字节,也就是说只对文件头进行了检测
unpack(format,data)函数是规定在解包数据时所使用的格式,这里是文件头按照 c 格式解包
intval() 函数用于获取变量的整数值
但直接访问图片并不能把图片当做 PHP 解析
还需要利用文件包含漏洞
www/upload-labs/upload 目录下建立一个 php 文件
15.图片马绕过
- 源码
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)>=0){
return $ext;
}else{
return false;
}
}else{
return false;
}
}
使用 getimagesize()函数来获取图片的大小及相关信息,成功返回一个数组,失败则返回 FALSE 并产生一条 E_WARNING 级的错误信息。。
仍然可以使用图片马绕过。
16.图片马绕过
源码
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;
}
}
用 exif_imagetype 来判断文件的类型,仍然可以通过图片马绕过,成功上传,但是需要使用文件包含漏洞来访问对应文件
17.二次渲染绕过
源码
$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_PATH.'/'.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格式的图片!";
@unlink($target_path);
}else{
//给新图片指定文件名
srand(time());
$newfilename = strval(rand()).".jpg";
//显示二次渲染后的图片(使用用户上传图片生成的新图片)
$img_path = UPLOAD_PATH.'/'.$newfilename;
imagejpeg($im,$img_path);
@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格式的图片!";
@unlink($target_path);
}else{
//给新图片指定文件名
srand(time());
$newfilename = strval(rand()).".png";
//显示二次渲染后的图片(使用用户上传图片生成的新图片)
$img_path = UPLOAD_PATH.'/'.$newfilename;
imagepng($im,$img_path);
@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格式的图片!";
@unlink($target_path);
}else{
//给新图片指定文件名
srand(time());
$newfilename = strval(rand()).".gif";
//显示二次渲染后的图片(使用用户上传图片生成的新图片)
$img_path = UPLOAD_PATH.'/'.$newfilename;
imagegif($im,$img_path);
@unlink($target_path);
$is_upload = true;
}
} else {
$msg = "上传出错!";
}
}else{
$msg = "只允许上传后缀为.jpg|.png|.gif的图片文件!";
}
}
- 判断了后缀名、content-type
- 利用 imagecreatefromgif 判断格式
- 二次渲染了上传的图片
- 绕过方法:找到渲染前后没有变化的位置,然后将 php 代码写进去,就可以成功上传带有 php 代码的图片
假设这里使用图片马绕过,上传之后的图片中的 PHP 语句会被过滤掉
针对 gif 文件上传,做个尝试
在 hex 代码中找到前后没有变化的内容,将添加到 1.gif 的尾部,即可成功上传。
针对 png 和 jpg 的二次渲染上传,可参考大佬文章
18.条件竞争
源码
$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 = '上传失败!';
}
}
先上传文件,然后白名单判断文件的后缀名,如果不符,则报错及删除文件,如果在白名单,则重命名。
这就在文件的处理顺序上出现了问题,不管文件类型是否合格就上传至服务器,之后再对其类型进行判断,这样的处理顺序导致了在多线程的情况下,有可能对于不合格的文件还没来得及删除就已经被访问,导致不合格的文件绕过了限制
因此我们可以打个时间差:上传 1.php,只需要在它删除之前访问即可
可以利用 burp 的 intruder 模块不断上传,然后我们不断的访问刷新该地址
条件竞争:也就是先上传再判断是否需要删除。。。。