arrow_backUS Cyber Games 2026
Webmedium

Incident(al) Report

by Pavel Knespl (capyplivl)

A Flask status-page app serves GET /api/incidents, which mixes public and [INTERNAL] incidents. The filter defaults to returning everything when the public query parameter is omitted, so internal incidents (including one carrying the flag) leak.

Solve

  1. Inspect app.py. The list contains public and internal incidents, and one internal incident (#219 "[INTERNAL] Retro: customer-data anomaly") embeds the flag in a "Postmortem" update body via flag = os.environ.get("FLAG", ...).
  2. Read the filter logic:
    v = request.args.get("public")
    if v is None:                          selected = data
    elif v.lower() in {"1","true","yes"}:  selected = [i for i in data if i["public"]]
    elif v.lower() in {"0","false","no"}:  selected = [i for i in data if not i["public"]]
    else:                                  selected = [i for i in data if i["public"]]
    
    The front-end only ever calls ?public=1. Omitting public returns every incident; public=0 returns only the internal ones.
  3. Start the instance, then GET /api/incidents (or /api/incidents?public=0) and grep the response for the flag:
    curl -s 'https://<target>/api/incidents' | grep -oE 'SVIBGR\{[^}]*\}'
    
    The flag is in incident #219's "Postmortem" update body.

Flag: SVIBGR{pr073c7_y0ur_s747us_p4g3s}