十一关(文件名后缀校验(双写绕过))
用了一个str_ireplace()函数
先了解一下
str_ireplace()是一个PHP函数,用于在字符串中查找并替换指定的子字符串。它与str_replace()函数类似,但不区分大小写。这意味着无论子字符串的大小写如何,它都会被替换为指定的新字符串。这个函数的语法是:str_ireplace(search, replace, subject)。其中search是要查找的子字符串,replace是要替换为的新字符串,subject是要在其中进行查找和替换的原始字符串。
还是和上一关思路相同
但是空格拼接不太管用了
可以利用双写黑名单字符
对字符串的一次过滤后拼接出 php,文件名.pphphp
bp抓包,p.php后缀改为.pphphp就行了
上传成功
这题还是要多看源码emmm。。。
我直接用了个大写绕过也没问题,可以绕过
十二关(白名单校验(GET 型 0x00 截断))
这关弄的不是黑名单而是白名单了
它首先检查是否有提交表单的动作。如果有提交动作,它会检查上传的文件扩展名是否在允许的范围内('jpg','png','gif')。如果是允许的文件类型,它会将临时文件移动到指定的路径,并将变量isupload设置为true。如果移动文件时出现错误,它会将isupload变量设置为true。如果移动文件时出现错误,它会将msg变量设置为'上传出错!'。如果文件类型不在允许的范围内,它会将$msg变量设置为"只允许上传.jpg|.png|.gif类型文件!"。
注:本关需要将phpStudy版本调为5.2.17,关闭magic_quotes_gpc选项(其他选项菜单->php扩展及设置->参数开关设置->把magic_quotes_gpc关闭。)。
代码漏洞点就在于 用$_GET['save_path']来组成上传的文件路径 而这个get传参是我们可以控制的地方
因此我们考虑用是否能进行截断 例如形成../upload/12.php/截断后面的(xxx.jpg)
这样就通过了白名单校验 并且保存成了php文件
这里我们使用的方法是0x00截断知识
url中的%00(只要是这种%xx)的形式,webserver会把它当作十六进制处理,
然后把16进制的hex自动翻译成ascii码值“NULL”,实现了截断burpsuite中16进制编辑器将空格20改成了00。
本质上来说,都是利用0x00是字符串的结束标识符,进行截断处理。
只不过GET传参需要url编码成%00而已
原理:php的一些函数的底层是C语言,而move_uploaded_file就是其中之一,遇到0x00会截断,0x表示16进制,URL中%00解码成16进制就是0x00。
注意 %00的使用是在路径上!
开始过关
bp抓包
将如上地方进行修改
自己构造一个路径,并且上传白名单的文件格式
成功修改名字和上传
十三关(POST 型 0x00 截断)
同第12关做法相同 只不过上传路径在$_POST数据中 不需要url编码
这里说一个小技巧 不需要修改hex值那么麻烦 只要在burp里面输入%00 然后进行url解码即可 得到就是0x00
成功上传
十四关(文件内容检测(文件头校验))
康康源码
这段代码中的函数getReailFileType()用于获取文件的真实类型,而不是依靠文件扩展名来判断文件类型。它通过读取文件的前两个字节来判断文件类型,然后返回相应的文件扩展名。
在主程序中,当用户提交表单时,会调用getReailFileType()函数来获取上传文件的真实类型。如果文件类型是未知的,将会返回错误消息"文件未知,上传失败!"。如果文件类型是已知的,将会将上传文件移动到指定的路径,并将isupload变量设置为true。如果移动文件时出现错误,则会将isupload变量设置为true。如果移动文件时出现错误,则会将msg变量设置为"上传出错!"。
通过读文件的前 2 个字节,检测上传文件二进制的头信息,判断文件类型
我们就可以利用图片马绕过检测。
本关phpStudy版本>5.3,在php.ini中开启allow_url_fopen选项(改版本前面有,以下是第二步配置的操作)
其他选项菜单->打开配置文件->phg.ini->用记事本打开->找到allow_url_fopen,将其=On
图片马制作
事先准备好一个php文件(里面还是写phpinfo,一句话木马也可以)和一张jpg图片
我这里是1.php和2.png,且都在桌面创建的
win+r输入cmd打开命令提示符窗口,cd Desktop进入桌面
再输入copy /b 2.png + 1.php 666.png
尝试上传
上传成功!
十五关(getimagesize()绕过)
本关用了getimagesize() 函数
什么是getimagesize() 函数
PHP中的getimagesize()函数是用于获取图像的尺寸和文件类型信息的函数。该函数可以读取图像文件并返回一个包含图像尺寸和文件类型信息的数组。这个数组包含了图像的宽度、高度、类型以及图像的MIME类型等信息。
该函数的基本语法如下:
getimagesize(string $filename, array &$image_info): array|false
其中,filename是要获取信息的图像文件的路径,filename是要获取信息的图像文件的路径,image_info是一个可选的数组参数,用于存储图像的详细信息。
函数返回一个包含图像尺寸和文件类型信息的数组,如果出现错误则返回false。
通过getimagesize()函数,我们可以方便地获取图像文件的尺寸和类型信息,从而进行相应的处理和展示。
剩下的解题就和14关一样了,利用图片马进行绕过
十六关(exif_imagetype()绕过)
这一关用=了exif_imagetype()函数
先了解一下
在PHP中,exif_imagetype()函数用于获取图像文件的类型。它可以确定指定文件的MIME类型,并返回相应的整数值来表示图像的类型。
该函数的基本语法如下:
exif_imagetype(string $filename): int|false
其中,$filename是要获取信息的图像文件的路径。
函数返回一个表示图像类型的整数值,如果出现错误则返回false。
通过exif_imagetype()函数,我们可以方便地确定图像文件的类型,以便进行相应的处理和展示。
本关也是phpStudy版本>5.3。
在php.ini中开启allow_url_fopen选项。
开启php_exif模块
其他选项菜单->打开配置文件->php-ini->找到php_exif并开启
接下来的解题就同14、15关一样了,即利用图片马进行绕过
十七关(文件内容检测(二次渲染))
先看源码
$is_upload = false;
$msg = null;
if (isset($_POST['submit'])){
// 获得上传文件的基本信息,文件名,类型,大小,临时文件路径
$filename = $_FILES['upload_file']['name'];
$filetype = $_FILES['upload_file']['type'];
$tmpname = $_FILES['upload_file']['tmp_name'];
$target_path=UPLOAD_PATH.'/'.basename($filename);
// 获得上传文件的扩展名
$fileext= substr(strrchr($filename,"."),1);
//判断文件后缀与类型,合法才进行上传操作
if(($fileext == "jpg") && ($filetype=="image/jpeg")){
if(move_uploaded_file($tmpname,$target_path)){
//使用上传的图片生成新的图片
$im = imagecreatefromjpeg($target_path);
if($im == false){
$msg = "该文件不是jpg格式的图片!";
@unlink($target_path);
}else{
//给新图片指定文件名
srand(time());
$newfilename = strval(rand()).".jpg";
//显示二次渲染后的图片(使用用户上传图片生成的新图片)
$img_path = UPLOAD_PATH.'/'.$newfilename;
imagejpeg($im,$img_path);
@unlink($target_path);
$is_upload = true;
}
} else {
$msg = "上传出错!";
}
}else if(($fileext == "png") && ($filetype=="image/png")){
if(move_uploaded_file($tmpname,$target_path)){
//使用上传的图片生成新的图片
$im = imagecreatefrompng($target_path);
if($im == false){
$msg = "该文件不是png格式的图片!";
@unlink($target_path);
}else{
//给新图片指定文件名
srand(time());
$newfilename = strval(rand()).".png";
//显示二次渲染后的图片(使用用户上传图片生成的新图片)
$img_path = UPLOAD_PATH.'/'.$newfilename;
imagepng($im,$img_path);
@unlink($target_path);
$is_upload = true;
}
} else {
$msg = "上传出错!";
}
}else if(($fileext == "gif") && ($filetype=="image/gif")){
if(move_uploaded_file($tmpname,$target_path)){
//使用上传的图片生成新的图片
$im = imagecreatefromgif($target_path);
if($im == false){
$msg = "该文件不是gif格式的图片!";
@unlink($target_path);
}else{
//给新图片指定文件名
srand(time());
$newfilename = strval(rand()).".gif";
//显示二次渲染后的图片(使用用户上传图片生成的新图片)
$img_path = UPLOAD_PATH.'/'.$newfilename;
imagegif($im,$img_path);
@unlink($target_path);
$is_upload = true;
}
} else {
$msg = "上传出错!";
}
}else{
$msg = "只允许上传后缀为.jpg|.png|.gif的图片文件!";
}
}
这段代码实现了一个图片上传功能,并对上传的图片进行二次渲染,生成新的图片。让我们逐步解释代码的功能:
-
当用户提交表单时,检查是否有文件上传,并获取上传文件的基本信息,包括文件名、类型、大小和临时文件路径。
-
构建目标路径($target_path)用于存储上传的文件。
-
获取上传文件的扩展名,并根据文件扩展名和类型判断是否允许上传。只有当文件扩展名和类型符合要求时,才会进行上传操作。
-
如果文件是 jpg 格式且类型为 image/jpeg,将上传文件移动到目标路径,并使用 imagecreatefromjpeg() 函数创建一个新的 JPEG 图像资源。如果创建失败,将提示用户该文件不是 jpg 格式的图片,并删除上传的文件。如果创建成功,将生成一个新的随机文件名,将新图片保存到指定路径,删除原始上传的文件,并将 $is_upload 设置为 true。
-
如果文件是 png 或 gif 格式,也按照上述步骤进行相应的处理。
-
如果上传的文件不符合要求,将提示用户只允许上传后缀为 .jpg、.png 或 .gif 的图片文件。
绕过方法:GIF图片绕过
(1)正常上传图片木马。(使用copy命令,之间的题目有)
(2)下载被渲染后的图片
(3)使用010editor进行比对。左边为16进制,找到二次渲染后不变的地方,在这个地方插入一句话木马,然后再上传!
(这玩意难搞啊)
010editor是什么
010 Editor是一款专业的文本和十六进制编辑器,旨在编辑计算机上的任何文件、驱动器或进程。它提供了广泛的功能,用于编辑和分析二进制文件、文本文件和硬盘驱动器。它具有十六进制编辑、文本编辑、脚本编写、数据分析、磁盘编辑等功能,并支持Windows、macOS和Linux操作系统。010 Editor对于开发人员、逆向工程师、取证分析人员以及需要在低级别处理二进制和文本数据的用户来说是一个强大的工具。
十八关(逻辑漏洞(条件竞争))
康康源码
给定的代码是一个处理文件上传的PHP脚本。它检查是否正在上传文件,然后检查文件扩展名,以确保它是.jpg、.png或.gif文件。如果文件通过验证,它会被移动到指定的上传路径,并被赋予一个新的名字,包括一个随机数和日期时间戳。如果文件未通过验证,将显示错误消息,并删除临时文件。
我们可以使用条件竞争方法(即在文件被删除之前访问该文件)绕过。此方法需要不停且迅速地上传、访问文件。
首先我们需要一个PHP文件
然后在题目中上传
接下来就是bp抓包
send to Intruder,并清除$
接下来就是设置Payload type为Null payloads,Payload Options设置为Continue indefinitely
线程调大,比如我们设置为20
随后点击Start attack,不断上传该文件
然后我们转到后台,不断访问该文件1.php
手速一定要快,直到访问成功为止
十九关(逻辑漏洞(条件竞争-图片马))
本关必须在php.ini中开启allow_url_include选项!
康康源码1
$is_upload = false;
$msg = null;
if (isset($_POST['submit']))
{
require_once("./myupload.php");
$imgFileName =time();
$u = new MyUpload($_FILES['upload_file']['name'], $_FILES['upload_file']['tmp_name'], $_FILES['upload_file']['size'],$imgFileName);
$status_code = $u->upload(UPLOAD_PATH);
switch ($status_code) {
case 1:
$is_upload = true;
$img_path = $u->cls_upload_dir . $u->cls_file_rename_to;
break;
case 2:
$msg = '文件已经被上传,但没有重命名。';
break;
case -1:
$msg = '这个文件不能上传到服务器的临时文件存储目录。';
break;
case -2:
$msg = '上传失败,上传目录不可写。';
break;
case -3:
$msg = '上传失败,无法上传该类型文件。';
break;
case -4:
$msg = '上传失败,上传的文件过大。';
break;
case -5:
$msg = '上传失败,服务器已经存在相同名称文件。';
break;
case -6:
$msg = '文件无法上传,文件不能复制到目标目录。';
break;
default:
$msg = '未知错误!';
break;
}
}
- 初始化了两个变量:
$is_upload
和$msg
,分别设置为 false 和 null。 - 使用
isset($_POST['submit'])
检查表单是否已提交。 - 如果表单已提交,包含
myupload.php
文件,并使用文件名、临时文件名、文件大小和基于时间戳的文件名创建了MyUpload
类的实例。 - 然后调用
MyUpload
类的upload
方法,检查返回的状态码。 - 根据状态码,将
$is_upload
变量设置为 true,将错误消息设置到$msg
变量,或将上传的文件路径设置到$img_path
变量中
//myupload.php
class MyUpload{
......
......
......
var $cls_arr_ext_accepted = array(
".doc", ".xls", ".txt", ".pdf", ".gif", ".jpg", ".zip", ".rar", ".7z",".ppt",
".html", ".xml", ".tiff", ".jpeg", ".png" );
......
......
......
/** upload()
**
** Method to upload the file.
** This is the only method to call outside the class.
** @para String name of directory we upload to
** @returns void
**/
function upload( $dir ){
$ret = $this->isUploadedFile();
if( $ret != 1 ){
return $this->resultUpload( $ret );
}
$ret = $this->setDir( $dir );
if( $ret != 1 ){
return $this->resultUpload( $ret );
}
$ret = $this->checkExtension();
if( $ret != 1 ){
return $this->resultUpload( $ret );
}
$ret = $this->checkSize();
if( $ret != 1 ){
return $this->resultUpload( $ret );
}
// if flag to check if the file exists is set to 1
if( $this->cls_file_exists == 1 ){
$ret = $this->checkFileExists();
if( $ret != 1 ){
return $this->resultUpload( $ret );
}
}
// if we are here, we are ready to move the file to destination
$ret = $this->move();
if( $ret != 1 ){
return $this->resultUpload( $ret );
}
// check if we need to rename the file
if( $this->cls_rename_file == 1 ){
$ret = $this->renameFile();
if( $ret != 1 ){
return $this->resultUpload( $ret );
}
}
// if we are here, everything worked as planned :)
return $this->resultUpload( "SUCCESS" );
}
......
......
......
};
这是一个名为 MyUpload
的自定义类,用于处理文件上传操作。该类包含了一些属性和方法,其中包括文件类型限制、文件上传操作等。
在 MyUpload
类中,有一个成员变量 $cls_arr_ext_accepted
,它包含了允许上传的文件类型数组。
类中的 upload
方法是用于执行文件上传的主要方法。该方法接受一个参数 $dir
,表示上传文件的目标目录。在方法内部,依次执行了文件是否上传、目录设置、文件类型检查、文件大小检查、文件是否存在检查、移动文件、文件重命名等步骤,并根据每个步骤的返回值进行处理。最终,根据上传的结果返回相应的状态信息。
总的来说,该类封装了文件上传的各个步骤,并提供了相应的错误处理和状态返回。
我们可以使用图片木马配合文件包含漏洞进行绕过。(虽然进行了移动和重命名,但是网页会回显地址)就是把18关的1.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","jsp","jspa","jspx","jsw","jsv","jspf","jtml","asp","aspx","asa","asax","ascx","ashx","asmx","cer","swf","htaccess");
$file_name = $_POST['save_name'];
$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)) {
$is_upload = true;
}else{
$msg = '上传出错!';
}
}else{
$msg = '禁止保存为该类型文件!';
}
} else {
$msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
}
}
- 如果用户提交了表单(通过检查
$_POST['submit']
是否存在),则执行文件上传操作。 - 首先检查上传目录是否存在,如果不存在则将错误消息设置为“UPLOAD_PATH 文件夹不存在,请手工创建!”。
- 如果上传目录存在,继续检查文件的扩展名是否在禁止的扩展名列表中,如果是则将错误消息设置为“禁止保存为该类型文件!”。
- 如果文件的扩展名不在禁止列表中,则尝试将临时文件移动到指定的上传目录中。
- 如果移动操作成功,则将
$is_upload
变量设置为 true,表示上传成功;否则将错误消息设置为“上传出错!”
move_uploaded_file函数简介
move_uploaded_file
函数是 PHP 中用于将上传的临时文件移动到新位置的函数。它的语法如下:
bool move_uploaded_file ( string $filename , string $destination )
$filename
参数是上传文件的临时路径。$destination
参数是要将文件移动到的目标路径。
这个函数会检查上传的文件是否是通过 HTTP POST 上传的,如果是则会将文件移动到指定的目标路径,并返回 true。如果移动成功,原始的临时文件将被删除。如果移动失败,则返回 false。
这个函数通常用于在文件上传后将临时文件保存到服务器的永久位置。在移动文件之前,通常会对文件进行一些验证,比如文件类型、文件大小等。
因为文件名可控,这题利用move_uploaded_file的一个特性,会自动忽略后面的/.
我们上传1.php,将文件保存改为upload-19.php/.
发现可以上传成功
二十一关(逻辑漏洞(数组绕过))
$is_upload = false;
$msg = null;
if(!empty($_FILES['upload_file'])){
//检查MIME
$allow_type = array('image/jpeg','image/png','image/gif');
if(!in_array($_FILES['upload_file']['type'],$allow_type)){
$msg = "禁止上传该类型文件!";
}else{
//检查文件名
$file = empty($_POST['save_name']) ? $_FILES['upload_file']['name'] : $_POST['save_name'];
if (!is_array($file)) {
$file = explode('.', strtolower($file));
}
$ext = end($file);
$allow_suffix = array('jpg','png','gif');
if (!in_array($ext, $allow_suffix)) {
$msg = "禁止上传该后缀文件!";
}else{
$file_name = reset($file) . '.' . $file[count($file) - 1];
$temp_file = $_FILES['upload_file']['tmp_name'];
$img_path = UPLOAD_PATH . '/' .$file_name;
if (move_uploaded_file($temp_file, $img_path)) {
$msg = "文件上传成功!";
$is_upload = true;
} else {
$msg = "文件上传失败!";
}
}
}
}else{
$msg = "请选择要上传的文件!";
}
-
首先,检查是否有上传的文件。如果有上传的文件,则继续执行文件类型和文件名的检查;如果没有上传的文件,则将错误消息设置为“请选择要上传的文件!”。
-
接下来,检查上传的文件类型是否在允许的 MIME 类型列表中(这里只允许上传 JPEG、PNG 和 GIF 图像文件),如果不在列表中,则将错误消息设置为“禁止上传该类型文件!”。
-
然后,检查文件名是否符合要求。如果用户没有指定保存的文件名(通过
$_POST['save_name']
),则使用上传文件的原始文件名;如果用户指定了保存的文件名,则使用用户指定的文件名。然后检查文件的后缀名是否在允许的后缀名列表中(这里只允许 jpg、png 和 gif 后缀的文件),如果不在列表中,则将错误消息设置为“禁止上传该后缀文件!”。 -
如果文件类型和文件名都符合要求,则将临时文件移动到指定的上传目录中。如果移动成功,则将
$is_upload
变量设置为 true,表示上传成功,并设置成功的消息;否则将错误消息设置为“文件上传失败!”。
总之,这段代码实现了对上传文件类型和文件名的检查,并在上传成功或失败时给出相应的消息。
补充知识:
explode(separator,string[,limit]) 函数,把字符串打散成数组。
end(array)函数,输出数组中的当前元素和最后一个元素的值。
reset(array)函数,把数组的内部指针指向第一个元素,并返回这个元素的值。
count(array)函数,计算数组中的单元数目,或对象中的属性个数。
绕过方法:
1、上传1.php并抓包,将1.php修改为1.jpg(验证文件类型)。修改content_type为image/jpeg(构造为白名单中的值)
2、修改post的参数为数组类型,save_name[0]为"upload-20.php",save_name[1]不设置, save_name[2]为png或jpg或gif(因为前面改为了1.jpg,这里以jpg为例)
因为上面的代码会先判断最后一个数组元素是否在白名单中,我们索引[2]为png或jpg或gif
正好可以绕过,而后面会将数组第一位和$file[count($file) - 1]进行拼接
因为我们的save_name[1]为空,即$file[1]为空,所以拼接后文件名就会变成upload-20.php
然后send,上传成功
确实有说明成功上传
可算是打完了