$ cat writeup.md…
$ cat writeup.md…
ASIS CTF Quals 2026
Task: an eSIM QR activation code leads to a GSMA SGP.22 SM-DP+ server requiring full ES9+ mutual auth with a CI-signed eUICC identity. Solution: custom Python LPA; test-eUICC key chain leaked in hidden HTML comments; hardcoded BSP keys decrypt the profiles; flag reassembled by diffing downloaded eSIM profiles.
$ cat /etc/rate-limit
Rate limit reached (20 reads/hour per IP). Showing preview only — full content returns at the next hour roll-over.
Still got an old phone lurking in your rig?
English summary: The challenge exposes two HTTP surfaces on the same host. Port 80 serves a simple page with an ASIS logo and a QR code; port 443 (dpp.asisctf.com) is a GSMA SGP.22 eSIM SM-DP+ server (the remote side of consumer eSIM provisioning). The goal is to complete the full RSP provisioning flow as an eSIM-capable device (an LPA) and recover the flag from the downloadable eSIM profiles. Flag format: ASIS{...}.
Step 0 — semantic clue. "Old phone" is not about old-browser User-Agents: the page's QR code is the entire hint. Decoding it (parse the 29×29 module grid out of the embedded base64 SVG path rects into a bitmap, then zxingcpp) yields:
LPA:1$dpp.asisctf.com$
That is a GSMA SGP.22 eSIM activation code (LPA:1$<SM-DP+ host>$<matching-id>) with an empty matching-id. The "old phone" is the thing that has an eSIM: we must emulate an LPA client.
Server identification. dpp.asisctf.com resolves to the same IP. HTTPS is fronted by nginx proxying a Twisted/Klein app. A POST to /gsma/rsp2/es9plus/initiateAuthentication with an empty body produced a full Python traceback revealing /opt/pysim/osmo-smdpp.py — the osmocom pySim SM-DP+ reference implementation (asn1tools, DER codec, RSP ASN.1; EUICCInfo1 carries tag BF20). Line numbers in later tracebacks matched a fresh GitHub clone of osmocom/pysim byte-for-byte, so the deployed server is stock code — anything hardcoded in the public source is exploitable as-is.
...
$ grep --similar