目录
pass-01
存在前端验证
onsubmit:当提交表单时执行一段 JavaScript。它只认true或者false.如果不返回值,则默认为true
写一个图片马,后缀是图片后缀,然后bp抓包改就行了
蚁剑连接
源码分析
前端:
checkile()函数
function checkFile() {
var file = document.getElementsByName('upload_file')[0].value;
if (file == null || file == "") {
alert("请选择要上传的文件!");
return false;
}
//定义允许上传的文件类型
var allow_ext = ".jpg|.png|.gif";
//提取上传文件的类型
var ext_name = file.substring(file.lastIndexOf("."));
//判断上传文件类型是否允许上传
if (allow_ext.indexOf(ext_name + "|") == -1) {
var errMsg = "该文件不允许上传,请上传" + allow_ext + "类型的文件,当前文件类型为:" + ext_name;
alert(errMsg);
return false;
}
}
首先通过getElementsByName获得表单元素,它获得的也是类数组,如果想准确得到某一个元素,可以使用数组下标的方式获取,那么这里document.getElementsByName('upload_file')[0].value;就获得了文件名。
后面if语句判断上传文件是否为空
substring() 方法用于提取字符串中介于两个指定下标之间的字符。
实例:
var str="Hello world!";
document.write(str.substring(3)+"<br>");
document.write(str.substring(3,7));
---------------------------------------------------------------------
lo world!
lo w
indexOf() 方法可返回某个指定的字符串值在字符串中首次出现的位置。 lastIndexOf() 方法获取后缀名 返回指定值在调用该方法的字符串中最后出现的位置,如果没找到则返回 -1。从该字符串的后面向前查找,从 fromIndex 处开始。 字符串中的字符被从左向右索引。首字符的索引(index)是 0,最后一个字符的索引是 stringName.length - 1。 例子:
String name = "upload.doc";
// 获取后缀名
String sname = name.substring(name.lastIndexOf("."));
//获得upload部分
String fileName=name.substring(0,name.lastIndexOf("."));
if (allow_ext.indexOf(ext_name + "|") == -1) ,在允许上传的后缀名里查找刚刚提取出的后缀名,如果找不到indexOf()函数会返回-1,这样就判断出了
pass-02(MIME请求头验证)
和第一关一个通关方式,查了下才知道这一关是MIME验证,就是会检查求情包, 修改上传的PHP的content-type为image/png 就好。
源码分析
<?php
setcookie("pass","02");
include '../config.php';
include '../head.php';
include '../menu.php';
$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
if (file_exists($UPLOAD_ADDR)) {
if (($_FILES['upload_file']['type'] == 'image/jpeg') || ($_FILES['upload_file']['type'] == 'image/png') || ($_FILES['upload_file']['type'] == 'image/gif')) {
if (move_uploaded_file($_FILES['upload_file']['tmp_name'], $UPLOAD_ADDR . '/' . $_FILES['upload_file']['name'])) {
$img_path = $UPLOAD_ADDR . $_FILES['upload_file']['name'];
$is_upload = true;
}
} else {
$msg = '文件类型不正确,请重新上传!';
}
} else {
$msg = $UPLOAD_ADDR.'文件夹不存在,请手工创建!';
}
}
?>
检查submit、upload文件夹是否存在
if (isset($_POST['submit'])) {
if (file_exists($UPLOAD_ADDR))
$FILES数组:
$_FILES['userfile']['name']
客户端机器文件的原名称。
$_FILES['userfile']['type']
文件的 MIME 类型,需要浏览器提供该信息的支持,例如“image/gif”。
$_FILES['userfile']['size']
已上传文件的大小,单位为字节。
$_FILES['userfile']['tmp_name']
文件被上传后在服务端储存的临时文件名。
$_FILES['userfile']['error']
和该文件上传相关的错误代码。['error'] 是在 PHP 4.2.0 版本中增加的。
判断文件的 MIME 类型,例如“image/gif”。||是或符号
if (($_FILES['upload_file']['type'] == 'image/jpeg') || ($_FILES['upload_file']['type'] == 'image/png') || ($_FILES['upload_file']['type'] == 'image/gif'))
进行文件上传, move_uploaded_file(上传的文件的文件名,移动文件到这个位置)
move_uploaded_file($_FILES['upload_file']['tmp_name'], $UPLOAD_ADDR . '/' . $_FILES['upload_file']['name']))
pass-03(后端黑名单验证)
这次好像是有对后缀的后端验证
我们尝试通过扩展名绕过,可以尝试phtml,php3,php4, php5, pht后缀名都可以绕过,但是前提是要在配置文件里面有这样的一句话
wamp配置文件路径:wamp64\bin\apache\apache2.4.51\conf\httpd.conf
phpstudy配置文件路径: PhpStudy2018\PHPTutorial\Apache\conf
AddType application/x-httpd-php .php .phtml .phps .php1 .php4 .pht
蚁剑连接
源码分析
这个源码就是先设置了一个黑名单数组 $deny_ext, 然后对传入的文件名进行处理(),最终目标是利用in_array()函数判断文件后缀是否在黑名单里,如果不在里面,就是我们熟悉的move_uploaded_file()函数进行文件上传的操作了,那么这里一旦黑名单过滤不全,就会导致可执行webshell传入后台。
<?php
$msg = null;
if (isset($_POST['submit'])) {
if (file_exists($UPLOAD_ADDR)) {
$deny_ext = array('.asp','.aspx','.php','.jsp'); //黑名单数组,可以发现过滤的并不全
$file_name = trim($_FILES['upload_file']['name']); //trim — 去除字符串首尾处的空白字符(或者其他字符)
$file_name = deldot($file_name);//删除文件名末尾的点
$file_ext = strrchr($file_name, '.');//strrchr — 查找指定字符在字符串中的最后一次出现指定字符的位置并返回该位置及之后的字符串部分
$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)) {
if (move_uploaded_file($_FILES['upload_file']['tmp_name'], $UPLOAD_ADDR. '/' . $_FILES['upload_file']['name'])) {
$img_path = $UPLOAD_ADDR .'/'. $_FILES['upload_file']['name'];
$is_upload = true;
}
} else {
$msg = '不允许上传.asp,.aspx,.php,.jsp后缀文件!';
}
} else {
$msg = $UPLOAD_ADDR . '文件夹不存在,请手工创建!';
}
}
?>
Pass-04( 黑名单验证.htaccess)
和pass03类似,只不过过滤更严格了,我们可以使用.htaccess
.htaccess的使用技巧总结http://t.csdn.cn/TRQGt
.htaccess内容:
<FilesMatch "png">
SetHandler application/x-httpd-php
</FilesMatch>
源码分析
和pass03一样的,只不过这过滤的确实丧心病狂
$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
if (file_exists($UPLOAD_ADDR)) {
$deny_ext = array(".php",".php5",".php4",".php3",".php2","php1",".html",".htm",".phtml",".pHp",".pHp5",".pHp4",".pHp3",".pHp2","pHp1",".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");
$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)) {
if (move_uploaded_file($_FILES['upload_file']['tmp_name'], $UPLOAD_ADDR . '/' . $_FILES['upload_file']['name'])) {
$img_path = $UPLOAD_ADDR . $_FILES['upload_file']['name'];
$is_upload = true;
}
} else {
$msg = '此文件不允许上传!';
}
} else {
$msg = $UPLOAD_ADDR . '文件夹不存在,请手工创建!';
}
}
pass-05(大小写绕过)
可以使用大小写后缀绕过,原因是这次源码少了strtolower()函数
.user.ini文件里的意思是:所有的php文件都自动包含666.jpg文件。.user.ini相当于一个用户自定义的php.ini
使用条件: (1)服务器脚本语言为PHP (2)对应目录下面有可执行的php文件 (3)服务器使用CGI/FastCGI模式 .user.ini.它比.htaccess用的更广,不管是nginx/apache/IIS,只要是以fastcgi运行的php都可以用这个方法
pass-06(空格绕过)
对比两关的源码后发现,第六关少了首尾去空的代码,我们上次文件尝试抓包在文件名后面加一个空格
要注意的是,空格的位置在php后面这个文件才能被当做php文件执行,系统好像会自动去掉末尾的空格,但是加在别的地方就不行
pass-07(点号绕过)
这一关没有deldot()函数,即不会去掉文件名末尾的点,这就导致虽然其他步骤都正常处理的情况下,只要后缀名后紧跟一个点,就能绕过黑名单验证,而和pass-06的系统自动去掉末尾的空格一样,它也是会自定去掉末尾的点的,所以就可以被执行了
pass-08(::$DATA绕过)
NTFS文件系统包括对备用数据流的支持。这不是众所周知的功能,主要包括提供与Macintosh文件系统中的文件的兼容性。备用数据流允许文件包含多个数据流。每个文件至少有一个数据流。在Windows中,此默认数据流称为:$ DATA。
没有对::D A T A 进 行 处 理 , 可 以 使 用 : : DATA绕过黑名单, 上传成功后保存的文件名其实是1.php 。
补充知识:php在window的时候如果文件名+"::$DATA"会把::$DATA之后的数据当成文件流处理,不会检测后缀名,且保持"::$DATA"之前的文件名 他的目的就是不检查后缀名。
测试了一下,确实没有对上传文件的文件名做出处理
pass-09(多点和空格绕过)
虽然会对.和空格进行过滤,但是只会过滤一次。 所以我们可以通过写多个点和空格进行绕过。
1.php. .
pass-10(双写文件名绕过)
对文件名进行处理的代码只有这一句:
$file_name = str_ireplace($deny_ext,"", $file_name);
$file_name = str_ireplace($deny_ext,"", $file_name);
意思就是在上传的文件的文件名中把黑名单的后缀名去掉,那这种删掉的操作就可以用双写来绕过。
pass-11(GET文件路径%00截断)
最终文件的存放位置是以拼接的方式,可以使用%00截断,但需要php版本<5.3.4
,并且magic_quotes_gpc
关闭。
知识补充:
strrpos(string,find[,start]) 函数查找字符串在另一字符串中最后一次出现的位置(区分大小写)。
substr(string,start[,length])函数返回字符串的一部分(从start开始 [,长度为length])
magic_quotes_gpc 着重偏向数据库方面,是为了防止sql注入,但magic_quotes_gpc开启还会对$_REQUEST, $_GET,$_POST,$_COOKIE 输入的内容进行过滤
上传1.php用BP抓包修改参数,把upload/后面加上1.php%00
(即图二),下面的filename=”zoe.php”
改为zoe.png
,这样一来,它读取的是我们自己加的1.php,以这个文件名进行存储,这样文件名和文件后缀我们就可控了。
放包之后用蚁剑连接
pass-12(post 00截断)
这一关白名单,文件上传路径拼接生成,而且使用了post发送的数据进行拼接,我们可以控制post数据进行0x00截断绕过白名单
补充知识:POST不会对里面的数据自动解码,需要在Hex中修改。
操作和pass-11差不多,区别就是这里的%00需要在hex页面手动写上00
pass-13(图片马unpack)
这一关会读取判断上传文件的前两个字节,判断上传文件类型,并且后端会根据判断得到的文件类型重命名上传文件 使用 图片马 + 文件包含
绕过
copy 233.png/b + 233.php pass.png 制作图片马,上传图片马
补充知识: 1.Png图片文件包括8字节:89 50 4E 47 0D 0A 1A 0A。即为 .PNG。 2.Jpg图片文件包括2字节:FF D8。 3.Gif图片文件包括6字节:47 49 46 38 39|37 61 。即为 GIF89(7)a。 4.Bmp图片文件包括2字节:42 4D。即为 BM。
但是上传的图片并不能当成php文件解析,据说还要利用文件包含漏洞,没看懂他们是怎么把include.php整进去的,貌似是直接在电脑上拖进去的,意思就是实际上这种方式需要有一个文件包含的入口才可以用
<?php
header("Content-Type:text/html;charset-utf-8");
$file=$_GET['file'];
if(isset($file)){
include $file;
}else{
show_source(__file__);
}
?>
pass-14(getimagesize图片马)
通过使用getimagesize()检查是否为图片文件, getimagesize() 函数将测定任何 GIF,JPG,PNG,SWF,SWC,PSD,TIFF,BMP,IFF,JP2,JPX,JB2,JPC,XBM 或 WBMP 图像文件的大小并返回图像的尺寸以及文件类型和一个可以用于普通 HTML 文件中 IMG
标记中的 height/width 文本字符串。 所以还是可以用第十四关
的图片马绕过,并使用文件包含漏洞解析图片马
这个样子就是图片马被解析了,蚁剑连接就好了
Pass-16(exif_imagetype图片马)
知识补充: exif_imagetype()读取一个图像的第一个字节并检查其后缀名。 返回值与getimage()函数返回的索引2相同,但是速度比getimage快得多。需要开启php_exif模块。
emmmmm
做法和pass-15一样的
pass-17(二次渲染绕过)
二次渲染:后端重写文件内容 basename(path[,suffix]) ,没指定suffix则返回后缀名,有则不返回指定的后缀名 strrchr(string,char)函数查找字符串在另一个字符串中最后一次出现的位置,并返回从该位置到字符串结尾的所有字符。 imagecreatefromgif():创建一块画布,并从 GIF 文件或 URL 地址载入一副图像 imagecreatefromjpeg():创建一块画布,并从 JPEG 文件或 URL 地址载入一副图像 imagecreatefrompng():创建一块画布,并从 PNG 文件或 URL 地址载入一副图像
防御
-
不要暴露上传文件的位置
-
禁用上传文件的执行权限
-
黑白名单
-
对上传的文件重命名,不易被猜测
-
对文件内容进行二次渲染
-
对上传的内容进行读取检查