无参数RCE

正则的递归

正则表达式中有一种递归的用法,今天才知道。但是这种递归用法并不是所有语言都支持。它适用于PHP和java等语言。

以下列出几个简单的递归

(?R)?
(?0)?
\g<0>?
加号 , +
星号 *
问号 ? 一般来说问号也是递归的一种

一般用到最多就是?R。

括号里不能有参数,而有些题中就就会用这种递归的正则来过滤恶意函数,所以我们就用无参的函数像上图中套娃一样注入命令。

无参数RCE

一般情况下的命令执行都是由参数的,比如system函数,eval函数,passthru函数等等。但是用无参函数套娃也是常见的绕过姿势。(今天算是见识到了)

核心代码



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

在最近的春秋杯中web签到题就用到的这个代码来过滤有参函数。这段代码就是code传参的内容通过正则表达式匹配到的内容来替换为空,然后只能剩下;来满足强等。满足这种if条件后,传入的代码就会被eval函数来执行。这种正则就只能用无参函数来绕过限制。就比如a(b(c()))这种形式就满足,而a(b('c'))这种带参数的就不能执行成功。

正则的具体解析

无参函数

getallheaders()

这个函数的内容就是获取http所有的头部信息。接着我们可以用var_dump函数来把函数的执行结果都打印出来。这个函数有一个缺陷,它只能在apache中间件环境下使用。我们来在本地测试一下无参函数的具体效果。

测试代码:

<?php
highlight_file(__FILE__);
if(isset($_GET['shell'])){
if(';' === preg_replace('/[^\W]+\((?R)?\)/', '', $_GET['shell'])) {    
    eval($_GET['shell']);}
else
    die('hacker!!!!');}
else
    echo('hahaha');
?>

我们将var_dump(getallheaders());语句传入进去。

可以看到head头部信息以一维数组的形式 打印出来。当然,这么多内容我们并不是都需要,我们需要选择指定我们想要的一条来执行得出我们想要的结果。

end()

在php中end()函数是取出数组的最后一位。这个end函数是只会取出最后一位的键值,就是以字符串的形式输出出来,所以键名是可以随便起的。

我本地用var_dump输出getallheaders()函数的内容是倒着输的,(具体原因不清楚)所以使用end函数就截取了第一行的内容localhost。 其实原理是一样的。我们可以在http报文后加上我们要执行的恶意代码,再使用end函数截取最后一行,把var_dump函数换成eval函数就可以达到无参函数执行命令了。

session_id()

这种方法就是把恶意代码写在cookie的session_id里面。然后就用session_id()这个函数来读取它,然后它会返回出一个字符串,再用eval函数执行命令。但是,这种方法的前提是我们要开启session才能用session_id(),也就是说,要先session_start();用var_dump函数输出一下

session_id(session_start()),出来的是字符串,我们不需要做任何转换,但是session字符串只能有大小写字母和字符串。但不包含字符,所以我们在session这个地方注入命令就要做一些转换了,那么我们可以将字符串进行16进制编码后再用php中的hex2bin函数解码,执行恶意命令。

 可以进行eval函数执行注入在PHPSESSID中的命令。我们修改PHPSESSID的值,写入十六进制编码后的恶意代码。

 我本地环境搞不了,所以借用了师傅的图。大佬博客:RCE篇之无参数rce - 学安全的小白 - 博客园

php读取文件函数

直接用php函数来读取我们想要的文件。

localeconv

localeconv() 函数返回一包含本地数字及货币格式信息的数组。

乍一看这个函数跟文件读取也没关系啊,但就是这个占不到边的函数也能玩出新花样。它返回的是一个数组,数组的第一行是一个点(.) 这个点就可以代表当前目录了,我们可以用current()函数取出这个点。取出这个点之后,再用scandir函数进行目录遍历。

具体传参:var_dump(scandir(current(localeconv())));就可以遍历当前目录所有文件。

current()

它还有别名pos(),其实都是一样的。

输出数组当前元素的值,每一个数组中都有一个内部的指针,指向他当前的元素。初始指向当前数组中的第一个元素。这个函数不会移动指针,但是有next()函数和prev函数可以移动指针。

scandir()

列出当前目录的文件和目录,以数组的形式展现出来。

chdir()

chdir() 函数改变当前的目录。这个函数是是用来跳目录的,有时我们要读取的文件不在当前目录下,所以我们要改变当前目录。比如用chdir('..')来跳回上一级目录。再配合scandir函数遍历任意目录下的文件。

array_reverse()

根据单词意思就知道这个函数的功能就是将数组倒过来。

 highlight_file()

这个函数用来高亮代码,可以相当于文件读取。相当于show_source()。

getcwd()

这个函数返回当前工作目录。成功则返回当前工作目录,失败则返回FALSE。也需要用scandir函数遍历当前工作目录。

dirname()

这个函数返回路径中的目录名称部分。

get_defined_vars()

返回由所有已定义变量所组成的数组。

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

具体使用参考菜鸟教程

例题

这次用GXYCTF禁止套娃这个题目来切身感受一下。

这个涉及到了git泄露,用GitHack扫一下下载源码。

<?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__);
?>

可以看到源代码中出现了我们的核心代码

if(';' === preg_replace('/[a-z,_]+\((?R)?\)/', NULL, $_GET['exp'])) {

这里就只能用无参函数了。

首先我们需要一个打印函数print_r把内容输出出来,我们可以用localeconv()函数,它返回的是一个数组,那我们用current()函数来返回数组的第一个值,也就是. 表示当前目录。接着我们可以用

scandir()函数来遍历目录。

payload:

?exp=print_r(scandir(current(localeconv())));

就能找到在当前目录下有flag.php。

 读取文件时我们可以用show_source来高亮源代码。上图可知flag.php在第三行,因为数组指针默认是指向第一行,所以我们可以用next()函数和array_reverse()函数让数组指针指向flag.php,,最后高亮源代码。

payload:

?exp=show_source(next(array_reverse(scandir(current(localeconv())))));

最后得出flag.。

 当然,这并不是唯一的方法。我们还可以自己构造PHPSESSID来通过PHPSESSID来注入命令。

使用session_id(session_start())获取我们构造的恶意PHPSESSID。接着我们可以用print_r或

show_source函数来打印或者高亮源代码。具体操作不再演示。

结尾

php中还有很多像这样的无参函数,多做题,多积累。

参考

RCE篇之无参数rce - 学安全的小白 - 博客园

[GXYCTF2019]禁止套娃 1 &无参数RCE - op_hxc - 博客园

  • 7
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

XiLitter

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值