目录
文件上传之黑白名单绕过
文件上传常见的验证方式
后缀名、类型、文件头等
后缀名:黑名单、白名单
文件类型:MIME信息
文件头:内容头信息
其中后缀名的验证方式就是验证文件的后缀是否是允许上传的文件的后缀,后缀名验证方式又分为黑名单和白名单的方式;其中的黑名单指的就是规定不允许上传的文件的格式(如规定不允许上传.asp、.jsp、.aspx、.php、.cgi、.war等等);而白名单是指明确可以上传的格式后缀(如.jpg、.png、.zip、.rar、.gif);显然白名单的方式是更加安全一些。
接下来,通过upload_labs来配合学习黑白名单的绕过知识点。upload_labs是一个使用
php
语言编写的,专门收集渗透测试和CTF中遇到的各种上传漏洞的靶场。目前一共21关,每一关都包含着不同上传方式。项目地址:https://github.com/c0ny1/upload-labs;直接下载解压到phpstudy中的WWW目录下即可使用。
第一关
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;
}
}
其中的这里的allow_ext就是允许上传的后缀名,file.substring截取了文件名中的最后一个点开始,到末尾的信息,也就是说文件的类型,这里就是使用到了javascript的验证。我们便可以通过抓包修改文件的后缀。下面的图是正常上传的原始图片,然后就是修改后缀名为.php,然后再发送出去。
通过相应包我们可以找到上传的地址,然后通过地址直接进行访问:../upload/logo.php
第二关
$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.'文件夹不存在,请手工创建!';
}
}
首先就是判断上传的路径是否存在,如果存在的话,获取文件的MIME信息。这里的$_FILES的用法如下:
$_FILES['myFile']['name'] 客户端文件的原名称。
$_FILES['myFile']['type'] 文件的 MIME 类型,例如上面图片中的"image/jpeg"。
$_FILES['myFile']['size'] 已上传文件的大小,单位为字节。
$_FILES['myFile']['tmp_name'] 文件被上传后在服务端储存的临时文件名,一般是系统默认。可以在php.ini的upload_tmp_dir 指定,但用 putenv() 函数设置是不起作用的。
$_FILES['myFile']['error'] 和该文件上传相关的错误代码。['error'] 是在 PHP 4.2.0 版本中增加的。
所以说第二关的代码就是检验MIME信息,所以我们可以通过抓包然后修改MIME的信息来进行绕过。
Content-Type: application/octet-stream //这里是上传.php文件的MIME信息将其修改为image/png
第三关
通过$deny_ext语句,我们就可以判断出,这里采用的是黑名单,也就是说我们不可以上传.asp、.aspx、.php、.jsp等后缀名的文件;
$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, '.');// strrchr函数:查找字符在指定字符串中从右面开始的第一次出现的位置,如果成功,返回该字符以及其后面的字符,如果失败,则返回 NULL。 这里就是取出他的后缀
$file_ext = strtolower($file_ext); //转换为小写 将后缀中的所有的字符全部转换为小写
$file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
$file_ext = trim($file_ext); //收尾去空 去掉后缀名中最后的空格
apache服务器能够使用php解析 .phtml 、.php5 、.php3等文件。所以我们可以将后缀名修改为.php3来绕过。
第四关
$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解析上传一个正常的png图片,其中的代码为:
<FilesMatch "info.png">
setHandler application/x-httpd-php
</FilesMatch>
然后我们上传一个名为info.png的图片即可。
第六关
显然比之前的关卡少了将文件中的字符转换成小写的操作。因此我们可以通过大小写混合来绕过。 上传文件.pHP即可绕过。
第七关
显然相比之前的代码少了最后的去空操作,所以我们可以通过抓包的操作,修改文件名的后缀为.php ,通过空格来绕过。
第八关
相比之前代码中少了去除文件末尾的点,所以我们可以在文件的后缀名中增加一个点来绕过。
第九关
相比之前的代码减少了去除字符串::$DATA的操作,因此我们可以在文件名的后缀上加上::$DATA,其中的::$DATA就是截取它之前的文件名。所以我们可以上传1.php::$DATA。在上传后就变成了1.php。
第十关
我们可以很明显的看出,所有的操作(如删除文件名末尾的点、去除文件名末尾的空格)都只有过滤一次,所以我们可以构造后缀为.php. .来绕过。
第十一关
这里代码将$deny_ext中的后缀名替换为空,所以我们可以通过双写来进行绕过,所以我们上传文件3.pphphp,最后上传之后我们来访问3.php即可。
后面的关卡会陆续更新的......