upload-labs通关笔记-第13关 文件上传之白名单POST法
目录
一、白名单过滤
二、%00截断
1.截断原理
2、截断条件
(1)PHP版本 < 5.3.4
(2)magic_quotes_gpc配置为Off
(3)代码逻辑存在缺陷
三、源码分析
1、代码审计
(1)文件存储依赖字符串截断
(2)路径拼接可控
2、GET型和POST型区别
3、攻击思路
四、渗透实战
1、构建脚本test13.jpg
2、配置php服务
(1)安装php5.3.4以下的php版本
(2)切换php5.3.4以下的php版本
(3)关闭magic_quotes_gpc配置
(4)重启Apache服务
3、打开靶场
4、bp开启拦截
5、点击上传
6、bp拦截
7、POST参数增加test13.php%00
8、手动URL解码
9、发包并获取脚本地址
10、访问脚本
本文通过《upload-labs靶场通关笔记系列》来进行upload-labs靶场的渗透实战,本文讲解upload-labs靶场第13关白名单POST法渗透实战。
一、白名单过滤
在文件上传功能中,为了保障Web服务安全,通常会对上传的文件采用文件类型过滤机制。其中白名单机制是一种较为安全的做法,它会明确规定允许上传的文件类型,只有符合白名单列表的文件才能被上传,其他类型的文件则会被拒绝。例如,一个图片上传功能可能只允许上传 .jpg、 png、 .gif等类型的文件。黑名单过滤方法和白名单的过滤方法的区别具体如下图所示。
对比项目 | 白名单 | 黑名单 |
---|---|---|
定义 | 明确规定允许上传的文件类型列表,只有在白名单内的文件类型才允许上传。 | 列出禁止上传的文件类型列表,除黑名单内的文件类型外,其他类型文件允许上传。 |
安全性 | 通常安全性较高,因为只允许已知的安全文件类型上传,能有效防止大多数恶意脚本上传。 | 安全性相对较低,因为需要不断更新和完善黑名单,以防止新出现的恶意文件类型绕过检测。 |
误判可能性 | 较低,只要文件类型在白名单中,一般不会被误判为不合规文件。 | 较高,可能会因为黑名单设置不全面或对某些特殊情况考虑不足,导致合法文件被误判为非法文件。 |
绕过难度 | 相对较难绕过,除非攻击者能找到白名单机制的安全风险,如文件类型检查不严格、路径拼接问题等。 | 相对容易绕过,攻击者可以通过各种方法,如更改文件后缀名大小写、添加特殊字符、利用系统特性(如 Windows 下的点绕过、空格绕过、::$DATA 绕过等)来尝试绕过黑名单检测。 |
二、%00截断
1.截断原理
%00截断是一种利用字符串处理特性和编程语言安全风险的攻击手段,%00截断的原理如下:空字符\0(URL 编码为%00)被定义为字符串结束符,程序处理字符串时一旦遇到该字符,便会认定字符串到此结束。一般来说就是这一个特定字符起截断作用。0x00,%00,/00之类的截断,都是一样的,只是不同表示而已。\0
代表空字符,在 URL 编码中,其对应的十六进制值是 00
,所以按照 URL 编码规则会被编码为 %00。
字符 | ASCII 码值 | 十六进制表示 | URL 编码表示 |
---|---|---|---|
\0 | 0 | 0x00 | %00 |
如果程序在处理文件名或文件路径这类涉及用户输入的环节,没有对输入进行严格过滤与验证,就极易引发%00截断攻击。攻击者可构造类似test13.php%00.jpg的文件名,在程序拼接文件保存路径时,%00会使后续内容被截断,最终以test13.php保存文件,进而绕过文件类型检查等安全机制,造成安全隐患。
2、截断条件
(1)PHP版本 < 5.3.4
-
PHP 5.3.4之前:默认会解析
%00
(空字节)为字符串终止符从而导致截断风险。 -
PHP 5.3.4及之后:修复了
%00
自动截断的问题。
(2)magic_quotes_gpc配置为Off
- magic_quotes_gpc能自动对来自GET、POST和COOKIE的数据转义,在单引号、双引号、反斜杠和空字符等前加反斜杠,旨在防止 SQL 注入,保障数据库操作安全。当它为Off,用户输入数据不再自动转义,攻击者输入的%00字符可直接传入程序。
- 若程序处理文件上传或字符串时未额外过滤%00,当magic_quotes_gpc为Off时%00不会被转义为\%00,这会导致系统容易被用于%00截断攻击,攻击者构造的evil.php%00.jpg文件名可绕过文件类型检查。
(3)代码逻辑存在缺陷
-
未过滤
%00
:代码直接使用用户输入拼接路径,未做空字节过滤。 -
未规范化路径:未使用
basename()
、realpath()
等函数处理文件名,导致%00
被保留。
三、源码分析
1、代码审计
对upload-labs 第13关的源码进行审计,相对于第3-11关卡的黑名单过滤,本关卡与12关一样,变为了白名单过滤,仅允许文件后缀名为“jpg,png和gif”三种后缀的文件上传,具体如下所示。
对比第12关卡,区别仅仅是save_path由GET参数变为了POST型参数,其中第12关的代码对比如下所示,两者的区别就是红框所示参数的传入方式变化。
经过详细注释的代码如下所示,相对于12关卡,只是参数的传递方式发生变化,由GET方法变为POST方法。
<?php
// 初始化上传状态和消息变量
$is_upload = false; // 用于标记文件是否上传成功,初始为false
$msg = null; // 用于存储上传过程中的提示信息,初始为null// 检查是否提交了上传表单
if(isset($_POST['submit'])){// 定义允许上传的文件扩展名白名单$ext_arr = array('jpg','png','gif');// 获取上传文件的扩展名// strrpos()查找文件名中最后一个"."的位置// substr()从"."后一位开始截取,得到文件扩展名$file_ext = substr($_FILES['upload_file']['name'],strrpos($_FILES['upload_file']['name'],".")+1);// 检查文件扩展名是否在白名单中if(in_array($file_ext,$ext_arr)){// 获取上传的临时文件路径$temp_file = $_FILES['upload_file']['tmp_name'];// 构建最终存储路径和文件名// 包含:用户指定的保存路径 + 随机数(10-99) + 当前日期时间 + 原扩展名$img_path = $_POST['save_path']."/".rand(10, 99).date("YmdHis").".".$file_ext;// 移动临时文件到最终位置if(move_uploaded_file($temp_file,$img_path)){$is_upload = true; // 上传成功,设置状态为true} else {$msg = "上传失败"; // 移动文件失败时的提示}} else {// 文件类型不在白名单中的提示$msg = "只允许上传.jpg|.png|.gif类型文件!";}
}
?>
分析代码可知存在的白名单%00截断POST型安全风险,具体原因如下所示。
(1)文件存储依赖字符串截断
上传的文件存储过程使用了move_uploaded_file()的C底层函数,它遇到%00(\0)会截断字符串。
(2)路径拼接可控
攻击者能控制文件保存路径或文件名的一部分(如通过$_POST的save_path参数传入)。 \0(在 URL 编码中表示为 %00)被视为字符串的结束符。
此代码在拼接文件保存路径时,使用了 $_POST['save_path'] 从 URL 的POST参数中获取保存路径,并且没有对这个参数进行严格的过滤和验证。
2、GET型和POST型区别
GET请求:
-
参数通过URL明文传输,如:
/upload.php?filename=evil.php%00.jpg
-
浏览器会自动对
%00
进行URL解码,变成空字节\x00
POST请求:
在使用POST方法传递%00字符时,他不会像GET型参数一样自动将%00进行URL界面,故而POST法进行%00 截断攻击时,需要手动进行URL 解码将其还原成原始的 %00 字符。如果使用burpsuite进行操作,那么需要使用Convertselection——>选择URL——>选择URL-decode才能达到攻击目的。
3、攻击思路
在文件上传功能中,如果程序只是简单地通过检查文件名的后缀来验证文件类型,而没有对整个文件名进行严格的安全检查,那么上传的路径可以GET型渗透方法一样进行截断绕过,将save_path由../upload/改为../upload/test13.php%00,但是这样做POST方法却没法像GET方法一样绕过服务器的文件上传过滤机制,这是因为GET方式的%00会自动被服务器进行URL解码成空字符,而POST方式需要先自己手动使用工具进行URL解码,然后再发送报文,具体步骤如下所示。
-
攻击者构造POST参数传入的save_path包含%00的特殊文件名(如"test13.php%00")
save_path:../upload/test13.php%00
-
由于POST报文发出的%00不会被自动解码,故而使用bp工具将%00手动选择并使用Convertselection——>选择URL——>选择URL-decode进行解码后发送。
-
前端验证看到的是".jpg"扩展名,file_ext为“jpg”,可以通过检查
-
$_FILES['upload_file']['name']:test13.jpg file_ext:jpg
-
由于$img_path = $_GET['save_path']."/".rand(10, 99).date("YmdHis").".".$file_ext;得知图片的路径为“test13.php%00”与“/”和“rand(10, 99).date("YmdHis").”和".jpg"的结合。假设rand(10, 99).date("YmdHis")的输出为9920250429,于是上传文件的路径变为如下所示。
-
save_path:../upload/test13.php%00 重命名后的图片:9920250429.jpg img_path:../upload/test13.php%00/9920250429.jpg
-
后端move_uploaded_file()处理时,遇到%00即停止解析,实际存储文件名变为"test13.php"。
-
img_path:../upload/test13.php
四、渗透实战
1、构建脚本test13.jpg
注意此时文件的后缀为jpg,这是因为本关卡为白名单,故脚本的后缀需要与上传允许的后缀“jpg,png和gif”中的任意一个保持一致。
<?php
phpinfo();
?>
2、配置php服务
需要将php版本号切换到5.3.4以下,具体如下所示。
(1)安装php5.3.4以下的php版本
如果当前phpstudy没有安装5.3.4以下的版本,需要安装对应版本的php,如下所示。
(2)切换php5.3.4以下的php版本
网站-管理-php版本-选择php5.3.4以下的php版本,具体如下所示。
(3)关闭magic_quotes_gpc配置
打开phpstudy保存目录——>选择Extensions目录——>php目录——>选择php5.2.17nts版本——>找到php.ini文件,方法如下所示。
将magic_quotes_gpc值修改为off,修改前如下所示。
将magic_quotes_gpc值修改为off,修改后如下所示。
(4)重启Apache服务
修改后需要重启小皮的Apache服务,具体如下所示。
3、打开靶场
打开靶场第13关,浏览选择该脚本,但不点击上传。
4、bp开启拦截
5、点击上传
6、bp拦截
bp捕获到上传报文,下图红框的部分即为需要修改的部分。
这时候注意,这个POST报文需要修改的是save_path的参数,如上图红框所示,具体如下所示。
../upload/
由于需要将savepath的"save_path=../upload/"后缀改为"save_path=../upload/test13.php%00",修改之前文件名为"test13.php",如下所示。
../upload/test13.php%00
7、POST参数增加test13.php%00
将save_path改为"../upload/test13.php%00",修改后效果如下所示。
8、手动URL解码
GET方式的%00会被服务器进行URL解码成null空字符,而POST方式需要先自己进行URL解码成null后再发送报文,此时选中%00内容字段,然后点击右键选择Convertselection——>选择URL——>选择URL-decode,具体效果如下所示。
解码后效果如下所示,已经变为不可见的空字符。
9、发包并获取脚本地址
将bp的inception设置为off,此时修改后的报文发送成功。
回到靶场的Pass13关卡,图片已经上传成功,在图片处右键复制图片地址。
右键图片获取图片地址,如下所示获取到图片URL。
http://127.0.0.1/upload-labs/upload/test13.php /9920250421160947.jpg
但由于%00截断,可将test13.php之后的内容截断,因此上传的脚本url地址如下所示。
http://127.0.0.1/upload-labs/upload/test13.php
10、访问脚本
如下所示访问test13.php脚本获取到服务器的php信息,证明文件上传成功。