The challenge
"This JSP source is shaped like a webshell: it reads requests and stages server-side work. Recover the order of requests and stages, then separate the inert fixture behavior from the lines that would be real execution primitives in the wild."
Script is cumulative: the Single Solve asked 1,516 questions over one evidence.zip of 162 scripts. Today's new files were five JSP files with 11 questions each (55 in all). For the webshell host:
| answer_id | Question | Format |
|---|---|---|
host- |
"Classify the inert source role." | enum (benign_, downloader, dropper, staged_, …) |
host- |
Map the decoded ordered behaviour graph to one primary ATT&CK technique (with a stated precedence) | ATT&CK ID |
host- |
"Recover the ordered behavior graph using > separators." | tokens joined by > |
host- |
"Identify the script host or webshell source container." | exact text |
host-, -, - |
Stage count, multi-stage relationship profile, next language in the inert handoff | integer / exact text |
host-, -, -, - |
Modelled adapter, campaign ID, URL indicator, ordered encoding profile | exact text |
The Time Trial asked 1,560 questions in Day 23's timed schema (13 per polyglot file); the Mastery asked them across 8 sets (12,480).
Reading the prompt: what it said we'd need
| Clue | What it pointed to | What I did about it |
|---|---|---|
| "This JSP source" | New language: JavaServer Pages | Five . files: four polyglots and one host- fixture. The solver's generic plan reader covers any polyglot- or host- file, so nothing new was needed |
| "shaped like a webshell" | A host fixture with a webshell profile | Its plan names host_ = jsp_ (an older PHP fixture is php_) |
| "reads requests and stages server-side work" | Download-then-execute steps | The plan's steps: acquire_, stage profile two_ |
| "Recover the order of requests and stages" | Ordered fields | steps (joined by >), stage-, stage-, next- |
| "separate the inert fixture behavior from the lines that would be real execution primitives" | Role and ATT&CK classification, and trusting the decoded plan only when the code agrees | The solver uses a plan only if the header agrees with the body; otherwise it stops ("read it by hand") |
| "Web of Lies" (title) | A webshell, and possibly a descriptor that doesn't tell the truth | The header/body check is the guard. The one inconsistency found was inside the plan itself (below) |
Safety
Static analysis only. Nothing was executed. The scripts sit in a password-protected (WinZip AES) archive and were read as text and decoded in Python; no JSP container, Java runtime, or other interpreter was started. The archives stayed on our Kali VM. For this write-up the JSP source wasn't re-read: what it contains is described from the answers the Game Master accepted and the code that produced them. Indicators below are defanged.
What was in the files
- Single Solve:
evidence.zip, 144,836 bytes, 162 members: Day 23's 157 scripts pluspolyglot-jsp-{benign-utility,downloader,launcher,staged-chain}.jspandhost-jsp-webshell-jsp.jsp(1,253 bytes). - The webshell host's plan (accepted values): role
staged_chain, ATT&CKT1059, adapteroperation_graph, 2 stages, stepsacquire_remote>dispatch_process, host profilejsp_webshell, obfuscationbase64, stage profiletwo_stage_download_execute, next languagejscript, campaignJSP-0f2508fa, URLhxxps://jsp-0f2508fafa[.]example[.]invalid/stage. - The plan disagrees with itself. A download-and-execute graph would normally map to a downloader and
T1105(thepolyglot-jsp-downloader.jspplan has the same steps and isdownloader/T1105), but the webshell's plan recordsstaged_chain/T1059. The solver sends the plan's own values, and they were accepted, as on Day 23. - Time Trial:
evidence.zip, 163,210 bytes, also 162 files, in Day 23's timed schema: every file renamed to a 6-hex stem (588efb.jsp), question IDs built from it (polyglot-jsp-588efb-role), host files folded into the polyglot family, and aHUNTRESS_ANALYSIS_V1:LYR-…header naming the encoding layers in the order applied. The five JSP files had rolesbenign_utility(2),stager,downloader, andpersistence_descriptor, with layer lists such asb32_rev_b64andb64_frag_b64. - Mastery: 1,292,954 bytes, 8 sets in the timed schema.
So the timed variants were the same generator as Day 23's timed sets, with five JSP files per set added.
Single Solve
The solver got it in two batches, with no stop. Its core answered 1,386 questions, all 55 new ones included: it finds each polyglot-* or host-* file, parses the analysis header, checks that the header agrees with the body, and returns the plan field the question names. The core flagged 55 values it had seen rejected on other questions before (none, T1105, T1059.007, delayed, and similar) and sent them anyway, as designed; none was rejected. The add-on modules then sent the other 130.
started_at 2026-10-24T13:01:20.478Z
deadline none (Single Solve)
answers 1,386 core (accepted, remaining 130), then 130 add-on answers
start 608 ms
artifact 398 ms
solve 348 ms
submit 2261 ms (core 470, add-ons 1789)
TOTAL 3618 ms
result CORRECT flag 07da80aa…72b0
Time Trial
CORRECT on the first attempt, ranked. The question IDs were hex stems, so the routing added on Day 23 sent the set to the timed-schema adapter, which answers each file from its own decoded plan. No change was needed for JSP.
started_at 2026-10-24T13:01:29.390Z
deadline 2026-10-24T13:02:19.390Z (50 s)
answers 1,560
start 673 ms
artifact 450 ms
solve 61 ms
submit 989 ms
TOTAL 2175 ms (submit +1.69 s after started_at)
result CORRECT flag 980f4633…a7df
Mastery
CORRECT on the first attempt, ranked (8 sets, 260 s clock). Day 23's fix that scopes the .ps1 hand-off lookup to one inner zip held across the 8 sets.
started_at 2026-10-24T13:01:36.896Z
deadline 2026-10-24T13:05:56.896Z (260 s)
answers 12,480 across 8 sets
start 1240 ms
artifact 483 ms (1.3 MB)
solve 496 ms
submit 1943 ms
TOTAL 4179 ms (submit +3.59 s after started_at)
result CORRECT flag f0977c3e…5daf
Time to solve
All three modes were solved by 09:01:40 ET, 1 min 40 s after release (20 s from the first start request on this lane). The Time Trial and Mastery were both ranked and each went through on the first submit.
Takeaways
- Yesterday's fix carried today. Day 23's timed-schema adapter and the same-zip hand-off scope were written for GDScript; today's JSP files went through them unchanged in both timed modes.
- A generic plan reader beats a per-language parser. The solver never needed to understand JSP: it reads the analysis header and checks it against the body, whatever the language.
- The plan is the answer, even when it looks wrong. The webshell's
staged_chain/T1059doesn't match its download-and-execute steps, but the plan's own values were accepted. Don't "correct" a descriptor value from the rubric unless a rejection says to. - The prompt promises more than the questions ask. "The order of requests" sounded like HTTP parameters or verbs; the fields were the usual plan fields.