Skip to content

fix: CTF challenge lifecycle status#189

Closed
niklashaug wants to merge 48 commits into
mniedermaier:mainfrom
niklashaug:fix-challenge-lifecycle-status
Closed

fix: CTF challenge lifecycle status#189
niklashaug wants to merge 48 commits into
mniedermaier:mainfrom
niklashaug:fix-challenge-lifecycle-status

Conversation

@niklashaug
Copy link
Copy Markdown
Contributor

Fixes the lifecycle status so it automatically shows "Starting", "Started" and "Stopped" status in the UI.

frnzfk and others added 30 commits March 18, 2026 10:20
Implements the update mechanism described in issue #3:
- Python daemon polling http://update.cybics:8080 for firmware updates
- MAC verification using MD5(secret || firmware) - vulnerable to Length Extension Attack
- Flash via OpenOCD telnet (port 4444)
- Docker container with entrypoint that generates a 16-byte MAC key on first run
- CTF setup helper (generate_mac.py)
- Integration into both software/docker-compose.yaml and .devcontainer/virtual/docker-compose.yml

Co-authored-by: zierh <48314848+zierh@users.noreply.github.com>
Agent-Logs-Url: https://github.com/niklashaug/CybICS/sessions/cfab9622-f941-46ac-b589-03acc5207742
Tests cover:
- MAC verification (valid/invalid MAC, modified firmware, wrong secret)
- Pure-Python MD5 Length Extension Attack proof (forges valid MAC without the secret)
- save_firmware function
- flash_via_openocd with mock OpenOCD TCP server
- poll_for_update: happy path, 404, bad MAC, unreachable server
- Docker image build, entrypoint key generation, generate_mac.py script

Co-authored-by: zierh <48314848+zierh@users.noreply.github.com>
Agent-Logs-Url: https://github.com/niklashaug/CybICS/sessions/ce8276a7-c2c6-45a1-a7f0-ca5e43cffd92
…ded.sh)

- Add flash_firmware.sh: pipes OpenOCD program command via nc to the running
  OpenOCD telnet server, following the same shell-script pattern as
  flash_if_needed.sh in the stm32 container
- Update Dockerfile to install netcat-openbsd and copy flash_firmware.sh
- Replace raw Python socket/telnet code in update_daemon.py with a subprocess
  call to flash_firmware.sh
- Add _SCRIPT_TIMEOUT_BUFFER constant for clarity; all 16 tests still pass

Co-authored-by: zierh <48314848+zierh@users.noreply.github.com>
Agent-Logs-Url: https://github.com/niklashaug/CybICS/sessions/136a9380-5004-431e-8cf2-252dc04cc859
frnzfk and others added 18 commits April 13, 2026 13:12
# Conflicts:
#	software/landing/templates/challenge.html
add draft for CTF challenge
the /home/docker/zephyrproject/app/ directory was owned by root, which
stopped the docker user from building or rather creating the build
directory and finishing 'west build'.
the url path '../OpenPLC_v3' could not be resolved on any other dev
machine.
@niklashaug niklashaug closed this May 5, 2026
@niklashaug
Copy link
Copy Markdown
Contributor Author

Sorry, this was a mistake and this PR should have landed in our fork repo.

firmware = f.read()
with open(key_path, "rb") as f:
secret = f.read()
return hashlib.md5(secret + firmware).hexdigest()
Intentionally vulnerable to Length Extension Attack because MD5 uses
the Merkle-Damgård construction.
"""
expected = hashlib.md5(secret + firmware).hexdigest()
Comment thread software/landing/app.py
def challenge_status(challenge_id):
"""Get lifecycle status for a challenge"""
result, status_code = lifecycle_manager.get_status(challenge_id)
return jsonify(result), status_code
Comment thread software/landing/app.py
def start_challenge_environment(challenge_id):
"""Start lifecycle resources for a challenge"""
result, status_code = lifecycle_manager.start_challenge(challenge_id)
return jsonify(result), status_code
Comment thread software/landing/app.py
def stop_challenge_environment(challenge_id):
"""Stop lifecycle resources for a challenge"""
result, status_code = lifecycle_manager.stop_challenge(challenge_id)
return jsonify(result), status_code
Comment thread software/landing/app.py
def challenge_status(challenge_id):
"""Get lifecycle status for a challenge"""
result, status_code = lifecycle_manager.get_status(challenge_id)
return jsonify(result), status_code
Comment thread software/landing/app.py
def start_challenge_environment(challenge_id):
"""Start lifecycle resources for a challenge"""
result, status_code = lifecycle_manager.start_challenge(challenge_id)
return jsonify(result), status_code
Comment thread software/landing/app.py
def start_challenge_environment(challenge_id):
"""Start lifecycle resources for a challenge"""
result, status_code = lifecycle_manager.start_challenge(challenge_id)
return jsonify(result), status_code
Comment thread software/landing/app.py
def stop_challenge_environment(challenge_id):
"""Stop lifecycle resources for a challenge"""
result, status_code = lifecycle_manager.stop_challenge(challenge_id)
return jsonify(result), status_code
Comment thread software/landing/app.py
def stop_challenge_environment(challenge_id):
"""Stop lifecycle resources for a challenge"""
result, status_code = lifecycle_manager.stop_challenge(challenge_id)
return jsonify(result), status_code
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants