消灭php webshell与一句话木马
的有关信息介绍如下:总体来讲,对php webshell和一句话木马的查杀,主要从三个方面进行
1.Shell特征
2.PHP安全方面的函数和变量以及安全配置选项
3.语法检测
首先,基于Shell特征,此方法主要针对base64_decode编码与gzinflate等参考特征库与匹配算法:Web Shell Detector v1.51,当前共有296个特征https://github.com/emposha/PHP-Shell-Detector/zipball/master可同时参考其他开源软件特征库
其次,对涉及PHP安全方面的函数和安全配置选项进行了归类,参照了很多文章,博客以及开源工具,在此表示感谢。
1.include/require/require_once/include_once/file_get_contents//文件包含2.exec/system/popen/passthru/proc_open/pcntl_exec/shell_exec/curl_exec/curl_multi_exec/``/escapeshellcmd/pcntl_exec//系统命令执行3.eval/preg_replace/assert/call_user_func/call_user_func_array/create_function/ob_start/array_map//代码执行4._GET/_POST/_COOKIE/_SERVER/_REQUEST/_ENV/GLOBALS/php://input/getenv/ //数据传递5.session/cookie6.extract/parse_str/mb_parse_str/import_request_variables/unserialize7.copy/rmdir/chmod/delete/fwrite/fopen/readfile/fpassthru/move_uploaded_file/file_put_contents/unlink/upload/opendir/fgetc/fgets/ftruncate/fputs/fputcs//文件操作8.select/insert/update/delete/order by/groupby/limit/in(/stripslashes/urldecode//数据库操作9.confirm_phpdoc_compiled/mssql_pconnect/mssql_connect/crack_opendict/snmpget/ibase_connect10.echo/print/printf/vprintf/document.write/document.innerHTML/document.innerHtmlText11.phpinfo/highlight_file/show_source/parse_ini_file//敏感信息,源代码泄露12.iconv/mb_convert_encoding13.base64_decode,gzinflate,gzuncompress,gzdecode,str_rot13//代码加密
其他:
usort(), uasort(), uksort() array_filter() array_reduce() array_diff_uassoc(), array_diff_ukey() array_udiff(), array_udiff_assoc(), array_udiff_uassoc() array_intersect_assoc(), array_intersect_uassoc() array_uintersect(), array_uintersect_assoc(), array_uintersect_uassoc() array_walk(), array_walk_recursive() xml_set_character_data_handler() xml_set_default_handler() xml_set_element_handler() xml_set_end_namespace_decl_handler() xml_set_external_entity_ref_handler() xml_set_notation_decl_handler() xml_set_processing_instruction_handler() xml_set_start_namespace_decl_handler() xml_set_unparsed_entity_decl_handler() stream_filter_register() set_error_handler() register_shutdown_function() register_tick_function()
附带php.ini中涉及安全配置选项。
safe_mode = off ( a lot of shit cannot be done with this on ) disabled_functions = N/A ( no one,we want all ) register_globals = on ( we can set variables by request ) allow_url_include = on ( for lfi/rfi ) allow_url_fopen = on ( for lfi/rfi ) magic_quotes_gpc = off ( this will escape ' " \ and NUL's with a backslash and we don't want that )short_tag_open = on ( some scripts are using short tags,better on ) file_uploads = on ( we want to upload ) display_errors = on ( we want to see the script errors,maybe some undeclared variables? ) open_basedir 限制访问目录 display_errors = off 显示错误信息 auto_prepend_file = on 在每个页面之前被载入 auto_append_file = on 在每个页面之后被载入
利用.htaccess:
SetHandler
//可将php代码存于非php后缀文件,例: x.jpg,将以下代码写入.htaccess中,连接x.jpg即可启动后门木马SetHandler application/x-httpd-php
利用auto_prepend_file
//可将php代码存于非php后缀文件,例: 123.gif,将以下代码写入.htaccess中,文件路径必须是绝对路径,访问网站上任何php文件都会启动该php后门木马,可在不更改站点源代码的情况下记录所有$_REQUEST的值,也可批量挂马
php_value auto_prepend_file c:/apache2/htdocs/123.gif
利用auto_append_file
//类似auto_prepend_file,可将php代码存于非php后缀文件,例: 123.gif,将以下代码写入.htaccess中,文件路径必须是绝对路径,访问网站上任何php文件都会启动该php后门木马
php_value auto_append_file c:/apache2/htdocs/123.gif
再次,既然已经知道了webshell的特征以及常用的执行和利用方法,是不是就可以做特征通杀了呢?实则不然,由于php语言本身的灵活性和扩展性,导致大量的躲避ids/ips/av等检测的变形出现。
下面是一些常见的变形方式:
我们从最简单的一句话木马开始神奇的变换之旅。
1. //将POST变形 2. //将${变形 3. //语法变形4. //隐藏assert5. //隐藏assert6. b=assert&a=phpinfo() //替换掉assert,日志完全无法记录
以上为较为常见的php一句话变形方法,同时以上方法可以相互叠加,增加检测难度。下面是一些更加高级和BT的躲避变形方法,
1.
=($_=@$_GET).@$_($_GET)?>
//利用方法:/test.php?1=phpinfo()&2=assert
2.Spanner的Non alphanumeric webshell
$_="";$_[+""]='';$_="$_"."";$_=($_[+""]|"").($_[+""]|"").($_[+""]^"");?>=${'_'.$_}['_'](${'_'.$_}['__']);?>//利用方法:_=shell_exec&__=whoami
3. //利用花括号的特性
4.配合之前提到的各类php函数如create_function,preg_replace等等。
从以上内容可以看出,如果把当中的assert看作数据执行部分,把$_POST[‘a’]看作数据传递部分,虽然在传递数据的时候我们无法控制,但是我们可以比较方便的找到数据执行的位置"(",所以我们可以很容易的做出我们的webshell检测方法:使用token_get_all将php代码分解成token,然后找到每一个”(”,判断括号前面的数据是不是合法的即可。
至于如何判断合法,我们遵循一个原则:如果是空格、注释之类,则采取忽略方式(即continue,继续往前判断);如果是分支、条件判断或运算符,则我们认为是合法的;如果是字符串,并且在黑名单,我们认为是非法的,否则是合法的;如果不满足上述条件,我们先暂认为是非法的,之后不断完善此项。Demo在最后。
挑战:
1.两句话webshell
所谓的两句话webshell并不是简单的将shell分成两句,如$a = $_GET[‘a’];eval($a);这样做没有任何意义,真正有意义的是隐藏数据执行和数据传递的代码,将这些代码分散在多个php应用程序的源码中。这里简单举一个例子,譬如我们在a.php中插入了这样的代码,以便在必要时生成一个shell文件:
file_put_contents(“/home/www/abc.txt”, str_rot13 (‘some code already encode’));
然后我们在b.php中再实现一个数据执行者,最简单的莫过于:
include “/home/www/abc.txt”;
那么这种两步的webshell是很难被发现的,类似的譬如上传一个文件,然后通过$_FILES['userfile']['tmp_name']获取文件位置;或者使用ftp_get获取一个文件;或者构造一个可以直接backupshell的注入点等等方式来完成我们这步。我们也可以使用curl、file、imagecreatefrompng、get_headers、bzopen、svn_checkout这样的函数来实现我们的数据提供。而这类的函数有十几个到几十个之多,只要可以进行网络信息的读取或者可以进行本地文件的读取或写入,那么我们就可以使之成为我们的数据提供者,而shell检测脚本绝不会把自己的触手伸这么远。而且通过sqlite、mysql或其它数据库隐藏我们的数据也不失为一个好方法。
而对于数据执行者,即便将所有的include都grep出来,想把这个webshell的数据执行者找到也不是那么容易的事。尤其我们假设一种情况:这个include是原来正常的代码逻辑,而变化的仅仅是之前的数据传递者(即被include的页面)。另外有些MVC框架有自动加载helper和model的功能,甚至有的有自动加载viewer的功能,这些也都是我们藏身的好地方。此外我们还可以使用一般不在黑名单中的函数来绕过shell脚本检测,或者直接在应用代码中找上述的函数,看其所引用的变量是否可以稍加改变变成我们的数据提供者。
所以我们看出“两句话”与一句话webshell的最大的区别在于不构造新的数据执行者或者可以完全隐匿数据执行者(利用已有的代码逻辑等),仅通过变换或构造数据提供者的前提下完成shell的功能。而$_GET、$_POST、$_SERVER、$_COOKIE、$_FILE、$_REQUEST、$GLOBALS[“_GET”]、$/*hello*/{"_G"."ET"}都是我们的数据传送者;file_get_contents、file、file_put_contents甚至print_r、unserialize都可以隐匿我们要传输的数据。当然扫描代码隐患的工具(譬如Rips)肯定能够扫描到这些隐患,但是这类工具毕竟不是用于扫描webshell的,且误报率是相当的高。
2.逻辑后门
当然如上的利用方式中还是少不了特殊的函数的存在的。因此除此之外,我们可以尝试留下另一种后门:逻辑后门,譬如,文件源码如下:
$value) { //由攻击者添加 $$key = $value;}// ... some code if (logged_in() || $authenticated) { //原有逻辑 // ... administration area }?>
或者增加逻辑
if($user_level==ADMIN || $user_name==’admin’) {//admin area}
或者增加配置
$allow_upload = array( ‘jpg’,’gif’,’png’, ‘php’,);
那么可以想象检测难度之大,几乎是无法用扫描特征的方式解决的。对此可能我们只能采取其他方法,譬如线上文件监控以及线上与SVN代码的周期性diff等方式来解决了。