Upload Labs做题记录
记录一下,让自己了解上传的一些常见姿势,同时学习学习php代码,增加自己代码审计的能力。
pass-01 js前端验证
只是在前端进行验证,这时候可以使用firefox的Noscript插件,来禁用JS,达到上传目的。
前端判断代码为:
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; } }
可以直接F12,在文件上传类型中添加.php就可以上传php文件了。
我这里是进行BP抓包:
然后访问,蚁剑链接
pass-02 Content-Type
文件类型绕过
BP抓包,将文件类型改为图片即可
源码审计:
$is_upload = false; //初始的上传状态 $msg = null; if (isset($_POST['submit'])) { //判断是否是点了submit传过来的 if (file_exists(UPLOAD_PATH)) { //判断上传路径是否存在,也就是说在根目录下,是否存在upload这么一个文件夹,如果自己本地搭建的话,需要创建这么一个文件夹 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']; //判断允许上传的文件类型,BP抓包可以看到,他的文件类型是application/octet-stream,将其改为上面三个中的一个就行了 $img_path = UPLOAD_PATH . '/' . $_FILES['upload_file']['name'] //上传的路径 upload/你上传文件的名字 我这里就是:upload/test.php if (move_uploaded_file($temp_file, $img_path)) { $is_upload = true; } else { $msg = '上传出错!'; } } else { $msg = '文件类型不正确,请重新上传!'; } } else { $msg = UPLOAD_PATH.'文件夹不存在,请手工创建!'; } }
pass-03 文件后缀的判断
上传文件可以看到,他不允许上传以下后缀的文件,那么我们用其他php别名就行了,常见的别名有:pht,phpt,phtml,php3,php4,php5,php6
这里使用的是php3后缀
蚁剑访问成功
源码审计:
$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']); //使用trim函数去掉文件名前后的字符 $file_name = deldot($file_name);//删除文件名末尾的点 $file_ext = strrchr($file_name, '.'); //strchr函数是strstr函数的别名,这里返回 .后缀名。 $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 . '文件夹不存在,请手工创建!'; } }
pass-04 .htaccess解析
上传之后,发现文件不允许,应该在第三题的基础上加了其他别名到黑名单了,基本上是都过滤掉了,不过没有过滤 .htaccess。.htaccess文件可以实现重写文件解析的规则。
创建一个.htaccess的文件,里面写的内容为:SetHandler application/x-httpd-php,然后上传,这样就可以实现把锁上传的文件当成php来执行了。
在上传一个图片马,或者是将一句话文件改为图片形式上传就行了
蚁剑连接成功
源码审计:
$is_upload = false; $msg = null; if (isset($_POST['submit'])) { if (file_exists(UPLOAD_PATH)) {
//这里可以看到,他几乎是把所有的php别名都给ban了,其他地方和之前的都一样,就不审了。 $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.'/'.$file_name; if (move_uploaded_file($temp_file, $img_path)) { $is_upload = true; } else { $msg = '上传出错!'; } } else { $msg = '此文件不允许上传!'; } } else { $msg = UPLOAD_PATH . '文件夹不存在,请手工创建!'; } }
pass-05 大小写绕过
这里和上面一个题一样都,把.htaccess添加进去了。但是没有进行小写的转换,所以说可以使用大小写绕过
蚁剑连接成功
源码审计:
$is_upload = false; $msg = null; if (isset($_POST['submit'])) { if (file_exists(UPLOAD_PATH)) {
//可以看到,在最后面,他把.htaccess加进去了,但是在下面的变量里,没有像之前那样,使用strtolower函数将分离出来的后缀,都改为小写。 $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 . '文件夹不存在,请手工创建!'; } }
pass-06 trim
相较与上一题的没有进行小写转换,这一题是对文件名没有进行严格的检查,没有使用trim函数将文件名前后的字符去点,所以说可以BP抓包之后,在php后面加一个空格就行了
然后访问,蚁剑连接,这里只是没有时候trim函数进行首尾去空而已,就不审了。
pass-07 deldot,windows特性
在这一关,去掉了,也就是说,可以在文件名末尾加上点进行绕过,根据windows的特性,test.php.这样,最后执行的时候会把php.后面的点去掉。
$file_name = deldot($file_name);//删除文件名末尾的点
蚁剑连接成功
pass-08 ::$DATA文件流
由于之前burp suite一直抓不到本地包,所以上面做的几个题都是在 https://buuoj.cn/ 做的,现在终于可以本地复现了,开心,之前抓不到包,好像 是8080端口的锅
将端口改为8081,终于可以抓本地包,可以更加清楚的知道上传文件的变化了。
在这一关,缺少了这个函数对文件后缀的限定,
str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
::$DATA是windows上的一个文件流特性,必须在windows上,且是php文件,源文件的时候加上::$DATA,windows会把::$DATA之后的数据当作文件流来处理
不会检测后缀名.且保持"::$DATA"之前的文件名。
这里上传成功之后,在upload下的文件名自动的去掉了::$data,之后直接访问php就行了,然后蚁剑连接
pass-09 代码逻辑漏洞
这一关,其实和第四关差不多,只不过,第四关没有过滤掉.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); //首尾去空
、
访问shell.php,蚁剑连接
pass-10 后缀名双写绕过
看一下这一关关键的源代码
$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;
可以看到,直接去掉了黑名单里面存在的文件名后缀,不过只是进行了一次,所以说可以后缀名双写绕过
可以看到上传之后又两个文件,访问php文件,蚁剑连接即可
pass-11 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类型文件!"; } }
从代码上可以看出,设置了一个白名单,只允许jpg/png/gif后缀的文件上传,但是在后面的保存的时候,文件名是拼接的,其中前面是可控的
$img_path = $_GET['save_path']."/".rand(10, 99).date("YmdHis").".".$file_ext; //save_path是可控的部分
这时候就可以使用00截断进行尝试,00截断使用的条件:
PHP版本小于5.3.4 php.ini配置文件里magic_quotes_gpc=Off
这样就可以上传成功了,然后访问upload/13.php,蚁剑连接
pass-12 00截断
还是和上面一样,不过将save_path的传参方式变成了POST,GET传参的时候,会自动进行URL解码,POST不会,
访问,连接即可。
pass-13 图片马文件包含
根据关卡要求,需要上传图片马,以及使用文件包含
来看一下源代码
function getReailFileType($filename){ $file = fopen($filename, "rb"); $bin = fread($file, 2); //只读2字节 fclose($file); $strInfo = @unpack("C2chars", $bin);
//pcak函数是将对应参数打包成二进制字符串,uppack($format,$data)就是进行解包,format是解包时使用的数据格式,C是无符号字符 $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 = "上传出错!"; } } }
pack函数例子:
test.php
<?php echo pack('C3',80,72,80); ?>
test.php里面的C表示无符号的字符,3就是后面对应的参数个数,前面的数字必须小于等于后面的参数个数。
从源码可以知道,后端是对上传文件进行了文件头的的检测。图片马上传成功之后,还不能直接访问到
还需要文件包含才能成功执行php代码
包含之后还是出错了
网上查了发现,发现是我自己phpstudey使用的php版本低了,做上面的00截断的题的时候,用了5.2版本的php,没改过来,所以这里报了这样的错误
将php改为5.4.45之后就可以成功了
pass-14 图片马/getimagesize
核心源代码
$types = '.jpeg|.png|.gif'; if(file_exists($filename)){ $info = getimagesize($filename); //判断文件,如果是图片,就返回一个数组,数组里面有图片大小和后缀 $ext = image_type_to_extension($info[2]); //$info[2]=后缀名
这里使用getimagesize函数来判断文件,用来获取图像的大小及后缀名,成功的话,返回一个数组,失败返回false
也是只检查了文件头,绕过的话,只需要制造一个图片马,或者是上传一个一句话木马,抓包,然后在一句话内容前面加上图片的文件头就行了
然后访问,蚁剑连接
pass-15 图片马/exif_imagetype
这题也是一样,和上面两个图片马,也是检查了文件的文件头格式
这里使用的是exif_imagetype函数来判断
方法个上面一样,就不多说了。