登录后更精彩...O(∩_∩)O...
您需要 登录 才可以下载或查看,没有账号?立即注册
×
BUUCTF靶场38 -- [GXYCTF2019]禁止套娃打开连接,发现啥也没有,只有一句话在那,看了源码、抓包数据也没发现啥,用dirsearch目录扫描一下,间隔设置为0.1线程设置为1,防止429,就是跑得比较久 

[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__);
?>
开始审计源码,我们最终的目标是执行@eval($_GET['exp']) 正则不太记得可以参考 PHP正则表达式,看这一篇就够了
函数不分大小写地过滤掉了 [PHP] 纯文本查看 复制代码 data://
filter://
php://
phar://
et
na
info
dec
bin
hex
oct
pi
log 对于第二个if,参考别人的wp - 如果';'===preg_replace(...),那么就执行exp传递的命令
- \ : 转义字符不多说了
- [a-z,_]+ : [a-z,_]匹配小写字母和下划线 +表示1到多个
- (?R)? : (?R)代表当前表达式,就是这个(/[a-z,_]+((?R)?)/),所以会一直递归,?表示递归当前表达式0次或1次(若是(?R)*则表示递归当前表达式0次或多次,例如它可以匹配a(b(c()d())))
简单说来就是:这串代码检查了我们通过GET方式传入的exp参数的值,如果传进去的值是传进去的值是字符串接一个(),那么字符串就会被替换为空。如果(递归)替换后的字符串只剩下;,那么我们传进去的 exp 就会被 eval 执行。比如我们传入一个 phpinfo();,它被替换后就只剩下;,那么根据判断条件就会执行phpinfo();。
(?R)?能匹配的只有a(); a(b()); a(b(c()));这种类型的。比如传入a(b(c()));,第一次匹配后,就剩a(b());,第二次匹配后,a();,第三次匹配后就只剩下;了,最后a(b(c()));就会被eval执行。
(?R)是引用当前表达式,(?R)? 这里多一个?表示可以有引用,也可以没有。,引用一次正则则变成了[a-z,_]+[a−z,]+\((?R)?[a−z,]+\((?R)?\),可以迭代下去,那么它所匹配的就是print(echo(1))、a(b(c()));类似这种可以括号和字符组成的,这其实是无参数RCE比较典型的例子
无参数RCE参考 无参数RCE总结_L1am0ur的博客-CSDN博客_无参数rce
因为不能传参,所以只能利用函数回显套娃来代替目标参数,这题有两种方法解决
法一利用函数找到并代替flag.php 先看一下flag文件的位置,构建payload [PHP] 纯文本查看 复制代码 ?exp=print_r(scandir(pos(localeconv()))); 看一下上面的函数 localeconv(),回显数组,第一个数组是字符"."点号 pos(),传入数组,回显数组的第一个值,pos可以用current代替 所以pos(localeconv())等价于.号 而函数scandir(.)意思是以数组的形式回显当前目录下的所有文件 再配合print_r函数输出数组 
flag在第四个数组里 ,我们只要想办法包涵这个文件即将flag.php调到第一个数组里或者是数组指针指向flag.php,这里有几个函数 PHP array_reverse() 函数 (w3school.com.cn)数组反转 PHP next() 函数 (w3school.com.cn) 数组指针移动下一位
所以可以先利用函数array_reverse将数组反转,flag就在第二位了,再利用next指向第二位数组,在用文件显示包涵即可输出flag.php文件,构造payload [PHP] 纯文本查看 复制代码 ?exp=show_source(next(array_reverse(scandir(current(localeconv())))));

拿到flag 通过分析源码发现preg_match()函数过滤传参中的一些关键字,通过解析可以推导出来,允许传入的参数值格式是:A(B(C(…))),这种很明显是函数模式,那么就好说了,我们首先思考一下我们需要做的是什么? 查看目录里面的所有文件,那么php中恰好就有一个函数scandir()浏览目录内的所有文件;--vardump(scandir(".")) 我们直到.是代表当前目录的意思,那么也就是说我们要满足scandir中接收的参数是".",而PHP中localeconv() 函数可返回一包含本地数字及货币格式信息的数组 --vardump(scandir(localecnov()))) 然后,我们就只需要让指针指向这个数组内的第一个值就好了

这时候,我们可以成功看到有一个flag.php文件 接下来,我们就需要想办法把该文件的内容读取出来: next() 函数将内部指针指向数组中的下一个元素,并输出。 array_reverse() 函数返回翻转顺序的数组。
exp=show_source(next(array_reverse(scandir(current(localeconv())))));
这段代码是什么意思呢?就还是利用scandir()函数查看当前目录的文件,然后利用next()函数和array_reverse()函数将文件中的内容读取出来,最后调用show_source()函数将内容展示到页面上 
法二利用session_id代替flag.php 方法就是先用cookie传参来设置session_id的值,session_id的值等于cookie中PHPSESSID的值,所以构建cookie头 Cookie: PHPSESSID=flag.php
在PHP中,session通常不会手动开启,需要利用php函数session_start来开启,所以可以构造payload
?exp=show_source(session_id(session_start()));
用burpsuite抓包写入cookie头 
添加完http头后点击放行
拿到flag
referer: 1. [GXYCTF2019]禁止套娃--详解:https://blog.csdn.net/l2872253606/article/details/125246229 2. Git信息泄露原理解析及利用总结:https://blog.csdn.net/wulanlin/article/details/122409259 3. 无参数RCE总结:https://blog.csdn.net/Manuffer/article/details/120738755
dirsearch -u http://4489bc7f-150b-4d29-ac8a-b4949b3d3950.node4.buuoj.cn:81 --delay=1 -x 429 -t 1
|