一级目录
0x00 前言
写这篇主要巩固一下正则,难度不是特别大
递归正则属于是盲区了
0x01 Sunwear
看看页面空空如也,抓包看看也是空空如也
果断扫一下
Githack一把梭
拿到index.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__);
?>
第一层ban掉了伪协议
主要是第二层的正则一开始没见过
参考掘金
https://juejin.cn/post/6972854176069992484
提取精华
递归就是引用自身,在正则里,就是引用整个表达式(?R)或指定子组(?n)
(?R)在翻译的时候,可以理解为一个”占位符“。它的位置决定了下一次递归时,捕获值的插入位置。
ok,我举几个直观的例子直观感受一下
表达式`(abc)(123|def)(?R)?(xyz)`可以匹配什么样的字符串?我们拆开看一下:
先进行一轮匹配,(abc)(123|def)可以匹配对应字符串abc123或abcdef,
(?R)?如果取0次,则直接跳到结尾xyz,那么abc123xyz或abcdefxyz肯定是可以匹配的,
并成功退出;
如果(?R)?取1的话,进行第一次递归,则这时表达式可以翻译成:
(abc)(123|def)(abc)(123|def)(?R)?(xyz)(xyz),
这时(?R)?可以再次取0,则表达式可以匹配abc123abcdefxyzxyz,abc123abc123xyzxyz,
abcdefabcdefxyzxyz, abcdefabc123xyzxyz,这4个字符串,并成功退出;
同理,我们还可以把(?R)?再取1,那么实际上这个表达式可以匹配
(abc(123|def)\*n+(xyz)\*n这种特征的字符串;
在回到本题里
preg_replace(’/[a-z,]+((?R)?)/’, NULL, $GET[‘exp’])
提出pattern: /[a-z,_]+\((?R)?\)/
实际上就是匹配 [a-z,]+()
然后表达式捕获值可以递归插入这个位置
/[a-z,_]+\(占位符\)/
[a-z,]+([a-z,_]+((?R)?)) 无限套娃下去
这就是无参数函数校验
可能说的比较抽象
在线实操几下
正常匹配完函数,后面的括号多余了
改变一下占位符的位置
可以看到只有里面的s_can()被匹配替换了
因为现在是在末尾,捕获值只能递归插入在尾部
再改一下,在尾部追加函数,成功被匹配替换
brain.md
利用函数scandir扫目录
需要能返回字符串’.'的函数
函数扩充
array_reverse()函数将数组倒序
next()函数指向下一个索引
localeconv()函数返回一包含本地数字及货币格式信息的数组,包含小数点字符,可作为当前路径使用
由于next函数无法嵌套
我们可以倒转数组然后使用一次next
poc
?exp=show_source(next(array_reverse(scandir(current(localeconv())))));
done
0x02 rethink
歇后语
ctfer学正则——够用就行?!