安全-Pass05之黑名单.user.ini绕过(upload-labs)

Posted 小狐狸FM

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了安全-Pass05之黑名单.user.ini绕过(upload-labs)相关的知识,希望对你有一定的参考价值。

前言

  • phpHypertext Preprocessor超文本预处理器,多用于web后端
    .ini 文件是Initialization File的缩写,即初始化文件,是windows的系统配置文件所采用的存储格式,统管windows的各项配置
  • Pass05与Pass04相比,删除了文件后缀.ini的处理,增加了.htaccess的黑名单限制
    BUUCTF的upload-labs在线靶场和本地的靶场有点差别,如果用文章的方法没法绕过时,注意看一下源码是否一致

相关介绍

PHP 百度百科

ini文件 百度百科

PHP: PHP 手册 - Manual

PHP: .user.ini文件 - Manual

PHP: php.ini配置选项列表

PHP 通过.user.ini 绕过黑名单限制

其他介绍

文件上传绕过思路集合

upload-labs靶场下载

upload-labs在线靶场-BUUCTF

蚁剑AntSword

菜刀Cknife

Seay

一、题目

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). 首尾去空

//代码
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). 移动临时文件

//代码
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绕过

  • .user.ini配置参考的PHP 通过.user.ini 绕过黑名单限制,在.user.ini设置auto_prepend_file属性
  • 下方的配置的意思是在.user.ini相同的目录下,所有的php文件都包含test5.jpg这个文件
    所以之后我们就可以利用到readme.php包含一个我们上传的木马文件,再连接readme.php文件达到连接一句话木马的目的

上传.user.ini文件到服务器,返回了一个图片,可以右键复制链接查看
readme.php里面的内容不是很重要,只需要这个文件是存在的就行