Getting Started
Platform overview
Web eXploitation Laboratory (WXL) is a web-security challenge platform for learners. A challenge, the tools you attack it with, and the check on your answer all run inside your own browser tab.
- Opening a challenge page downloads a
runtime.wasmfile for that challenge. The challenge application's code and files are packed inside it. - The flag you submit is compared inside that in-browser WASM module. It is never sent to a server.
- Your notes, attack session records and Code drafts are written to an IndexedDB database named
challenge-toolsin this browser only. Nothing is uploaded, and clearing the site's browser data destroys all of it with no way to recover. - On arrival the site registers a Service Worker named
challenge-sw.js. It is what lets the browser intercept challenge URLs. It only takes over hostnames of the formchallenge-<slug>.localhost; every other request passes straight through, and it caches nothing for offline use.
System requirements
| Requirement | Why it is needed |
|---|---|
| A browser that runs WebAssembly | Each challenge page fetches a runtime.wasm module and executes it in the tab. |
| Service Workers allowed for this site | The site registers challenge-sw.js on arrival. Without it, requests to challenge-<slug>.localhost are not intercepted. |
| A live network connection while you work | The Python environment is loaded from an external CDN at https://cdn.jsdelivr.net/pyodide/v0.29.3/full/pyodide.js. Fully offline, Python challenges do not run. The first load pulls down tens of megabytes. |
| Browser storage (IndexedDB) available | Notes, attack records and Code drafts live in the challenge-tools database. If it cannot be opened, a warning bar that you cannot dismiss appears at the top of the challenge page saying the session is not being recorded, and after a correct flag the attack-record download button does not appear at all — only the pentest-notes one, which then downloads a Markdown file with only the heading and no notes. |
| A desktop-width screen | The Browser panel's back, forward and reload buttons are only rendered at desktop widths; at phone widths the whole group is hidden and only the address bar and Go remain. |
The Service Worker registration and the Pyodide download are the two things that most often leave a panel stuck. If a panel will not respond, output never appears, or notes cannot be saved, go to Troubleshooting.
Quick start
Step 1: Pick a challenge
- Open the challenge list. Each row shows a zero-padded number such as
#003, the title, a difficulty badge, a category badge and a date, with the description and tags on a second line. - Filter with the toolbar. The search box matches the title, description and tags only — typing a difficulty or a date matches nothing. Results refresh 300 ms after you stop typing.
- The difficulty dropdown is a fixed set of four English options: Easy, Medium, Hard, Mystery. The category dropdown is built from the categories that actually occur in the list. Difficulty badges print the raw frontmatter string in lowercase, in English, in both interface languages.
- The count on the right of the toolbar is how many challenges survive the current filter. The button at the top right switches between the default list view and a grid view.
- Click a row to enter the challenge.
The #NNN number is a load-order index, not something the author assigns. Adding or renaming a challenge folder shifts the numbers, and the English and Chinese lists are numbered independently — do not use the number to refer to a challenge.
Step 2: Work in the panels
- The challenge page opens on the Browser tab. By default the tab strip holds four tabs: Browser, Network, Repeater and Code. The labels are hardcoded English in both interface languages.
- A challenge author may restrict which tool tabs are offered, but the Browser tab is always kept and never disappears.
- The Browser panel's address bar arrives prefilled with
https://challenge-<slug>.localhost/for the challenge you are in. It is an ordinary editable text field: press Enter or click Go to navigate. - Watch the status light at the top right of the nav bar. A pulsing yellow dot means the runtime is still loading, a green dot means it is ready, and a red dot means it failed — hover the red dot to read the actual error message.
- The Code tab's Python environment only starts downloading the first time you open that tab, not when the challenge page loads. Expect a wait on that first click.
Step 3: Submit the flag
- Paste the flag into the submission field. Its placeholder is
CTF{...}and the button next to it readsSubmit Flag— both hardcoded English. - A correct flag produces a single hardcoded English line,
Correct! Challenge solved., and two download buttons appear beside it, one for the attack record and one for the pentest notes. Those two button labels are translated; the sentence next to them is not. - A wrong flag produces a single hardcoded English line,
Incorrect flag. Try again.It does not tell you which part was wrong, and there is no limit on the number of attempts. - With the runtime ready and the attack record successfully created, every submission — wrong ones included — is written into that challenge's attack record together with the exact text you typed. A correct one additionally writes a
challenge_solvedevent and stamps the solve time.
Two things about this step are easy to misread:
- The Submit Flag button is never disabled and has no pending state. Submitting while the status light is still yellow returns
Incorrect flag. Try again.— that submission was rejected because the challenge's WASM runtime had not finished loading, and it is not written into the attack record either. Wait for the green dot before you read a rejection as a wrong answer. - If the
challenge-toolsdatabase could not be opened, no attack session exists, so after a correct flag you get the congratulation line and one download button instead of two. The attack-record button is not greyed out — it is absent. The pentest-notes button still appears, but pressing it downloads a Markdown file with no notes in it.
If you collapse the challenge description on the left, a flag submission bar stays pinned to the bottom of the screen, so you can submit without expanding it again.
Tool panel overview
| Panel | Where it lives | What it gives you |
|---|---|---|
| Code Editor | Code tab | A Python editor over an output pane, split 65 / 35 at first open with a draggable divider between them. Run and Stop buttons, a per-challenge execution history, and a settings popover for autocomplete, bracket auto-closing and font size. See Python Code Editor. |
| Browser | Browser tab | A prefilled, editable address bar over the rendered response, with back, forward and reload buttons at desktop widths. Back and forward replay stored screens and issue no new request. See Browser panel. |
| Network Traffic | Network tab | A log headed Network Traffic with the current entry count beside it, five columns — #, Method, URL, Status, Time — and a Clear button. Requests from the Browser panel, the Repeater and the Code Editor all land in this one log; none of the three is excluded. See Network and Repeater. |
| Repeater | Repeater tab | A free-text Raw HTTP Request box, prefilled with a GET / request whose Host is challenge-<slug>.localhost, plus a Send button and a saved-snapshot sidebar. Expanding a row in the Network log offers Send to Repeater, which switches to this tab and overwrites whatever is in the box. See Network and Repeater. |
| Pentest Notes | Nav bar button, opens a panel | Per-challenge notes with a Markdown preview, a search box and a newest/oldest sort toggle. The button carries a count badge once the challenge has at least one note. Text in the editor is saved only when you press Save. See Pentest Notes. |
The Network log lives in the page you are on. Navigating away or switching language empties it completely.