前言
php 即Hypertext Preprocessor 超文本预处理器,多用于web 后端- BUUCTF的upload-labs在线靶场和本地的靶场有点差别,如果用文章的方法没法绕过时,注意看一下源码是否一致
相关介绍
PHP 百度百科
PHP: PHP 手册 - Manual
文件上传漏洞“%00截断”绕过
其他介绍
文件上传绕过思路集合
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",".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);
$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自带的函数
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). 变量判断
$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
}
(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 (file_exists(UPLOAD_PATH)) {
}else {
$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 (xxx) {
$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 {
}
}
(4). 首尾去空
$_FILES['upload_file']['name'] 会获取上传文件的名称,如下图的test3.txt trim($_FILES['upload_file']['name']) 就是删除文件名称首尾 的空白符,然后赋值给变量$file_name PHP:trim - Manual
if (xxx) {
if (xxx) {
$file_name = trim($_FILES['upload_file']['name']);
}else{
}
}
(5). 删除尾部小数点
deldot($file_name) 将会删除变量$file_name 中的最后一个小数点,经过查询发现这个函数不是php自带的,而是靶场中自己定义的- 通过
Seay 源码审计系统可以找到函数的位置是在common.php 文件中 如果不想去思考其中的算法,可以使用php 的集成开发环境phpstorm 运行一下函数看看结果
if (xxx) {
if (xxx) {
$file_name = deldot($file_name);
}else{
}
}
(6). 获取文件后缀
- 之前的步骤删除了文件名末尾的小数点后赋值给了
$file_name strrchr($file_name, '.') 会返回小数点和文件后缀组成的子串。 假设文件名为test3.php.jpg ,得到的$file_txt 值就是.jpg - PHP: strrchr - Manual
if (xxx) {
if (xxx) {
$file_ext = strrchr($file_name, '.');
}else{
}
}
(7). 小写转换
if (xxx) {
if (xxx) {
$file_ext = strtolower($file_ext);
}else{
}
}
(8). 置空::$DATA
- 在window的时候如果文件名+
::$DATA 会把::$DATA 之后的数据当成文件流处理,不会检测后缀名 如果文件名为test.php::$DATA.jpg 时,在windows中会删除::$DATA 及之后的内容,则上传window服务器后的文件名为test.php str_ireplace('::$DATA', '', $file_ext) 将会用空字符来替换变量$file_ext 中的::$DATA 子串,防止了::$DATA 的上传绕过- PHP: str_ireplace - Manual
【文件上传绕过】八、: : $ DATA上传绕过
if (xxx) {
if (xxx) {
$file_ext = str_ireplace('::$DATA', '', $file_ext);
}else{
}
}
(9). 首尾去空
if (xxx) {
if (xxx) {
$file_ext = trim($file_ext);
}else{
}
}
(10). 黑名单过滤
".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 (xxx) {
if(!in_array($file_ext, $deny_ext)) {
} else {
$msg = '此文件不允许上传';
}
}else{
}
}
(11). 文件存储路径设置
- 在上传文件的时候,文件都会被存储在一个临时的文件夹下
我们不需要知道具体路径,只需要通过tmp_name参数获取路径即可 UPLOAD_PATH 的值为../ ,在config.php 文件中定义 - PHP: in_array - Manual
if (xxx) {
if (xxx) {
if (xxx) {
$temp_file = $_FILES['upload_file']['tmp_name'];
$img_path = UPLOAD_PATH . '/' . $file_name;
} else {
}
}else {
}
}
(12). 移动临时文件
if (xxx) {
if (xxx) {
if(xxx) {
if (move_uploaded_file($temp_file,$img_path)) {
$is_upload = true;
} else {
$msg = '上传出错!';
}
} else {
}
}else{
}
}
[3]. 点空点绕过
- 可以在文件后添加
.空格. 绕过代码限制,假设上传的文件名为test10.php.空格. - 源码中对文件名和后缀处理的顺序依次是,
trim() 、deldot()、strrchr()、strtolower()、str_ireplace()、trim()
变量 | 传入的值 | 处理后 | 代码 | 代码作用 |
---|
$file_name | test10.php.空格. | test10.php.空格. | $file_name = trim($_FILES['upload_file']['name']) | 删除首尾空白符 | $file_name | test10.php.空格. | test10.php.空格 | $file_name = deldot($file_name) | 删除末尾小数点 | $file_ext | test10.php.空格 | .空格 | $file_ext = strrchr($file_name, '.') | 获取最后一个小数点及右侧的字符 | $file_ext | .空格 | .空格 | $file_ext = strtolower($file_ext) | 小写转换 | $file_ext | .空格 | .空格 | $file_ext = str_ireplace('::$DATA', '', $file_ext) | 置空操作 | $file_ext | .空格 | . | $file_ext = trim($file_ext) | 删除首尾空白符 |
- 结束上面的操作后,
$file_name 值为test10.php.空格 ,$file_ext 值为. 因为. 不在黑名单之中,所以可以进入第三条if语句 - 存储文件时是以
$file_name 变量的值,test10.php.空格 作为文件名 因为文件名不符合windows规则,上传到服务器之后会保留文件名test10.php
if (xxx) {
if (xxx) {
if(!in_array($file_ext, $deny_ext)) {
$img_path = UPLOAD_PATH.'/'.$file_name;
} else {
$msg = '此文件类型不允许上传!';
}
}else{
}
}
使用蚁剑或菜刀连接shell,URL末尾的小数点可有可无
|