From 2a136311d095ed53591419dcbd717878bda3495f Mon Sep 17 00:00:00 2001 From: Simon Date: Fri, 24 Nov 2023 18:19:13 +0100 Subject: [PATCH] crypto --- Unibw 2023/crypto/T800 - I'm Back/README.md | 17 +++ .../ca1aabdacd9e5b65baad49c9fd635168 | 107 ++++++++++++++++++ 2 files changed, 124 insertions(+) create mode 100644 Unibw 2023/crypto/T800 - I'm Back/README.md create mode 100644 Unibw 2023/crypto/T800 - I'm Back/ca1aabdacd9e5b65baad49c9fd635168 diff --git a/Unibw 2023/crypto/T800 - I'm Back/README.md b/Unibw 2023/crypto/T800 - I'm Back/README.md new file mode 100644 index 00000000..a23bca3d --- /dev/null +++ b/Unibw 2023/crypto/T800 - I'm Back/README.md @@ -0,0 +1,17 @@ +Crypto +T800 - I'm back + +100.00pt + +0 solves + +Last solution: Not yet solved +T800 - I'm back + +Our last ressort is Crystal Peak. It is secured by our latest and highly secure access control system. + +Visit https://t800.codectf.localos.io + +Hint: Works best on Firefox. + +Basic authentication is the same as for the scoreboard diff --git a/Unibw 2023/crypto/T800 - I'm Back/ca1aabdacd9e5b65baad49c9fd635168 b/Unibw 2023/crypto/T800 - I'm Back/ca1aabdacd9e5b65baad49c9fd635168 new file mode 100644 index 00000000..7bfb8cb1 --- /dev/null +++ b/Unibw 2023/crypto/T800 - I'm Back/ca1aabdacd9e5b65baad49c9fd635168 @@ -0,0 +1,107 @@ +#!/usr/bin/env python3 + +from flask import Flask, Response, request, send_from_directory +from ecdsa import SigningKey, VerifyingKey, BadSignatureError +from base64 import b64decode, b64encode +from hashlib import sha256 +from random import randrange +import os +import time + + +... + +if not FLAG: + import sys + sys.exit(1) + +app_dir = os.path.abspath(os.path.dirname(__file__)) + +admin_priv = SigningKey.from_pem(open('{}/keys/admin_priv.pem'.format(app_dir)).read(), hashfunc=sha256) +admin_pub = admin_priv.verifying_key +user_keys = dict() + +for u in ['john', 'kate', 'silberman']: + user_keys[u] = VerifyingKey.from_pem(open('{}/keys/{}_pub.pem'.format(app_dir, u)).read(), hashfunc=sha256) + +K_COEFF = [randrange(0, admin_pub.curve.order) for _ in range(5)] +chal_ts = None +chal_sig = None +chal_k = randrange(0, admin_pub.curve.order) + +app = Flask(__name__, static_folder='{}/static'.format(app_dir)) + +... + +def timestamp(): + t = int(time.time()) + + return t - t % 30 + +def sigdecode(sig, order): + bl = (order.bit_length() + 7) // 8 + sig = b64decode(sig.encode('utf-8')) + assert len(sig) == 2 * bl + r = int.from_bytes(sig[:bl], 'big') + s = int.from_bytes(sig[bl:], 'big') + + return r % order, s % order + +def sigencode(r, s, order): + bl = (order.bit_length() + 7) // 8 + r = (r % order).to_bytes(bl, 'big') + s = (s % order).to_bytes(bl, 'big') + + return b64encode(r + s).decode('utf-8') + +def sigcheck(pubkey, sig): + try: + if pubkey.verify(sig, str(chal_ts).encode('utf-8') + b'login', hashfunc=sha256, sigdecode=sigdecode): + return True + except BadSignatureError: + pass + + return False + +@app.route('/static/', methods=['GET']) +def serve_static(path): + return send_from_directory('static', path) + +@app.route('/', methods=['GET']) +def index(): + return app.send_static_file('index.html') + +@app.route('/login', methods=['POST']) +def login(): + data = request.get_json() + + if 'user' not in data or 'sig' not in data: + return Response("Invalid request format", status=400) + + user = data['user'] + + if user not in user_keys: + return { 'invalid': 'Invalid username' } + + sig = data['sig'] + + if sigcheck(user_keys[user], sig) or sigcheck(admin_pub, sig): + return { 'valid': '{}'.format(FLAG) } + + return { 'invalid': 'Invalid private key' } + +@app.route('/challenge', methods=['GET']) +def challenge(): + global chal_ts, chal_sig, chal_k + ts = timestamp() + + if ts != chal_ts: + chal_ts = ts + chal_k = sum(a * chal_k ** i for i, a in enumerate(K_COEFF)) % admin_pub.curve.order + chal_sig = admin_priv.sign(str(chal_ts).encode('utf-8') + b'challenge', hashfunc=sha256, sigencode=sigencode, k=chal_k) + + return { 'challenge': str(chal_ts), 'sig': chal_sig } + + +if __name__ == '__main__': +...