最近在折腾一个FCK编辑器时,发现了一个有趣的问题:明明用的是已经修补过的版本,但一上手就遇到了障碍——创建文件夹时,系统竟然把“.”给过滤掉了,导致解析目录无法建立。
试了多次后发现,这个过滤并非实时生效,而是存在大约0.1秒的延迟。也就是说,输入内容后,系统会先让你看到,然后才悄悄把点去掉。既然看到了源码的存在,不如直接看看它是如何实现新建文件夹函数的。
先看一下前端这段JavaScript:
复制代码
代码如下:function addSort(obj) {
$(obj).parent().find('p').css('display','inline-block');
$(obj).css('display','none');
$(obj).parent().find('p > input:first').focus();
}
function newDir(obj) {
var pth = $(obj).prev().attr('value');
var basepth = $('#gtcurdir option:selected').text();
if (pth.replace(/^\s+|\s+$/g,").length == 0) {
alert("请输入字符");
$(obj).prev().focus();
return false;
} else {
$.ajax({
type : "GET",
dataType : "text",
url : "../mkdir.ajax.php?basedir="+basepth+"&newdir="+pth,
success : function(response) {
switch (response) {
case '0':
$(obj).prev().val("");
$(obj).parent().css('display','none');
$(obj).parent().parent().find('a').css('display','inline-block');
$('').appendTo('#gtcurdir');
break;
case '-1':
alert("文件夹已存在!");
$(obj).prev().focus();
break;
case '-2':
alert("新建文件夹失败!");
break;
}
},
error : function(response) {
alert("请求失败!");
return false;
}
});
}
}
看到这里,我注意到了一个非常有意思的细节:url : "../mkdir.ajax.php?basedir="+basepth+"&newdir="+pth,直接将前端输入的文件夹名拼接到URL中,然后发送到后端。换句话说,前端只负责发送,后端只要收到就能执行。那么,是否可以进行构造呢?
于是翻了一下后端脚本:
复制代码
代码如下:read()) !== false) {
if ($path == $newdir) {
$err = '-1';
break;
} else continue;
}
if ($err != '-1') {
if (!mkdir("../../{$basedir}{$newdir}", 0755)) $err = '-2';
}
echo $err;
?>
问题来了:这个后端脚本竟然没有验证用户是否已登录,也没有对newdir参数做任何过滤。前端的延迟过滤,纯粹是面子工程。后端这边,直接拿$_GET['basedir']和$_GET['newdir']拼接路径,然后调用mkdir()。
也就是说,只要知道后台路径,任何人都可以直接通过GET方式发送一个创建目录的请求。例如:
https://admin/fckeditor/mkdir.ajax.php?basedir=upload/image/&newdir=shell.asp
返回0,表示创建成功。虽然系统内部过滤了“.”,但后端脚本压根没管。前端延迟过滤一旦被绕过,就能直接创建asp解析目录。
修复方案:
最直接的做法是给mkdir.ajax.php加上身份验证,确认当前用户是合法管理员。同时,对newdir参数进行严格的过滤处理,例如只允许字母、数字和特定安全字符,禁止创建包含“.”的目录或特殊字符的目录。后端不能完全依赖前端的过滤,安全控制必须在服务器端实现。
