安全-Pass07之黑名单空格绕过(upload-labs)
Posted 小狐狸FM
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了安全-Pass07之黑名单空格绕过(upload-labs)相关的知识,希望对你有一定的参考价值。
文章目录
前言
php
即Hypertext Preprocessor
超文本预处理器,多用于web
后端- BUUCTF的upload-labs在线靶场和本地的靶场有点差别,如果用文章的方法没法绕过时,注意看一下源码是否一致
相关介绍
Upload-labs文件上传漏洞(空格绕过)——Pass06
其他介绍
一、题目
php
后端代码
<?php
$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",".ini");
$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 . '文件夹不存在,请手工创建!';
}
}
?>
二、WriteUp
[1]. 函数介绍
deldot是靶场自己定义的函数,不是PHP自带的函数
PHP函数 | 介绍 |
---|---|
file_exists(路径) | 判断指定的文件或目录是否存在,不存在返回true,否则返回false |
in_array(变量,数组) | 如果变量存在于数组就返回true,否则返回false |
isset(变量) | 如果变量存在且值不为null返回true,否则返回false |
move_uploaded_file(文件路径,文件夹路径) | 将文件移动到指定文件夹下 |
strrchr(字符串,字符) | 如果字符存在于字符串时,返回第一次找到的字符至字符串末尾的子串。不存在于字符串时就返回false |
strtolower(字符串) | 将字符串全部转换成小写 |
str_ireplace(字符串1,字符串2,字符串3) | 在字符串3中搜索,如果含有字符串1的子串就替换成字符串2 |
[2]. 源码审计
(1). 变量判断
- 对
php
代码进行审计,if语句比较多最好从外往内分析- 第一条
if
语句只是判断了一下提交的post
请求中submit
参数是否被设置且非空
PHP:isset - Manual
$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {//第一条if语句
//代码
}
(2). 路径判断
- 第二条语句判断了一下文件上传的路径存不存在,存在的话就执行里面的代码,不存在就给
$msg
设置回显信息- 通过
Seay
审计的全局搜索功能可以找到UPLOAD_PATH
是在config.php
中被定义的
Pass-02\\index.php
的代码中包含了上一级目录下的config.php
然后这个变量就可以在Pass-02\\index.php
中直接使用../
表示访问上一级的目录,所以upload-labs-master\\Pass-02\\index.php
包含的是upload-labs-master\\config.php
- 当
UPLOAD_PATH
变量在upload-labs-master\\Pass-02\\index.php
中被调用时,就会设置上传的父文件夹为upload-labs-master\\upload
$is_upload = false;
$msg = null;
if (xxx) {//第一条if语句
if (file_exists(UPLOAD_PATH)) {//第二条if语句
//代码
}else {//第二条if语句为假
$msg = UPLOAD_PATH.'文件夹不存在,请手工创建!';
}
}
(3). 黑名单
$deny_ext
是一个数组类型,存储了需要被过滤的后缀名
之后会对文件的后缀判断,如果后缀符合其中的一个,文件就会被过滤
".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",".ini"
//代码
if (xxx) {//第一条if语句
if (xxx) {//第二条if语句
$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",".ini");
//代码
}else {//第二条if语句为假
//代码
}
}
(4). 获取上传文件名
$_FILES['upload_file']['name']
会获取上传文件的名称,如下图的test3.txt
//代码
if (xxx) {//第一条if语句
if (xxx) {//第二条if语句
//代码
$file_name = $_FILES['upload_file']['name'];
//代码
}else {//第二条if语句为假
//代码
}
}
(5). 删除尾部小数点
deldot($file_name)
将会删除变量$file_name
中的最后一个小数点,经过查询发现这个函数不是php自带的,而是靶场中自己定义的- 通过
Seay
源码审计系统可以找到函数的位置是在common.php
文件中
如果不想去思考其中的算法,可以使用php
的集成开发环境phpstorm
运行一下函数看看结果
//代码
if (xxx) {//第一条if语句
if (xxx) {//第二条if语句
//代码
$file_name = deldot($file_name);//删除文件名末尾的点
//代码
}else{//第二条if语句为假
//代码
}
}
(6). 获取文件后缀
strrchr($file_name, '.')
会返回小数点和文件后缀组成的子串。
假设文件名为test3.php.jpg
,得到的$file_txt
值就是.jpg
- PHP: strrchr - Manual
//代码
if (xxx) {//第一条if语句
if (xxx) {//第二条if语句
//代码
$file_ext = strrchr($file_name, '.');
//代码
}else{//第二条if语句为假
//代码
}
}
(7). 小写转换
strtolower($file_ext)
就是对得到的字符串后缀进行全小写处理,这样就没法进行文件后缀的大小写绕过了
PHP: strtolower - Manual
//代码
if (xxx) {//第一条if语句
if (xxx) {//第二条if语句
//代码
$file_ext = strtolower($file_ext); //转换为小写
//代码
}else{//第二条if语句为假
//代码
}
}
(8). 置空::$DATA
- 在window的时候如果文件名+
::$DATA
会把::$DATA
之后的数据当成文件流处理,不会检测后缀名,具体参考【文件上传绕过】八、: : $ DATA上传绕过
如果文件名为test.php::$DATA.jpg
时,在windows中会删除::$DATA
及之后的内容,则上传window服务器后的文件名为test.php
str_ireplace('::$DATA', '', $file_ext)
将会用空字符来替换变量$file_ext
中的::$DATA
子串,防止了::$DATA
的上传绕过- PHP: str_ireplace - Manual
//代码
if (xxx) {//第一条if语句
if (xxx) {//第二条if语句
//代码
$file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
//代码
}else{//第二条if语句为假
//代码
}
}
(9). 黑名单过滤
$file_ext
存储的是文件后缀(含有小数点)
数组$deny_ext
的值如下,当文件的后缀不为其中的时,才能进入if语句- PHP: in_array - Manual
".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",".ini"
//代码
if (xxx) {//第一条if语句
if (xxx) {//第二条if语句
//代码
if(!in_array($file_ext, $deny_ext)) {//第三条if语句
//代码
} else {//第三条if语句为假
$msg = '此文件不允许上传';
}
}else{//第二条if语句为假
//代码
}
}
(10). 文件存储路径设置
- 在上传文件的时候,文件都会被存储在一个临时的文件夹下
我们不需要知道具体路径,只需要通过tmp_name参数获取路径即可UPLOAD_PATH
的值为../
,在config.php
文件中定义
$file_ext
是文件的后缀,包含了小数点
rand(1000,9999)
则是从1000到9999数字中取随机数,范围左闭右闭
date("YmdHis")
用于获取当前的时间,以xxxx年xx月xx日xx时xx分xx秒
为格式
两个字符串变量之间的连接用小数点- PHP: rand - Manual
PHP: date - Manual
//代码
if (xxx) {//第一条if语句
if (xxx) {//第二条if语句
//代码
if(xxx) {//第三条if语句
$temp_file = $_FILES['upload_file']['tmp_name'];
$img_path = UPLOAD_PATH.'/'.date("YmdHis").rand(1000,9999).$file_ext;
//代码
} else {//第三条if语句为假
//代码
}
}else{//第二条if语句为假
//代码
}
}
(11). 移动临时文件
- 剩余的代码仅是用于移动上传的文件,没有对文件进行过滤操作
- PHP: move_uploaded_file - Manual
//代码
if (xxx) {//第一条if语句
if (xxx) {//第二条if语句
//代码
if(xxx) {//第三条if语句
//代码
if (move_uploaded_file($temp_file,$img_path)) {//第四条if语句
$is_upload = true;
} else {//第四条if语句为假
$msg = '上传出错!';
}
} else {//第三条if语句为假
//代码
}
}else{//第二条if语句为假
//代码
}
}
[3]. 空格绕过
- 源码中没有将文件前后的空格过滤,所以可以在文件末尾添加一个空格
- 上传到服务器的时候,得到的后缀就是
.php空格
,从而绕过黑名单的限制
又因为.php空格
不符合windows文件的命名规则,所以服务器存储的时候会将末尾的空格去掉,文件末尾就变回了.php
复制图片链接,并访问,没有报错表示
.php
文件解析成功
在服务器中,文件是以
.php
的后缀存储
用蚁剑或菜刀连接shell
以上是关于安全-Pass07之黑名单空格绕过(upload-labs)的主要内容,如果未能解决你的问题,请参考以下文章
安全-Pass11之黑名单双写绕过(upload-labs)
安全-Pass09之黑名单::$DATA绕过(upload-labs)
安全-Pass05之黑名单.user.ini绕过(upload-labs)