upload-labs通关笔记-第20关 文件上传之杠点绕过
目录
一、pathinfo函数
二、move_uploaded_file函数
三、代码审计
1、流程分析
2、渗透思路
(1)方法一
(2)方法二
A.类 Unix 系统示例
B.Windows 系统示例
四、渗透实战
1、制作脚本test20.php
2、浏览图片
3、bp开启拦截
4、点击上传
5、bp拦截
6、save_name文件名修改
7、发包并获取脚本地址
8、访问脚本
本文通过《upload-labs靶场通关笔记系列》来进行upload-labs靶场的渗透实战,本文讲解upload-labs靶场第20关图片马之杠点绕过渗透实战。
一、pathinfo函数
pathinfo是 PHP 中的一个内置函数,其作用是返回文件路径的信息。借助这个函数,你能够提取出文件路径里的不同部分,像目录名、文件名、扩展名等。
pathinfo(string $path, int $options = PATHINFO_ALL): mixed
(1)参数说明
参数 | 类型 | 说明 |
---|---|---|
$path | string | 要解析的文件路径 |
$options | int | 可选参数,指定返回的信息部分 |
(2)返回值
-
当不指定
$options
时,返回包含所有信息的关联数组 -
当指定
$options
时,返回指定部分的字符串
(3)使用示例
假设path为/var/html/www/test20.php,那么使用示例如下所示。
// 只获取目录名
echo pathinfo($path, PATHINFO_DIRNAME); // 输出:/var/html/www/// 只获取文件名(带扩展名)
echo pathinfo($path, PATHINFO_BASENAME); // 输出:test20.php// 只获取文件扩展名
echo pathinfo($path, PATHINFO_EXTENSION); // 输出:php// 只获取文件名(不带扩展名)
echo pathinfo($path, PATHINFO_FILENAME); // 输出:test20
二、move_uploaded_file函数
move_uploaded_file是 PHP 中的一个重要函数,主要用于将通过 HTTP POST 上传的临时文件移动到指定的目标位置。在处理文件上传时,它起到了关键作用。
move_uploaded_file ( string $from , string $to ) : bool
(1)参数说明
$from
:这是必需的参数,代表上传文件在服务器上的临时存储路径。通常,我们可以从$_FILES
数组中获取这个临时文件的路径,例如$_FILES['upload_file']['tmp_name']
。$to
:同样是必需参数,指定了文件要被移动到的目标路径,也就是文件最终保存的位置。这个路径需要包含文件名。
(2)返回值
如果文件成功从临时位置移动到指定的目标位置,函数返回 true
;反之,如果移动过程中出现问题,比如目标路径不可写、临时文件不存在等,函数将返回 false
。
(3)安全性
move_uploaded_file是 PHP 中用于处理文件上传的函数,其安全性主要体现验证文件是否为合法上传文件,move_uploaded_file会检查指定的文件是否是通过 HTTP POST 上传的合法文件,这可以防止攻击者尝试移动服务器上已有的文件(如 /etc/passwd
)或通过其他方式创建的文件。
三、代码审计
打开靶场第20关,本关卡又恢复为通过后缀过滤黑名单的检测方法,具体源码如下所示。
这段代码实现了一个简单的文件上传功能,并且对上传的文件类型进行了限制,详细版注释如下所示。
// 初始化上传状态标志,默认设置为 false,表示文件未成功上传
$is_upload = false;
// 初始化消息变量,用于存储上传过程中的提示信息,初始值为 null
$msg = null;// 检查是否通过 POST 方式提交了名为 submit 的表单数据
if (isset($_POST['submit'])) {// 检查指定的上传目录是否存在if (file_exists(UPLOAD_PATH)) {// 定义一个数组,包含不允许上传的文件扩展名$deny_ext = array("php","php5","php4","php3","php2","html","htm","phtml","pht","jsp","jspa","jspx","jsw","jsv","jspf","jtml","asp","aspx","asa","asax","ascx","ashx","asmx","cer","swf","htaccess");// 从 POST 数据中获取用户指定的保存文件名$file_name = $_POST['save_name'];// 使用 pathinfo 函数提取文件名中的扩展名$file_ext = pathinfo($file_name, PATHINFO_EXTENSION);// 检查提取的文件扩展名是否不在禁止上传的扩展名数组中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)) {// 如果移动成功,将上传状态标志设置为 true$is_upload = true;} else {// 如果移动失败,设置提示消息,表明上传出错$msg = '上传出错!';}} else {// 如果文件扩展名在禁止上传的数组中,设置提示消息,禁止保存该类型文件$msg = '禁止保存为该类型文件!';}} else {// 如果上传目录不存在,设置提示消息,告知用户手动创建该目录$msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';}
}
1、流程分析
- 初始化变量:设置上传状态标志
$is_upload
为false
,并初始化消息变量$msg
为null
。 - 检查表单提交:当用户通过 POST 方式提交名为
submit
的表单时,开始处理上传逻辑。 - 检查上传目录:检查指定的上传目录
UPLOAD_PATH
是否存在。如果不存在,提示用户手动创建该目录。 - 文件类型检查:定义一个包含不允许上传的文件扩展名的数组
$deny_ext
。从用户指定的保存文件名中通过pathinfo($path, PATHINFO_EXTENSION)函数提取扩展名,并检查该扩展名是否在禁止列表中。如果在禁止列表中,提示用户禁止保存该类型文件。
$file_name = $_POST['save_name'];
$file_ext = pathinfo($file_name, PATHINFO_EXTENSION);
- 文件上传:如果文件扩展名不在禁止列表中,获取上传文件的临时存储路径,并使用move_uploaded_file函数将其移动到指定的保存路径。如果移动成功,将上传状态标志设置为
true
;否则,提示用户上传出错。
$temp_file = $_FILES['upload_file']['tmp_name'];// 获取上传文件在服务器上的临时存储路径
$img_path = UPLOAD_PATH . '/' . $file_name; // 拼接上传文件完整保存路径,包括上传目录和文件名
if (move_uploaded_file($temp_file, $img_path)){// 尝试将临时文件移动到指定的保存路径$is_upload = true;// 如果移动成功,将上传状态标志设置为 true
} else {$msg = '上传出错!';// 如果移动失败,设置提示消息,表明上传出错
}
2、渗透思路
(1)方法一
黑名单未检查大小写、没有尾部去空,没有尾部去点,也没有尾部去::$data等处理逻辑,故而可以使用upload-labs靶场的第6关卡、第7关卡、第8关卡和第九关的渗透方法绕过。假设计划上次脚本test.php,那么渗透方法包括如下方法
- 大小写变体:将文件save_name名重命名为"test.pHP"
- 文件名尾部增加空格:bp改包,将save_name文件名改为"test.php "
- 文件名尾部增加点:bp改包,将save_name文件名改为"test.php."
- 文件名尾部增加::$data:bp改包,将save_name文件名改为"test.php::$data"
(2)方法二
- 修改save_name为test.php/.此时调用pathinfo会认为后缀名为空,非黑名单后缀,绕过了黑名单检查,具体如下所示。
$file_name = $_POST['save_name'];
$file_ext = pathinfo($file_name, PATHINFO_EXTENSION);
- 文件名为test.php/.由于函数move_uploaded_file会忽略/.故而上传后的文件名为test.php从而渗透成功
// 获取上传文件在服务器上的临时存储路径$temp_file = $_FILES['upload_file']['tmp_name'];// 拼接上传文件的完整保存路径,包括上传目录和文件名$img_path = UPLOAD_PATH . '/' . $file_name;// 尝试将临时文件移动到指定的保存路径if (move_uploaded_file($temp_file, $img_path)){$is_upload = true;// 如果移动成功,将上传状态标志设置为 true} else {$msg = '上传出错!';// 如果移动失败,设置提示消息,表明上传出错}
A.类 Unix 系统示例
在类 Unix 系统(如 Linux、macOS)和 Windows 系统中,都存在特定的路径解析规则。单个点号(.)在路径中代表当前目录。所以当路径里包含 /. 时,系统会把它理解为当前目录,从而在路径解析过程中 “忽略” 它。 在类 Unix 系统的 shell 中,下面这些路径是等价的:
/path/to/dir/.
/path/to/dir
当你使用 cd /path/to/dir/.
和 cd /path/to/dir
时,效果是一样的,都会进入 /path/to/dir
目录。
B.Windows 系统示例
Windows 系统在路径解析上也有类似规则。例如,在命令提示符中输入 cd C:\Users\Admin\.
和 cd C:\Users\Admin
,最终都会进入 C:\Users\Admin
目录。
四、渗透实战
1、制作脚本test20.php
<?
phpphpinfo();
?>
2、浏览图片
进入靶场20关,选择test20.php,注意下面保存名称默认为upload-19.jpg,具体如下所示。
3、bp开启拦截
4、点击上传
5、bp拦截
bp捕获到上传报文,下图红框的部分涉及到两个文件名,其中根据源码分析我们指导save_name即为需要修改的文件名upload-19.jpg,需要将"upload-19.jpg"后缀改为"upload-20.php/.",修改之前文件名为"upload-19.jpg",如下所示
6、save_name文件名修改
upload-19.jpg改为"upload-20.php/.",修改后效果如下所示。
7、发包并获取脚本地址
将bp的inception设置为off,此时修改后的报文发送成功。
回到靶场的Pass20关卡,图片已经上传成功,在图片处右键复制图片地址。
右键图片获取图片地址,如下所示获取到图片URL。
http://127.0.0.1/upload-labs/upload/upload-20.php/
8、访问脚本
如下所示访问上传脚本获取到服务器的php信息,证明文件上传成功。