cscg24-smartlight

CSCG 2024 Challenge 'Smart Light Bulb'
git clone https://git.sinitax.com/sinitax/cscg24-smartlight
Log | Files | Refs | sfeed.txt

source.repr (12485B)


      1b'DECRYPT\x00\x00\x00\x00\x00\x00\x00#!/usr/bin/env python3\nfrom Crypto.Cipher import AES\nfrom challenge_secrets import AES_KEY, APP_KEY\nfrom crcmod.predefined import mkPredefinedCrcFun\nfrom flask import Flask, Response, flash, g, redirect, render_template_string, request, session\nimport io\nimport logging\nimport logging.handlers\nimport os\nimport signal\nimport struct\nimport sys\n\nVERSION_NUM = 150\nVERSION_STR = "1.5.0"\nUPDATE_FILENAME = "bulb-update.py"\nLOG_FILENAME = "bulb.log"\n\nTEMPLATE = """\n<!DOCTYPE html>\n<head>\n<title>Bulb</title>\n</head>\n<body>\n    <div style="text-align: center;">\n        <h2>Smart Lightbulb</h2>\n        <img src="static/bulb-{{current}}.png" style="width: 150px;height: 174px">\n        <form>\n            <button type="submit" name="value" value="{{next}}" formmethod="post" style="border: outset;">Turn {{next}}</button>\n            <button type="submit" name="value" value="color" formmethod="post" style="border: outset;">Change color</button>\n        </form>\n    </div>\n    <div>\n        <form style="position: absolute;bottom: 10px">\n            <button type="submit" formaction="/update" style="border: none;">Update</button>\n        </form>\n        <span style="position: absolute;bottom: 10px;right: 10px">Version: {{version}}</span>\n    </div>\n</body>\n"""\n\nUPDATE_TEMPLATE = """\n<!DOCTYPE html>\n<head>\n<title>Update</title>\n<style>\ninput[type="submit"], input::file-selector-button {\n    background: hsl(210, 98%, 80%);\n    border: none;\n    border-radius: 5px;\n    padding: 0.85em 2.5em;\n\tcursor: pointer;\n    margin-top: 0.85em;\n    margin-bottom: 0.85em;\n}\n.flash {\n    background: rgba(200, 50, 50, 0.5);\n    border: black solid;\n    display: inline-block;\n    border-radius: 5px;\n    padding: 10px;\n}\n</style>\n</head>\n<body>\n    <div style="text-align: center;">\n        <h2>Firmware Update</h2>\n        <p>Current version: {{version}}</p>\n        {%with messages = get_flashed_messages()%}\n        {%if messages%}\n        {%for message in messages%}\n            <div class="flash">{{message}}</div><br>\n        {%endfor%}\n        {%endif%}\n        {%endwith%}\n        <form method=post enctype=multipart/form-data>\n            <label for="file">Select update file</label>\n            <input type="file" name="file">\n            <br>\n            <input type="submit" value="Start Update">\n        </form>\n        <br>\n        <p style="max-width: 500px;margin: auto;">If you have any problems with the latest version please <a href="/logs">Download</a> debug files and send them to cusomer support.\n    </div>\n</body>\n"""\n\nlog_file = logging.handlers.WatchedFileHandler(LOG_FILENAME)\nlog_stdout = logging.StreamHandler(sys.stdout)\nlogging.basicConfig(handlers=[log_stdout, log_file])\nlogger_switch = logging.getLogger("switch")\nlogger_switch.setLevel(logging.DEBUG)\nlogger_update = logging.getLogger("update")\nlogger_update.setLevel(logging.DEBUG)\n\napp = Flask(__name__)\napp.config["SEND_FILE_MAX_AGE_DEFAULT"] = 300\napp.secret_key = APP_KEY\n\n\n@app.route("/", methods=["GET", "POST"])\ndef index():\n    if request.method == "POST":\n        match request.form.get("value"):\n            case "on":\n                session["switch"] = True\n                logger_switch.info("Switch On")\n            case "off":\n                session["switch"] = False\n                logger_switch.info("Switch Off")\n            case "color":\n                session["color"] = not session.get("color")\n                session["switch"] = True\n                logger_switch.info("Switch Color")\n            case _:\n                session["switch"] = True\n                pass\n        return redirect("/")\n    match session.get("switch", False), session.get("color", False):\n        case [True, True]:\n            c_val, n_val = "color", "off"\n        case [True, False]:\n            c_val, n_val = "on", "off"\n        case [False, _]:\n            c_val, n_val = "off", "on"\n    return render_template_string(TEMPLATE, current=c_val, next=n_val, version=VERSION_STR)\n\n\ncrc16 = mkPredefinedCrcFun("x-25")\n\n\ndef do_update(file):\n    data = file.read()\n    if len(data) != 8272:\n        logger_update.info("Update file has wrong length")\n        return False\n    magic, version, signed, vendor, product, iv, data = struct.unpack("<8sI?3x16s16s16s8208s", data)\n    if magic != b"Update\\x00\\x00":\n        logger_update.info("Got wrong magic value %s", magic)\n        return False\n    if version <= VERSION_NUM:\n        logger_update.info("Disallowed downgrade from current version %d to %d", VERSION_NUM, version)\n        return False\n    logger_update.info("Valid update header for %s %s to version %d", vendor, product, version)\n    logger_update.info("Decrypting update content")\n    cipher = AES.new(AES_KEY, AES.MODE_CBC, iv)\n    data = cipher.decrypt(data)\n    logger_update.debug("Decrypted content: %s", data)\n    magic, firmware, check_crc = struct.unpack("<8s6x8192sH", data)\n    if magic != b"DECRYPT\\x00":\n        logger_update.info("Wrong magic on update content %s", magic)\n        return False\n    calc_crc = crc16(data[:-2])\n    if calc_crc != check_crc:\n        logger_update.info("CRC check failed. given: %d, calculated: %d", check_crc, calc_crc)\n        return False\n    if signed:\n        # we don\'t need this as data is encrypted\n        return False\n    logger_update.info("All checks passed, writing new firmware file")\n    open(UPDATE_FILENAME, "wb").write(firmware.strip(b"\\x00"))\n    os.chmod(UPDATE_FILENAME, 0o777)\n    logger_update.info("Firmware has been written")\n    return True\n\n\n@app.route("/update", methods=["GET", "POST"])\ndef update():\n    if request.method == "POST":\n        if "file" not in request.files:\n            flash("Something went wrong")\n        elif request.files["file"].filename == "":\n            flash("No file selected")\n        elif do_update(request.files["file"]):\n            logger_update.info("Update was successful. Rebooting...")\n            if "SERVER_SOFTWARE" in os.environ:\n                # running under gunicorn\n                os.kill(os.getppid(), signal.SIGTERM)\n            else:\n                os.kill(os.getpid(), signal.SIGINT)\n            flash("Update successful")\n            return """\n            <!DOCTYPE html>\n            <meta http-equiv="refresh" content="3">\n            Update running please wait...\n            """\n        else:\n            flash("Invalid update file")\n        return redirect("/update")\n    return render_template_string(UPDATE_TEMPLATE, version=VERSION_STR)\n\n\n@app.route("/logs")\ndef logs():\n    try:\n        logs = open(LOG_FILENAME, "rb").read()\n        os.unlink(LOG_FILENAME)\n        logger_switch.info("Log file downloaded, removing old log")\n    except FileNotFoundError:\n        logs = b""\n    return Response(logs, mimetype="text/plain", headers={"Content-disposition": "attachment"})\n\n\nif __name__ == "__main__":\n    app.run(debug=False)\n\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xde\t'