无参数rce

关于了解无参数rce之前,我们先看一下rce
rce可以让攻击者直接向后台服务器远程注入操作系统命令或者代码,从而控制后台系统。
但是究竟什么是无参rce,我们通过一段代码分析:
phpinfo();
<?php
if(';' === preg_replace('/[^\W]+\((?R)?\)/', '', $_GET['code'])) {    
    eval($_GET['code']);
} else {
    show_source(__FILE__);
}
?>
这段代码有if构造了一个正则匹配的判断
/[^\W]+\((?R)?\)/
关于这个正则匹配的各项分析如下面图所示

img

这段代码的核心就是只允许函数而不允许函数中的参数,就是说传进去的值是一个字符串接一个(),那么这个字符串就会被替换为空,如果替换后只剩下;,那么这段代码就会被eval执行。而且因为这个正则表达式是递归调用的,所以说像a(b(c()));第一次匹配后就还剩下a(b());,第二次匹配后就还剩a();,第三次匹配后就还剩;了,所以说这一串a(b(c())),就会被eval执行,但相反,像a(b('111'));这种存在参数的就不行,因为无论正则匹配多少次它的参数总是存在的。那假如遇到这种情况,我们就只能使用没有参数的php函数,

例如我们传进去一个phpinfo();经过一次匹配就剩下了;与if的强相等匹配成功,那么最终代码就会去执行phpinfo();
但是如果我们去执行system('ls /');经过一次匹配后最终剩下ls /;与if的强相等匹配不成功,所以这个最终就不能被执行。

类似上面的代码就构造出来了一个无参数rce
我们只能去使用没有参数的函数去执行。
下面是关于一些绕过函数的介绍:
getallheaders()
这个函数的作用是获取http所有的头部信息,也就是headers,然后我们可以用var_dump把它打印出来,但这个有个限制条件就是必须在apache的环境下可以使用,其它环境都是用不了的
var_dump(): 函数用于输出变量的相关信息。
   函数显示关于一个或多个表达式的结构信息,包括表达式的类型与值。数组将递归展开值,通过缩进显示其结构。
current()函数:输出数组中的当前元素的值:每个数组中都有一个内部的指针指向它的"当前"元素,初始指向插入到数组中的第一个元素。
end():函数将内部指针指向数组中的最后一个元素,并输出
next() - 将内部指针指向数组中的下一个元素,并输出。
prev() - 将内部指针指向数组中的上一个元素,并输出。
reset() - 将内部指针指向数组中的第一个元素,并输出。
each() - 返回当前元素的键名和键值,并将内部指针向前移动。
测试代码:
 <?php
highlight_file(__FILE__);
if(isset($_GET['code'])){
if(';' === preg_replace('/[^\W]+\((?R)?\)/', '', $_GET['code'])) {    
    eval($_GET['code']);}
else
    die('nonono');}
else
    echo('please input code');
?> 
在本地环境测试

image-20220814140802448

利用参数shell传入函数:?code=var_dump(getallheaders());

image-20220814140904745

我们可以看到在响应包中以数组形式输出了报文的信息,但是是倒序输出,所以我们可以控制最后一个属性键值,输入我们想要执行的数据

通过current函数把第一个数据提取出来,也就是我们想要去执行的数据

image-20220814141706646

图中可以看到我们已经把数据提取出来了,那么接下来使用eval函数去执行命令就像可以了。

image-20220814141903071

可以看到phpinfo();已经成功了。

因为这个函数可以返回报文信息,那么我们可以操作报文信息,添加或者修改,例如在报文信息底部添加a:phpinfo();那么在回显信息中也会出现phpinfo(),var_dump的作用是按数组输出,那么如果把var_dump修改为eval配合end函数是不是就可以去执行phpinfo()。

这个就是利用getallheaders()函数去执行无参数rce的办法

但是正如上面所说getallheaders()函数有一定的局限性,如果中间件不是apache的话这个函数就用不了,那么我们可以去使用一种更为普遍的方法get_defined_vars()函数。

get_defined_vars()函数
这个函数与getallheaders()函数相同的一点就是都是获取信息,但是getallheaders()获取的是headers报文信息,get_defined_vars()函数获取的是四个全局变量,$_GET $_POST $_FILES $_COOKIE,而且他返回的是一个二维数组
下面是实际环境测试:

image-20220812221753069

方法和第一种函数方法差不多,都是操作函数所获取的信息,添加变量或者我们想要执行的函数,类如添加一个参数:

image-20220812222052505

根据图可以看见我们添加的参数已经被函数获取并返回,但是这个函数返回的是一个二维数组,而利用get传入的参数在第一个数组中,所以这里我们就要将二维数组转为一维数组,这里我们可以利用current函数,这个函数的作用是返回数组中的当前单元,而它的默认是第一个单元,也就是我们GET方式传入的参数。

image-20220812222425838

这里可以看到这个函数将二维数组的第一个数据输出了,也就是把get的数据全部输出来了,相当于变成了一个一维数组,同样还是使用end函数截取最后的数据,也就是截取我们传入的参数

image-20220812222754655

接着函数换做eval函数去执行命令,就可以成功执行我们想要执行的命令。

image-20220812222843195

session_id()
获取cookie中phpseesion的字符串
这个的原理和上面差不多
我们把恶意代码写到cookie里面的phpsession中,通过seesion_id函数去读取他,获取到一个字符串,在通过eval函数去执行
但是session_id()要开启session才能用,所以说要先session_start(),开启session功能。
?shell=eval(hex2bin(session_id(session_start())));
大概原理就是:
session_start()开启session功能
session_id()获取cookie中的sessio值(这里添加我们想要执行的语句)
但是PHPSESSIID中只能有A-Z a-z 0-9,-所以我们首先要将想要传入的语句(如果是带有不允许输入的字符,没有的话不用)进行16进制编码
但是我们想要执行这个命令所以肯定不能以16进制进行执行,要进行解码,所以要使用hex2bin函数解码。
然后eval()函数去执行这个命令。

首先我们先看一下session获取session值,用var_dump()函数代替eval()去输出获取的信息。

image-20220813215342701

可以看到响应包中输出了我们在phpsesssid中写入的字符串,这里没有使用16进制编码,所以就没有使用hex2bin()函数,但是在执行我们这个我们输入的字符串时如果不使用16进制编码,是没有办法执行的(如果是没带有不允许存在的字符是可以不用编码直接执行的)

image-20220813215737549

可以看到返回包是没有任何返回信息的,所以说明我们这个命令就没有执行,但是换成16进制编码就可以执行成功

image-20220813215939440

可以看到这个phpinfo命令已经执行成功了,706870696e666f28293b是phpinfo()的16进制编码。

同样执行system(‘ls /’);

image-20220813220200255

执行system(‘cat /flag’);

image-20220813220244650

上面的各种方法最终都是为了实习rce,但是在一下情况下实在无法实现rce,我们就可以借用php函数直接完成对目录以及文件的操作
1、localeconv()函数
官方解释:localeconv() 函数返回一个包含本地数字及货币格式信息的数组。

img

scandir:列出目录中的文件和目录

img

### current(pos是current的别名)

作用就是输出数组中当前元素的值,只输出值而忽略掉键,默认是数组中的第一个值,如果要移动可以用下列方法进行移动:

img

### chdir()

这个函数是用来跳目录的,有时想读的文件不在当前目录下就用这个来切换,因为`scandir()`会将这个目录下的文件和目录都列出来,那么利用操作数组的函数将内部指针移到我们想要的目录上然后直接用`chdir`切就好了,如果要向上跳就要构造`chdir('..')

`img

array_reverse()
将整个数组倒过来,有的时候当我们想读的文件比较靠后时,就可以用这个函数把它倒过来,就可以少用几个next()

highlight_file()
打印输出或者返回 filename 文件中语法高亮版本的代码,相当于就是用来读取文件的
类似于 cat的作用

比较有意思的是localeconv()函数返回的第一个数据是 .

image-20220813223037739

那么我们可以使用current将这个.单独输出出来

image-20220813223140555

那么这个.这liunx可以代表当前目录,那么我们使用scandir配合就可以将获取到当前目录文件并输出相当于执行力ls

image-20220813223324166

下面进行一道例题的分析:

GXYCTF 2019禁止套娃

<?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']))
这个代码是经典无参数rce的例子
第一中方法就单纯使用php内置函数去输出flag

image-20220813224201774

可以看到倒数第二个是flag文件,所以我们要想办法去读取这个文件,这里可以使用array_reverse()函数将输出文件的顺序倒序输出,然后在使用next函数将指针位置移动到下一个文件位置也就是第二个flag文件文章,然后可以使用highlight_file()h函数将flag文件读取并输出

image-20220813224544609

image-20220813224611053

然后读取这个文件

image-20220813224702221

第二种解法,我们可以看到源码中并没有过滤session,所以我们可以使用sessiod_id获取sessiod信息,然后通过修改session去打开flag文件

?exp=highlight_file(session_id(session_start()));

image-20220813225801908

然后使用highlight_file()函数去打开这个文件

image-20220813225853369

原文地址:http://www.cnblogs.com/GTL-JU/p/16819989.html

1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长! 2. 分享目的仅供大家学习和交流,请务用于商业用途! 3. 如果你也有好源码或者教程,可以到用户中心发布,分享有积分奖励和额外收入! 4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解! 5. 如有链接无法下载、失效或广告,请联系管理员处理! 6. 本站资源售价只是赞助,收取费用仅维持本站的日常运营所需! 7. 如遇到加密压缩包,默认解压密码为"gltf",如遇到无法解压的请联系管理员! 8. 因为资源和程序源码均为可复制品,所以不支持任何理由的退款兑现,请斟酌后支付下载 声明:如果标题没有注明"已测试"或者"测试可用"等字样的资源源码均未经过站长测试.特别注意没有标注的源码不保证任何可用性