安全-Pass05之黑名单.user.ini绕过(upload-labs)
Posted 小狐狸FM
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了安全-Pass05之黑名单.user.ini绕过(upload-labs)相关的知识,希望对你有一定的参考价值。
文章目录
前言
php
即Hypertext Preprocessor
超文本预处理器,多用于web
后端
.ini
文件是Initialization File
的缩写,即初始化文件,是windows
的系统配置文件所采用的存储格式,统管windows
的各项配置- Pass05与Pass04相比,删除了文件后缀
.ini
的处理,增加了.htaccess
的黑名单限制
BUUCTF的upload-labs在线靶场和本地的靶场有点差别,如果用文章的方法没法绕过时,注意看一下源码是否一致
相关介绍
其他介绍
一、题目
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");
$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 . '文件夹不存在,请手工创建!';
}
}
?>
二、WriteUp
[1]. 函数介绍
deldot是靶场自己定义的函数,不是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 |
trim(字符串) | 删除字符串前后的空白符,空白符: 空格、制表符(\\t )、换行符(\\n )、回车符(\\r )、空字节符(\\0 )和垂直制表符(\\x0B ) |
[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
是一个数组类型,存储了需要被过滤的后缀名
之后会对文件的后缀判断,如果后缀符合其中的一个,文件就会被过滤- 对其中的后缀分析可以发现,还是有重要的后缀没进行过滤,如
.ini
".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"
//代码
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");
}else {//第二条if语句为假
//代码
}
}
(4). 首尾去空
$_FILES['upload_file']['name']
会获取上传文件的名称,如下图的test3.txt
trim($_FILES['upload_file']['name'])
就是删除文件名称首尾
的空白符,然后赋值给变量$file_name
PHP:trim - Manual
//代码
if (xxx) {//第一条if语句
if (xxx) {//第二条if语句
//代码
$file_name = trim($_FILES['upload_file']['name']);
//代码
}else{//第二条if语句为假
//代码
}
}
(5). 删除尾部小数点
deldot($file_name)
将会删除变量$file_name
中的最后一个小数点,经过查询发现这个函数不是php自带的,而是靶场中自己定义的
如果你去百度搜deldot
是搜不到官方资料的,如果有文章介绍了,估计是文章的作者没意识到这个问题- 通过
Seay
源码审计系统可以找到函数的位置是在common.php
文件中
如果不想去思考其中的算法,可以使用php
的集成开发环境phpstorm
运行一下函数看看结果
//代码
if (xxx) {//第一条if语句
if (xxx) {//第二条if语句
//代码
$file_name = deldot($file_name);//删除文件名末尾的点
//代码
}else{//第二条if语句为假
//代码
}
}
(6). 获取文件后缀
- 之前的步骤删除了文件名末尾的小数点后赋值给了
$file_name
strrchr($file_name, '.')
会返回小数点和文件后缀组成的子串。
假设文件名为test3.php%00.jpg
,得到的$file_txt
值就是.jpg
因为之后的第三个if语句是直接对.jpg
进行判断,所以此时就不能通过%00
截断进行绕过了- PHP: strrchr - Manual
//代码
if (xxx) {//第一条if语句
if (xxx) {//第二条if语句
//代码
$file_ext = strrchr($file_name, '.');
//代码
}else{//第二条if语句为假
//代码
}
}
(7). 小写转换
strtolower($file_ext)
就是对得到的字符串.jpg
进行全小写处理,这样就没法进行文件后缀的大小写绕过了
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). 首尾去空
trim($file_ext)
删除$file_ext
变量首尾的空白符- PHP: trim - Manual
//代码
if (xxx) {//第一条if语句
if (xxx) {//第二条if语句
//代码
$file_ext = trim($file_ext); //收尾去空
//代码
}else{//第二条if语句为假
//代码
}
}
(10). 黑名单过滤
$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"
//代码
if (xxx) {//第一条if语句
if (xxx) {//第二条if语句
//代码
if(!in_array($file_ext, $deny_ext)) {//第三条if语句
//代码
} else {//第三条if语句为假
$msg = '不允许上传.asp,.aspx,.php,.jsp后缀文件!';
}
}else{//第二条if语句为假
//代码
}
}
(11). 文件存储路径设置
- 在上传文件的时候,文件都会被存储在一个临时的文件夹下
我们不需要知道具体路径,只需要通过tmp_name参数获取路径即可
UPLOAD_PATH
的值为../
,在config.php
文件中定义- 与Pass03相比,Pass03是以上传的时间作为文件名,而Pass04文件上传后存储的名称是不变的
- PHP: in_array - Manual
安全-Pass03之黑名单绕过(upload-labs)
//代码
if (xxx) {//第一条if语句
if (xxx) {//第二条if语句
//代码
if (!in_array($file_ext, $deny_ext)) {//第三条if语句
$temp_file = $_FILES['upload_file']['tmp_name'];
$img_path = UPLOAD_PATH . '/' . $file_name;
//代码
} else {//第三条if语句为假
$msg = '此文件不允许上传!';
}
}else {//第二条if语句为假
//代码
}
}
(12). 移动临时文件
- 剩余的代码仅是用于移动上传的文件,没有对文件进行过滤操作
- 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]. ini绕过
- 先看一下提示,上传的目录存在一个名为
readme.php
文件
因为.ini
后缀的文件可以上传,所以我们可以上传一个.user.ini
文件来改变服务器配置- PHP: .user.ini文件 - Manual
PHP: php.ini配置选项列表
.user.ini
配置参考的PHP 通过.user.ini 绕过黑名单限制,在.user.ini
设置auto_prepend_file
属性- 下方的配置的意思是在
.user.ini
相同的目录下,所有的php文件都包含test5.jpg
这个文件
所以之后我们就可以利用到readme.php
包含一个我们上传的木马文件,再连接readme.php
文件达到连接一句话木马的目的
上传
.user.ini
文件到服务器,返回了一个图片,可以右键复制链接查看
readme.php
里面的内容不是很重要,只需要这个文件是存在的就行
- 然后再上传一个一句话木马文件,名字需要与
.user.ini
配置的文件名相同- 因为
jpg
以上是关于安全-Pass05之黑名单.user.ini绕过(upload-labs)的主要内容,如果未能解决你的问题,请参考以下文章
安全-Pass07之黑名单空格绕过(upload-labs)
安全-Pass09之黑名单::$DATA绕过(upload-labs)
安全-Pass11之黑名单双写绕过(upload-labs)