Skip to content

Add defense CTF challenges, IDS integration, and CTF UX improvements#181

Merged
mniedermaier merged 11 commits into
mainfrom
feature/defense-ctf-challenges
Mar 26, 2026
Merged

Add defense CTF challenges, IDS integration, and CTF UX improvements#181
mniedermaier merged 11 commits into
mainfrom
feature/defense-ctf-challenges

Conversation

@mniedermaier
Copy link
Copy Markdown
Owner

Summary

  • 5 new defense challenges with automated verification: credential hardening (OpenPLC + FUXA), Modbus firewall rules, network segmentation, IDS monitoring & tuning
  • Redesigned CTF page with category tile layout, challenge type tags (recon/exploit/analysis/evasion/defense/hardware), step badges, and per-category progress
  • IDS-integrated detection challenges: detect_basic and detect_overwrite now embed flags in the IDS /api/rules/stats endpoint — students discover the flag by triggering the IDS rule and querying the API
  • Progressive 3-tier hints on challenge detail pages (nudge → detail → solution pointer)
  • Better flag submission feedback distinguishing format errors from wrong values
  • Browser history navigation (back/forward buttons work in CTF iframe)
  • Collapsible hints added to all 20 training module READMEs
  • Bug fixes: IDS port scan rule stale entry pruning, evasion challenge arp_spoof false positives, IDS tuning check wrong status field, fenced code block rendering in markdown, iframe navigation

Changes

New files

  • software/landing/modules/defense_checks/ — 5 verification modules (check OpenPLC/FUXA passwords, firewall rules, network segmentation, IDS tuning)
  • training/defense_password/, training/defense_firewall/, training/defense_network/, training/defense_ids/ — training READMEs for defense challenges
  • docker-compose.dev.yaml — minimal dev setup for UI testing

Modified

  • software/ids/ — per-rule CTF flags, port scan pruning fix, evasion alert counting fix
  • software/OpenPLC/Dockerfile — added iptables for firewall challenges
  • software/landing/ — CTF config (21 challenges, 6 categories), templates, ctf_manager, app routes
  • training/ — all 20 READMEs updated with hints; detect_basic and detect_overwrite rewritten with IDS integration

Test plan

  • All 5 defense challenges verified end-to-end (password change, iptables, IDS tuning)
  • All 3 IDS challenge solve scripts pass (ids_challenge, ids_forensics, ids_evasion)
  • detect_basic: port scan triggers port_scan rule, flag appears in /api/rules/stats
  • detect_overwrite: Modbus flood triggers modbus_flood rule, flag appears in /api/rules/stats
  • Flag submission feedback: format errors vs value errors distinguished
  • CTF page loads with 21 challenges across 6 categories
  • Browser back/forward navigation works
  • Progressive hints render correctly (3 tiers)
  • Markdown fenced code blocks render without language identifier as literal text

🤖 Generated with Claude Code

Matthias Niedermaier and others added 9 commits March 21, 2026 22:43
Add 5 defense challenges (password hardening, firewall rules, network
segmentation, IDS tuning) with automated verification modules that check
real system state. Add defense verification endpoint and UI support.

Fix IDS port scan rule not pruning expired entries before threshold check,
causing false alerts from stale scan data. Fix evasion challenge counting
background arp_spoof alerts as failures. Fix IDS tuning check reading
wrong status field ("running" vs "active"). Add iptables to OpenPLC
container for firewall challenges.

Update IDS training READMEs for consistent formatting and add all new
modules to training index.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The back button used target="_top" and success redirects used
window.top.location, both breaking out of the landing page iframe.
Use postMessage to navigate within the CTF iframe instead.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Indented fenced code blocks (```bash) inside list items were not
recognized by the markdown parser, causing the language identifier
to appear as literal text. Add preprocessing step to dedent fenced
code blocks before rendering.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Redesign CTF page with tile grid layout — each category gets its own
card with per-category progress. Add challenge type tags (recon, exploit,
analysis, evasion, defense, hardware) with distinct colors. Move IDS
challenges to their own category.

Make hints collapsible on challenge detail page so students can try
without seeing the hint first. Show tag badge on challenge detail header.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Every training README now has a 💡 Hint section using <details> so
students can try independently before revealing guidance. Hints give
a nudge toward the approach without spoiling the full solution.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ath, and History API navigation

Feature 1 - Challenge filtering: Add filter bar with pills for tag
(recon/exploit/analysis/evasion/defense/hardware), difficulty
(easy/medium/hard), and status (solved/unsolved). Client-side JS
filtering hides non-matching rows and empty category tiles.

Feature 2 - Progressive hints: Extract hint from training README
and show 3 collapsible tiers on challenge detail page: Hint 1
(quick nudge from config), Hint 2 (detailed guidance from README),
Hint 3 (points to training material solution). Each tier has a
distinct color (orange/yellow/red).

Feature 3 - Better flag submission feedback: Distinguish format
errors ("Flags look like CybICS(...)") from value errors ("format
is right, but the value is wrong"). Add client-side pre-validation
and format hint below input field.

Feature 4 - Learning path: Add numbered step badges to category
tiles and a recommended progression bar showing the path across
all categories.

Feature 8 - History API navigation: Push browser history state on
CTF navigation so back/forward buttons work. Restore view and
iframe URL from hash on page load. URL scheme: #ctf, #ctf:/path.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Simplify the CTF overview — the filter bar and recommended path
bar added visual clutter without enough value for the use case.
Step badges on category tiles remain for ordering guidance.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Embed CTF flags in the IDS /api/rules/stats endpoint: when a detection
rule fires, the flag for that challenge appears in the rule's JSON
response. Students must perform the attack AND query the IDS to find it.

- detect_basic: port_scan rule reveals CybICS(sc4n_d3tect3d)
- detect_overwrite: modbus_flood rule reveals CybICS(m0dbus_fl00d_d3tect3d)

Rewrite both READMEs with 3-phase structure:
  Phase 1: Launch the attack (recon.py / override.py)
  Phase 2: IDS analysis (query /api/rules/stats, find the flag)
  Phase 3: PCAP forensics (Wireshark analysis + correlation)

Students now experience the full detection loop: attacker action →
network packets → IDS rule → observable alert → flag discovery.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Resolve conflicts in training/README.md, ids_challenge/README.md,
and ids_forensics/README.md — keep defense module entries and IDS
MITRE alignment tables from both branches.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Comment thread software/landing/app.py
result['points'] = submit_result['points']
result['message'] = f"{result['message']} You earned {submit_result['points']} points!"

return jsonify(result)

Check warning

Code scanning / CodeQL

Information exposure through an exception Medium

Stack trace information
flows to this location and may be exposed to an external user.

Copilot Autofix

AI about 2 months ago

In general, the problem should be fixed by ensuring that exception objects (or their string representations) are never sent directly to clients. Instead, log detailed error information on the server and return a generic, user-friendly error message to the client.

The best targeted fix here is to change the except Exception as e block in CTFManager.verify_defense so that it continues to log the detailed error (including stack trace) but returns a generic message that does not contain str(e). We do not need to change the /ctf/verify/<challenge_id> route in app.py; it can continue to return whatever verify_defense produces. That keeps existing control flow and success behavior intact while only altering the contents of error messages. Concretely, in software/landing/modules/ctf_manager.py, lines 130–132 should be updated to:

  • Keep the logger.error(..., exc_info=True) call so that developers still see the detailed error and stack trace.
  • Replace the returned dict’s "message" value from f'Verification error: {str(e)}' to a generic string such as 'An internal verification error occurred.' (or similarly non-revealing text).

No new methods or imports are required; we rely only on the existing logger.

Suggested changeset 1
software/landing/modules/ctf_manager.py
Outside changed files

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/software/landing/modules/ctf_manager.py b/software/landing/modules/ctf_manager.py
--- a/software/landing/modules/ctf_manager.py
+++ b/software/landing/modules/ctf_manager.py
@@ -129,7 +129,11 @@
             return {'success': False, 'message': f'Verification module not found: {verify_module}', 'checks': []}
         except Exception as e:
             logger.error(f"Error running defense check {verify_module}: {e}", exc_info=True)
-            return {'success': False, 'message': f'Verification error: {str(e)}', 'checks': []}
+            return {
+                'success': False,
+                'message': 'An internal verification error occurred.',
+                'checks': []
+            }
 
     def reset_progress(self):
         """Reset all progress"""
EOF
@@ -129,7 +129,11 @@
return {'success': False, 'message': f'Verification module not found: {verify_module}', 'checks': []}
except Exception as e:
logger.error(f"Error running defense check {verify_module}: {e}", exc_info=True)
return {'success': False, 'message': f'Verification error: {str(e)}', 'checks': []}
return {
'success': False,
'message': 'An internal verification error occurred.',
'checks': []
}

def reset_progress(self):
"""Reset all progress"""
Copilot is powered by AI and may make mistakes. Always verify output.
Matthias Niedermaier and others added 2 commits March 26, 2026 20:00
- Restructure all 20 training READMEs: teach essential knowledge upfront,
  move hints to bottom, add consistent Task sections with numbered steps,
  flag format lines, and solution sections
- Redesign challenge.html with sidebar TOC, scrollspy navigation,
  collapsible sections by H2 headings, syntax highlighting (highlight.js),
  copy buttons on code blocks, language labels, and visual section accents
- Simplify hint system: remove 3-tier hint extraction, render hints
  naturally from markdown
- Clean markdown: remove redundant details/summary wrappers, standardize
  flag displays, ensure plain markdown readability

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Remove Approach 2 (firewall on attacker machine) from defense_network
  challenge — only teach rules on target containers
- Rewrite check_network_segmentation verify module to check iptables
  rules on target containers instead of testing from attacker
- Fix fuzzingMB hint to match actual interactive menu workflow
- Add explanation for FUXA vs OpenPLC ffuf command differences
- Fix wireshark_capture hint to include both Modbus function codes

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@mniedermaier mniedermaier merged commit 9405d2b into main Mar 26, 2026
9 of 11 checks passed
@mniedermaier mniedermaier deleted the feature/defense-ctf-challenges branch March 26, 2026 19:10
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.

2 participants