SSOS
SSOS is a sanitized challenge note from the local HTB archive, organized for quick review by category, difficulty, evidence flow, and reusable operator
Scenario
SSOS attack path
SSOS is a sanitized challenge note from the local HTB archive, organized for quick review by category, difficulty, evidence flow, and reusable operator
Objective
Challenge walkthrough focused on Web evidence, validation, and reusable operator lessons.
Walkthrough flow
Source and route audit
Trust boundary flaw
Exploit request chain
Admin or proof proof
Source coverage
High source coverage
Status: complete. This article is generated from 6 sanitized Markdown sources and keeps raw flags, credentials, keys, cookies, and reusable secrets out of the rendered blog.
High confidence: the page is reconstructed from a primary walkthrough plus multiple supporting notes or evidence sources. Treat the chain as source-backed, while still checking the listed source files for sensitive values.
- Web/SSOS/writeup.md
- htb-challenge/Web/SSOS/notes.md
- htb-challenge/Web/SSOS/memory-summary.md
- htb-challenge/Web/SSOS/hypothesis-board.md
- HTB/_knowledge/exports/ctf-lightrag-latest-203412/documents/challenge__Web__SSOS__memory-summary.md.caa61182ec.md
- HTB/_knowledge/exports/ctf-lightrag-latest-203412/documents/challenge__Web__SSOS__notes.md.e26a435e57.md
Technical Walkthrough
Writeup
Challenge
- Name: SSOS
- Category: Web
- Difficulty: Medium
- Mode: hybrid
Summary
Artifact Inventory
Reference analysis/artifact-inventory.json and summarize the relevant files or remote surface.
Analysis
Solve
Flag
Raw flag is stored in loot/flag.txt and intentionally not reproduced here.
Lessons
Source-Backed Dossier
The sections below are merged from companion Markdown notes for the same case. They are rendered after sanitization so the article stays precise without publishing raw flags, credentials, or target-specific secrets.
Notes
Scope
- Challenge: SSOS
- Category: Web
- Difficulty: Medium
- Mode: hybrid
- Remote instance: <TARGET>:30182
- Start time: 2026-06-11T00:12:14Z
- Operator: harness
- State file:
challenge-state.json
Harness Status
- Current phase: see
challenge-state.json - Next allowed actions: see
next-action.json - Raw flags and sensitive material stay in
loot/only. Do not paste them here.
Artifact Inventory
| File | Size | SHA256 | Type | Notes |
|---|---|---|---|---|
files/a12c7371-e2ee-4e9b-a80b-94b37779c389.zip | 66992 | <hash redacted> | Zip archive data, at least v1.0 to extract, compression method=store | zip entries: 70 shown in artifact inventory JSON |
Evidence Ledger
| Time | Action | Output/File | Finding | Confidence | Next |
|---|---|---|---|---|---|
| 2026-06-11T00:12:14Z | harness init | challenge-state.json | Workspace initialized with deterministic state file | High | Inventory artifacts |
| 2026-06-11T00:12:29Z | artifact inventory | analysis/artifact-inventory.json | 1 artifact(s) inventoried | High | Build or update hypotheses |
| 2026-06-11T00:14:49Z | hypothesis recorded | hypothesis-board.md | Leak a teacher OAuth authorization code through path-normalized redirect_uri into an assignment page with an attacker-controlled image, then redeem the code via the normal client callback to become teacher and read the flag submission. | High | Register as student, create a submission with external image beacon, report a crafted authorize URL with redirect_uri=/oauth/callback/../assignment/<id>, confirm beacon receives code in Referer, then redeem via /oauth/callback. |
| 2026-06-11T00:14:49Z | checkpoint recorded | analysis/checkpoint-analysis-20260611T001449626905Z-02153150.md | Checkpoint for ANALYSIS | High | Use checkpoint to drive next decision |
| 2026-06-11T00:14:56Z | source audit | analysis/source-audit.md | Source audit recorded | High | Gate before exploit |
| 2026-06-11T00:15:07Z | research task | analysis/research/task-20260611T001507445894Z-96eec801.md | Research task created for advisory investigation | Medium | Record research output |
| 2026-06-11T00:15:08Z | local memory search | analysis/research/local-memory-search-20260611T001508380599Z-34979848.md | Found 8 safe prior-note result(s) | Medium | Record useful result or skip |
| 2026-06-11T00:16:02Z | evaluator | analysis/evaluator-20260611T001602582811Z-10d20595.md | Validate first | High | run_live_preflight_then_marker_beacon_validation |
| 2026-06-11T00:16:41Z | research record | analysis/research/research-records.md | Research tagged GENERIC | Medium | Validate against current evidence |
| 2026-06-11T00:23:41Z | branch closed | hypothesis-board.md | An external img beacon alone is not reliable enough to leak an OAuth code from the assignment URL; it can validate bot reachability but not full URL exfil. | High | Rerank hypotheses |
| 2026-06-11T00:59:03Z | local memory record | analysis/local-memory-records.md | Prior local notes reviewed as fallback/advisory context | Medium | Validate against current evidence |
| 2026-06-11T00:59:03Z | evaluator | analysis/evaluator-20260611T005903373701Z-830cf0cf.md | Proceed | High | Write a reproducible solver/probe harness, validate remote sanitizer behavior, then run the OAuth/bot chain only if the primitive is live. |
| 2026-06-11T01:11:15Z | RAG query | analysis/rag/rag-query-20260611T011106000117Z-860a439b.txt | RAG helper exited 0; output saved | Medium | Record retrieval tag and validation |
| 2026-06-11T01:29:21Z | branch closed | hypothesis-board.md | Chrome 130 default strict-origin-when-cross-origin strips path/query on cross-origin image requests, so it does not leak the OAuth code. | High | Rerank hypotheses |
| 2026-06-11T01:29:21Z | branch closed | hypothesis-board.md | Live sanitizer behavior matches patched sanitize-html behavior; xmp/noembed/plaintext payloads did not produce executable HTML. | High | Rerank hypotheses |
| 2026-06-11T01:29:21Z | branch closed | hypothesis-board.md | Teacher session cookies were not sent for the cross-site POST in the tested browser context, and available POST actions do not read flag content anyway. | High | Rerank hypotheses |
| 2026-06-11T01:29:21Z | branch closed | hypothesis-board.md | Runtime env password generation is active on the remote; fallback credentials are not valid. | High | Rerank hypotheses |
| 2026-06-11T01:29:21Z | checkpoint recorded | analysis/checkpoint-analysis-20260611T012921989556Z-1eec5047.md | Checkpoint for ANALYSIS | High | Use checkpoint to drive next decision |
| 2026-06-11T01:29:32Z | RAG record | analysis/rag-records.md | Retrieved memory tagged MISSING | Medium | Validate or reject with live evidence |
| 2026-06-11T01:30:50Z | hypothesis recorded | hypothesis-board.md | No current proven exploit path after closing OAuth/referrer and sanitizer branches; next work must identify a new same-origin code sink, actual XSS primitive, or direct authorization bypass. | Medium | Audit only one new candidate at a time against source, then validate live with a disposable student and collector if source-backed. |
| 2026-06-11T01:30:50Z | checkpoint recorded | analysis/checkpoint-analysis-20260611T013050377847Z-09407c20.md | Checkpoint for ANALYSIS | High | Use checkpoint to drive next decision |
| 2026-06-11T02:26:47Z | branch closed | hypothesis-board.md | No executable HTML or flag-content leak is created by score mutation. | High | Rerank hypotheses |
| 2026-06-11T02:26:47Z | branch closed | hypothesis-board.md | No redirect_uri parser split was found to deliver the teacher code directly to attacker infrastructure. | High | Rerank hypotheses |
| 2026-06-11T02:26:47Z | branch closed | hypothesis-board.md | The attempted auth-refresh plus top-level POST did not carry a usable EduLearn session for the mutation. | High | Rerank hypotheses |
| 2026-06-11T02:26:47Z | branch closed | hypothesis-board.md | No payload survived remote sanitization in an executable browser context. | High | Rerank hypotheses |
| 2026-06-11T02:27:04Z | evaluator | analysis/evaluator-20260611T022704174413Z-e995f7b7.md | Do not proceed | High | Stop repeating OAuth/referrer/sanitizer variants. Search for a specific executable primitive or alternate authorization bug. |
| 2026-06-11T02:27:04Z | checkpoint recorded | analysis/checkpoint-analysis-20260611T022704286293Z-c00bbbd1.md | Checkpoint for ANALYSIS | High | Use checkpoint to drive next decision |
| 2026-06-11T02:55:40Z | research record | analysis/research/research-records.md | Research tagged PARTIAL | Medium | Validate against current evidence |
| 2026-06-11T02:55:50Z | hypothesis recorded | hypothesis-board.md | Public hints point to an XSS/OAuth/teacher-bot chain, but the exact executing primitive remains unidentified. | Medium | Run the challenge locally with an instrumented bot/browser and test only source-backed DOM/sanitizer/client-side candidates until one produces real JavaScript execution. |
| 2026-06-11T02:56:08Z | checkpoint recorded | analysis/checkpoint-analysis-20260611T025608671791Z-7bc98c5b.md | Checkpoint for ANALYSIS | High | Use checkpoint to drive next decision |
| 2026-06-11T02:56:10Z | research record | analysis/research/research-records.md | Research tagged PARTIAL | Medium | Validate against current evidence |
| 2026-06-11T02:56:10Z | checkpoint recorded | analysis/checkpoint-analysis-20260611T025610628024Z-057ff9d8.md | Checkpoint for ANALYSIS | High | Use checkpoint to drive next decision |
| 2026-06-11T02:57:00Z | checkpoint recorded | analysis/checkpoint-analysis-20260611T025700708157Z-826e4802.md | Checkpoint for ANALYSIS | High | Use checkpoint to drive next decision |
| 2026-06-11T02:57:22Z | research record | analysis/research/research-records.md | Research tagged PARTIAL | Medium | Validate against current evidence |
| 2026-06-11T02:57:22Z | research record | analysis/research/research-records.md | Research tagged PARTIAL | Medium | Validate against current evidence |
| 2026-06-11T03:33:14Z | instrumentation plan | analysis/instrumentation-plan.md | Find a source-backed Chrome-executing client-side primitive or same-origin OAuth-code storage sink before any further live exploit attempt. | High | Stop immediately if local Chrome does not execute the primitive, if no same-origin storage/reflection sink exists, or if the remote behavior differs from the local proof. Do not repeat closed OAuth referrer, sanitizer, score sink, CSRF, or parser-differential branches without new evidence. |
| 2026-06-11T03:58:55Z | branch closed | hypothesis-board.md | No attacker-readable code exfiltration or JavaScript execution was produced. | High | Rerank hypotheses |
| 2026-06-11T03:58:55Z | checkpoint recorded | analysis/checkpoint-analysis-20260611T035855380968Z-dfabc1e0.md | Checkpoint for ANALYSIS | High | Use checkpoint to drive next decision |
| 2026-06-11T03:58:55Z | evaluator | analysis/evaluator-20260611T035855432554Z-93b1f49f.md | Do not proceed | High | Find a new executable client-side primitive or same-origin storage/reflection route; do not run live exploit attempts until that exists. |
| 2026-06-11T04:00:13Z | checkpoint recorded | analysis/checkpoint-analysis-20260611T040013240997Z-be632fbf.md | Checkpoint for ANALYSIS | High | Use checkpoint to drive next decision |
| 2026-06-11T04:57:09Z | branch closed | hypothesis-board.md | Server allowlist and Object.entries rendering prevent attacker-controlled executable reaction markup. | High | Rerank hypotheses |
| 2026-06-11T04:57:09Z | branch closed | hypothesis-board.md | Fragment placement changes where the code lands but does not make it attacker-readable or executable. | High | Rerank hypotheses |
| 2026-06-11T04:57:09Z | branch closed | hypothesis-board.md | The bot cannot be made to submit unknown teacher credentials, and the logged-in teacher state does not auto-trigger the redirect. | High | Rerank hypotheses |
| 2026-06-11T04:57:09Z | branch closed | hypothesis-board.md | The tested pollution path does not cross into teacher-rendered executable output or authenticated profile state. | High | Rerank hypotheses |
| 2026-06-11T04:57:09Z | checkpoint recorded | analysis/checkpoint-analysis-20260611T045709716574Z-caca2dc3.md | Checkpoint for ANALYSIS | High | Use checkpoint to drive next decision |
| 2026-06-11T04:57:09Z | evaluator | analysis/evaluator-20260611T045709769205Z-f1065996.md | Do not proceed | High | Stop repeating current candidates. Continue only if new source-backed sink or specific public hint identifies a Chrome-executing primitive. |
| 2026-06-11T05:44:15Z | checkpoint recorded | analysis/checkpoint-analysis-20260611T054415977022Z-966081d9.md | Checkpoint for ANALYSIS | High | Use checkpoint to drive next decision |
| 2026-06-11T05:54:54Z | evaluator | analysis/evaluator-20260611T055454315842Z-a9ca284c.md | Proceed | High | Run bounded revalidation scripts only, then record a fresh evaluator. |
| 2026-06-11T05:57:14Z | branch closed | hypothesis-board.md | The refreshed target is reachable, but stored HTML payloads still strip scripts, event handlers, and raw-text wrappers; no executable browser primitive was recovered. | High | Rerank hypotheses |
| 2026-06-11T05:57:14Z | branch closed | hypothesis-board.md | Score mutation accepts unsafe-shaped values, but assignment rendering escapes HTML and stats normalize non-numeric values; no executable or flag-reading sink appears. | High | Rerank hypotheses |
| 2026-06-11T05:57:14Z | branch closed | hypothesis-board.md | Bot accepts the submitted URL, but the attempted auth-refresh plus top-level POST does not produce a useful teacher-side mutation; observed score remained normal auto-score. | High | Rerank hypotheses |
| 2026-06-11T05:57:24Z | checkpoint recorded | analysis/checkpoint-analysis-20260611T055724490561Z-b47bb46d.md | Checkpoint for ANALYSIS | High | Use checkpoint to drive next decision |
| 2026-06-11T05:57:32Z | evaluator | analysis/evaluator-20260611T055732934169Z-2c022405.md | Do not proceed | High | Stop live retries; find a new source-backed primitive or trusted specific hint. |
| 2026-06-11T05:59:53Z | evaluator | analysis/evaluator-20260611T055953493988Z-e326e2c1.md | Proceed | High | Run harmless bot reachability port check, then restore Do not proceed unless a new primitive is identified. |
| 2026-06-11T06:02:06Z | checkpoint recorded | analysis/checkpoint-analysis-20260611T060206889798Z-711e2fee.md | Checkpoint for ANALYSIS | High | Use checkpoint to drive next decision |
| 2026-06-11T06:02:06Z | branch closed | hypothesis-board.md | Teacher bot runs inside the challenge container and expects internal port 1337; submitting edulearn.htb:30182 to the bot produced a server-side failure. | High | Rerank hypotheses |
| 2026-06-11T06:02:19Z | evaluator | analysis/evaluator-20260611T060219351249Z-9098f4ef.md | Do not proceed | High | Do not repeat live probes; build or identify one new primitive using the corrected bot port model. |
Key Findings
- 2026-06-11: New operator-supplied remote instance is
<TARGET>:30182. - 2026-06-11: Operator-supplied Downloads artifact
a12c7371-e2ee-4e9b-a80b-94b37779c389 (1).ziphas SHA256<hash redacted>, matching the existing workspace artifact infiles/; no duplicate archive was needed. - 2026-06-11: External operator access uses
<TARGET>:30182, but URLs submitted to the teacher bot must use the container-internal hostnames and port, e.g.http://edulearn.htb:1337/orhttp://sso.edulearn.htb:1337/.... Live check: internal:1337report returned success; external:30182report returned a server-side failure. - Latest effective gate remains blocked: do not run live remote exploit attempts until a new source-backed executable primitive or same-origin readable sink is found and a fresh
Proceedevaluator is recorded.
RAG / Advisory Memory
RAG output is advisory only. Record evaluated retrievals with:
scripts/challenge_harness.py rag-record <workspace> --query "..." --tag MATCHED|PARTIAL|MISSING|<secret redacted>|GENERIC --validation "..."Secrets/Flags
Raw flags and sensitive material stay in loot/ only. Use scripts/challenge_harness.py capture-flag to validate and record flag capture without printing the value.
Memory Summary
Metadata
- Platform: HackTheBox Challenges
- Category: Web
- Challenge: SSOS
- Difficulty: Medium
- Source workspace:
<local workspace>
Validated Solve Chain
Concepts only. Do not include raw flags, reusable credentials, tokens, cookies, private keys, or live secrets.
1.
Reusable Lessons
-
Dead Ends
-
Tool Quirks
-
Evidence Paths
-
Ingestion Decision
- Proposed for LightRAG: yes/no
- Requires user approval before ingestion: yes
Hypothesis Board
Keep no more than 3 active hypotheses on Easy/Medium and 5 on Hard unless the user explicitly asks for breadth.
| Rank | Path | Evidence | Missing Proof | Cheapest Validation | Confidence | Status |
|---|---|---|---|---|---|---|
| 1 | OAuth path-prefix remains the likely first half; next work must find the client-side/same-origin exfil primitive that turns a teacher code landed on EduLearn into attacker-readable state. | analysis/live-sanitizer-probe.json; analysis/collector/requests.jsonl; analysis/research/sidecar-technical-research-20260611T025452Z.md; source route pass over client/server.js | A concrete same-origin code sink, executable XSS primitive, or direct authorization bypass that lets attacker redeem teacher OAuth code or read the default student's flag submission. | Build local instrumented Chrome harness first; test same-origin Referer/location storage and hidden/alternate route candidates before any more live probing. | Medium | Active |
| 2 | Public hints point to an XSS/OAuth/teacher-bot chain, but the exact executing primitive remains unidentified. | analysis/research/public-hint-research-20260611.md; LinkedIn solver tags ClientSide/XSS/OAuth; Reddit/LinkedIn comments mention URL exfil difficulty; source confirms OAuth redirect and teacher bot. | A payload or client-side sink that actually executes in Chrome under the challenge sanitizer and can read or exfiltrate the teacher OAuth code/current URL. | Run the challenge locally with an instrumented bot/browser and test only source-backed DOM/sanitizer/client-side candidates until one produces real JavaScript execution. | Medium | active |
Closed Branches
| Branch | Evidence Tested | Failure Output | Reason Closed | Revisit Condition |
|---|---|---|---|---|
| Simple img-only code exfil through cross-origin Referer | Local review: Chrome default referrer policy is expected to send only origin cross-origin; source has no referrerpolicy override; sanitize-html allowlist strips referrerpolicy attributes. | analysis/source-audit.md | An external img beacon alone is not reliable enough to leak an OAuth code from the assignment URL; it can validate bot reachability but not full URL exfil. | Only revisit if live beacon proves full URL Referer leakage or a same-origin/attacker-observable exfil route is found. |
| OAuth code exfil via img Referer from teacher-readable submission | Web/SSOS/analysis/collector/requests.jsonl and Web/SSOS/analysis/referrer-probe-run.json | Collector received image beacons, but Referer was only http://edulearn.htb:1337/ even when the bot visited /submission/<id>?code=<secret redacted>. | Chrome 130 default strict-origin-when-cross-origin strips path/query on cross-origin image requests, so it does not leak the OAuth code. | Only revisit if a same-origin open redirect with unsafe referrer policy or an XSS primitive is proven. |
| sanitize-html stored XSS in student submission | Web/SSOS/analysis/live-sanitizer-probe.json | Remote rendered test payloads stripped scripts/events/raw-text wrappers; no script or onerror survived. | Live sanitizer behavior matches patched sanitize-html behavior; xmp/noembed/plaintext payloads did not produce executable HTML. | Only revisit with a specific payload that survives remote rendering and executes in Chrome. |
| Cross-site POST CSRF from data: page into EduLearn | Web/SSOS/analysis/csrf-score-probe.json | Bot visited a data: page with an auto-submitting POST to /api/score/<own submission>; score remained the normal auto-score, not the CSRF value. | Teacher session cookies were not sent for the cross-site POST in the tested browser context, and available POST actions do not read flag content anyway. | Only revisit if a fresh-cookie Lax+POST chain is paired with a useful teacher-only state change. |
| Default teacher/student fallback credentials | live login attempts via SSO /api/login | <email redacted>:teacher123 and <email redacted>:student123 both returned 401. | Runtime env password generation is active on the remote; fallback credentials are not valid. | Do not revisit unless service is respawned with evidence env password generation failed. |
| Score field type-confusion/client-side sink | analysis/score-sink-probe.json | Score accepts strings/arrays/objects, but assignment rendering escapes HTML and user stats normalize unsafe averageScore values to null. | No executable HTML or flag-content leak is created by score mutation. | Only revisit if a new route renders score with unescaped output or a controllable object-to-string primitive is found. |
| Go/Chrome URL parser differential to external host | analysis/url-parser-differentials.txt; analysis/url-parser-differentials-wide.txt | 122 candidate encoded slash, backslash, userinfo, tab, fragment, and delimiter variants produced no case where Go validates edulearn.htb:1337 while browser-style parsing goes external. | No redirect_uri parser split was found to deliver the teacher code directly to attacker infrastructure. | Only revisit with a specific parser differential candidate tested against Go net/url and Chrome. |
| Fresh-cookie top-level CSRF from bot data page | analysis/csrf-refresh-score-probe.json | Bot accepted the data URL, but score_after was the normal auto-score, not the CSRF marker 1337. | The attempted auth-refresh plus top-level POST did not carry a usable EduLearn session for the mutation. | Only revisit if paired with a proven same-site delivery mechanism and a state-changing route that reads or exposes flag content. |
| Extended sanitize-html raw-text/mXSS fuzz pass | analysis/sanitizer-fuzz-20260611.json; analysis/sanitize-html-version-config-check.txt | Known raw-text and mXSS candidates did not produce executable markup. Apparent hits were an event string inside img src and entity-escaped text. | No payload survived remote sanitization in an executable browser context. | Only revisit with a payload that renders as live DOM event/script in Chrome, not merely entity-decoded text in analysis. |
| OAuth code exfil via submission image referrer, including two-up path-prefix landing | analysis/local-validation/ssos-local-browser-validation.json; analysis/local-validation/runtime/logs/proxy-requests.jsonl | Submission image fired in teacher Chromium, but external canary Referer was only http://edulearn.htb:1337/ and window.__pwned stayed false. Two-up OAuth redirect lands code on /api/stats/<id>, but only as a same-origin request not readable by the attacker. | No attacker-readable code exfiltration or JavaScript execution was produced. | Only revisit if a separate same-origin storage/reflection sink or executable sanitizer bypass is proven. |
| Reaction innerHTML sink bypass via payload shape or itemId pollution | analysis/local-validation/ssos-candidate-validation.json | HTML string, array, and duplicate-urlencoded emoji inputs returned 400 Invalid emoji. __proto__/constructor item IDs did not create executable reaction HTML; marker stayed false and reactionHtml was empty. | Server allowlist and Object.entries rendering prevent attacker-controlled executable reaction markup. | Only revisit with a concrete bypass that makes attacker-controlled reaction key/value render into the teacher page DOM. |
| OAuth fragment placement as code sink | analysis/local-validation/ssos-candidate-validation.json | Code can be placed after a fragment marker in location.hash, but EduLearn source/scripts do not read location.hash; pageReadsHash=false. | Fragment placement changes where the code lands but does not make it attacker-readable or executable. | Only revisit if a same-origin hash reader/sink is found. |
| SSO login redirect javascript sink for teacher bot | analysis/local-validation/ssos-candidate-validation.json | Logged-out user submitting known credentials executes javascript redirect marker, but logged-in teacher visiting /login?redirect=javascript:... sees the login form and marker remains false. | The bot cannot be made to submit unknown teacher credentials, and the logged-in teacher state does not auto-trigger the redirect. | Only revisit if a source-backed way exists to force an SSO login form submission with valid teacher credentials or to auto-trigger redirect while authenticated. |
| Plain-object map pollution via __proto__ profile/user keys | analysis/local-validation/ssos-candidate-validation.json | __proto__ registration/login produced a callback but client profile access fell back to /login; teacher marker remained false and Object.prototype keys stayed empty. | The tested pollution path does not cross into teacher-rendered executable output or authenticated profile state. | Only revisit with a proven pollution write that changes an own rendered property in a teacher-visible sink. |
| New-instance sanitizer retry on <TARGET>:30182 | analysis/live-sanitizer-probe-new-instance-20260611.json | The refreshed target is reachable, but stored HTML payloads still strip scripts, event handlers, and raw-text wrappers; no executable browser primitive was recovered. | Only revisit with a specific payload that survives sanitize-html and executes in Chrome. | |
| New-instance score sink retry on <TARGET>:30182 | analysis/score-sink-probe-new-instance-20260611.json | Score mutation accepts unsafe-shaped values, but assignment rendering escapes HTML and stats normalize non-numeric values; no executable or flag-reading sink appears. | Only revisit if a route renders score unescaped in a teacher-visible context. | |
| New-instance bot CSRF retry on <TARGET>:30182 | analysis/csrf-refresh-score-probe-new-instance-20260611.json | Bot accepts the submitted URL, but the attempted auth-refresh plus top-level POST does not produce a useful teacher-side mutation; observed score remained normal auto-score. | Only revisit if paired with a same-site delivery mechanism and a useful teacher-only state-changing/read path. | |
| Bot URL submitted with external operator port | analysis/bot-port-external-30182-20260611.json | Teacher bot runs inside the challenge container and expects internal port 1337; submitting edulearn.htb:30182 to the bot produced a server-side failure. | Use :1337 for any future reported bot URL; use :30182 only for operator-side access and code redemption. |
Memory Summary
approval_required: true
Sanitized Memory Summary
Metadata
- Platform: HackTheBox Challenges
- Category: Web
- Challenge: SSOS
- Difficulty: Medium
- Source workspace:
<local workspace>
Validated Solve Chain
Concepts only. Do not include raw flags, reusable credentials, tokens, cookies, private keys, or live secrets.
1.
Reusable Lessons
-
Dead Ends
-
Tool Quirks
-
Evidence Paths
-
Ingestion Decision
- Proposed for LightRAG: yes/no
- Requires user approval before ingestion: yes
Notes
Notes
Scope
- Challenge: SSOS
- Category: Web
- Difficulty: Medium
- Mode: hybrid
- Remote instance: <TARGET>:30182
- Start time: 2026-06-11T00:12:14Z
- Operator: harness
- State file:
challenge-state.json
Harness Status
- Current phase: see
challenge-state.json - Next allowed actions: see
next-action.json - Raw flags and sensitive material stay in
loot/only. Do not paste them here.
Artifact Inventory
| File | Size | SHA256 | Type | Notes |
|---|---|---|---|---|
files/a12c7371-e2ee-4e9b-a80b-94b37779c389.zip | 66992 | <hash redacted> | Zip archive data, at least v1.0 to extract, compression method=store | zip entries: 70 shown in artifact inventory JSON |
Evidence Ledger
| Time | Action | Output/File | Finding | Confidence | Next |
|---|---|---|---|---|---|
| 2026-06-11T00:12:14Z | harness init | challenge-state.json | Workspace initialized with deterministic state file | High | Inventory artifacts |
| 2026-06-11T00:12:29Z | artifact inventory | analysis/artifact-inventory.json | 1 artifact(s) inventoried | High | Build or update hypotheses |
| 2026-06-11T00: <REDACTED>, then redeem the code via the normal client callback to become teacher and read the flag submission. | High | Register as student, create a submission with external image beacon, report a crafted authorize URL with redirect_uri= <REDACTED>, confirm beacon receives code in Referer, then redeem via /oauth/callback. | |||
| 2026-06-11T00:14:49Z | checkpoint recorded | analysis/checkpoint-analysis-20260611T001449626905Z-02153150.md | Checkpoint for ANALYSIS | High | Use checkpoint to drive next decision |
| 2026-06-11T00:14:56Z | source audit | analysis/source-audit.md | Source audit recorded | High | Gate before exploit |
| 2026-06-11T00:15:07Z | research task | analysis/research/task-20260611T001507445894Z-96eec801.md | Research task created for advisory investigation | Medium | Record research output |
| 2026-06-11T00:15:08Z | local memory search | analysis/research/local-memory-search-20260611T001508380599Z-34979848.md | Found 8 safe prior-note result(s) | Medium | Record useful result or skip |
| 2026-06-11T00:16:02Z | evaluator | analysis/evaluator-20260611T001602582811Z-10d20595.md | Validate first | High | run_live_preflight_then_marker_beacon_validation |
| 2026-06-11T00:16:41Z | research record | analysis/research/research-records.md | Research tagged GENERIC | Medium | Validate against current evidence |
| 2026-06-11T00:23:41Z | branch closed | hypothesis-board.md | An external img beacon alone is not reliable enough to leak an OAuth code from the assignment URL; it can validate bot reachability but not full URL exfil. | High | Rerank hypotheses |
| 2026-06-11T00:59:03Z | local memory record | analysis/local-memory-records.md | Prior local notes reviewed as fallback/advisory context | Medium | Validate against current evidence |
| 2026-06-11T00:59:03Z | evaluator | analysis/evaluator-20260611T005903373701Z-830cf0cf.md | Proceed | High | Write a reproducible solver/probe harness, validate remote sanitizer behavior, then run the OAuth/bot chain only if the primitive is live. |
| 2026-06-11T01:11:15Z | RAG query | analysis/rag/rag-query-20260611T011106000117Z-860a439b.txt | RAG helper exited 0; output saved | Medium | Record retrieval tag and validation |
| 2026-06-11T01:29:21Z | branch closed | hypothesis-board.md | Chrome 130 default strict-origin-when-cross-origin strips path/query on cross-origin image requests, so it does not leak the OAuth code. | High | Rerank hypotheses |
| 2026-06-11T01:29:21Z | branch closed | hypothesis-board.md | Live sanitizer behavior matches patched sanitize-html behavior; xmp/noembed/plaintext payloads did not produce executable HTML. | High | Rerank hypotheses |
| 2026-06-11T01: <REDACTED>, and available POST actions do not read flag content anyway. | High | Rerank hypotheses | |||
| 2026-06-11T01: <REDACTED> | |||||
| 2026-06-11T01:29:21Z | checkpoint recorded | analysis/checkpoint-analysis-20260611T012921989556Z-1eec5047.md | Checkpoint for ANALYSIS | High | Use checkpoint to drive next decision |
| 2026-06-11T01:29:32Z | RAG record | analysis/rag-records.md | Retrieved memory tagged MISSING | Medium | Validate or reject with live evidence |
| 2026-06-11T01:30:50Z | hypothesis recorded | hypothesis-board.md | No current proven exploit path after closing OAuth/referrer and sanitizer branches; next work must identify a new same-origin code sink, actual XSS primitive, or direct authorization bypass. | Medium | Audit only one new candidate at a time against source, then validate live with a disposable student and collector if source-backed. |
| 2026-06-11T01:30:50Z | checkpoint recorded | analysis/checkpoint-analysis-20260611T013050377847Z-09407c20.md | Checkpoint for ANALYSIS | High | Use checkpoint to drive next decision |
| 2026-06-11T02: <REDACTED> | |||||
| 2026-06-11T02:26:47Z | branch closed | hypothesis-board.md | No redirect_uri parser split was found to deliver the teacher code directly to attacker infrastructure. | High | Rerank hypotheses |
| 2026-06-11T02:26:47Z | branch closed | hypothesis-board.md | The attempted auth-refresh plus top-level POST did not carry a usable EduLearn session for the mutation. | High | Rerank hypotheses |
| 2026-06-11T02:26:47Z | branch closed | hypothesis-board.md | No payload survived remote sanitization in an executable browser context. | High | Rerank hypotheses |
| 2026-06-11T02:27:04Z | evaluator | analysis/evaluator-20260611T022704174413Z-e995f7b7.md | Do not proceed | High | Stop repeating OAuth/referrer/sanitizer variants. Search for a specific executable primitive or alternate authorization bug. |
| 2026-06-11T02:27:04Z | checkpoint recorded | analysis/checkpoint-analysis-20260611T022704286293Z-c00bbbd1.md | Checkpoint for ANALYSIS | High | Use checkpoint to drive next decision |
| 2026-06-11T02:55:40Z | research record | analysis/research/research-records.md | Research tagged PARTIAL | Medium | Validate against current evidence |
| 2026-06-11T02:55:50Z | hypothesis recorded | hypothesis-board.md | Public hints point to an XSS/OAuth/teacher-bot chain, but the exact executing primitive remains unidentified. | Medium | Run the challenge locally with an instrumented bot/browser and test only source-backed DOM/sanitizer/client-side candidates until one produces real JavaScript execution. |
| 2026-06-11T02:56:08Z | checkpoint recorded | analysis/checkpoint-analysis-20260611T025608671791Z-7bc98c5b.md | Checkpoint for ANALYSIS | High | Use checkpoint to drive next decision |
| 2026-06-11T02:56:10Z | research record | analysis/research/research-records.md | Research tagged PARTIAL | Medium | Validate against current evidence |
| 2026-06-11T02:56:10Z | checkpoint recorded | analysis/checkpoint-analysis-20260611T025610628024Z-057ff9d8.md | Checkpoint for ANALYSIS | High | Use checkpoint to drive next decision |
| 2026-06-11T02:57:00Z | checkpoint recorded | analysis/checkpoint-analysis-20260611T025700708157Z-826e4802.md | Checkpoint for ANALYSIS | High | Use checkpoint to drive next decision |
| 2026-06-11T02:57:22Z | research record | analysis/research/research-records.md | Research tagged PARTIAL | Medium | Validate against current evidence |
| 2026-06-11T02:57:22Z | research record | analysis/research/research-records.md | Research tagged PARTIAL | Medium | Validate against current evidence |
| 2026-06-11T03:33:14Z | instrumentation plan | analysis/instrumentation-plan.md | Find a source-backed Chrome-executing client-side primitive or same-origin OAuth-code storage sink before any further live exploit attempt. | High | Stop immediately if local Chrome does not execute the primitive, if no same-origin storage/reflection sink exists, or if the remote behavior differs from the local proof. Do not repeat closed OAuth referrer, sanitizer, score sink, CSRF, or parser-differential branches without new evidence. |
| 2026-06-11T03:58:55Z | branch closed | hypothesis-board.md | No attacker-readable code exfiltration or JavaScript execution was produced. | High | Rerank hypotheses |
| 2026-06-11T03:58:55Z | checkpoint recorded | analysis/checkpoint-analysis-20260611T035855380968Z-dfabc1e0.md | Checkpoint for ANALYSIS | High | Use checkpoint to drive next decision |
| 2026-06-11T03:58:55Z | evaluator | analysis/evaluator-20260611T035855432554Z-93b1f49f.md | Do not proceed | High | Find a new executable client-side primitive or same-origin storage/reflection route; do not run live exploit attempts until that exists. |
| 2026-06-11T04:00:13Z | checkpoint recorded | analysis/checkpoint-analysis-20260611T040013240997Z-be632fbf.md | Checkpoint for ANALYSIS | High | Use checkpoint to drive next decision |
| 2026-06-11T04:57:09Z | branch closed | hypothesis-board.md | Server allowlist and Object.entries rendering prevent attacker-controlled executable reaction markup. | High | Rerank hypotheses |
| 2026-06-11T04:57:09Z | branch closed | hypothesis-board.md | Fragment placement changes where the code lands but does not make it attacker-readable or executable. | High | Rerank hypotheses |
| 2026-06-11T04:57:09Z | branch closed | hypothesis-board.md | The bot cannot be made to submit unknown teacher credentials, and the logged-in teacher state does not auto-trigger the redirect. | High | Rerank hypotheses |
| 2026-06-11T04:57:09Z | branch closed | hypothesis-board.md | The tested pollution path does not cross into teacher-rendered executable output or authenticated profile state. | High | Rerank hypotheses |
| 2026-06-11T04:57:09Z | checkpoint recorded | analysis/checkpoint-analysis-20260611T045709716574Z-caca2dc3.md | Checkpoint for ANALYSIS | High | Use checkpoint to drive next decision |
| 2026-06-11T04:57:09Z | evaluator | analysis/evaluator-20260611T045709769205Z-f1065996.md | Do not proceed | High | Stop repeating current candidates. Continue only if new source-backed sink or specific public hint identifies a Chrome-executing primitive. |
| 2026-06-11T05:44:15Z | checkpoint recorded | analysis/checkpoint-analysis-20260611T054415977022Z-966081d9.md | Checkpoint for ANALYSIS | High | Use checkpoint to drive next decision |
| 2026-06-11T05:54:54Z | evaluator | analysis/evaluator-20260611T055454315842Z-a9ca284c.md | Proceed | High | Run bounded revalidation scripts only, then record a fresh evaluator. |
| 2026-06-11T05:57:14Z | branch closed | hypothesis-board.md | The refreshed target is reachable, but stored HTML payloads still strip scripts, event handlers, and raw-text wrappers; no executable browser primitive was recovered. | High | Rerank hypotheses |
| 2026-06-11T05: <REDACTED>, but assignment rendering escapes HTML and stats normalize non-numeric values; no executable or flag-reading sink appears. | High | Rerank hypotheses | |||
| 2026-06-11T05:57:14Z | branch closed | hypothesis-board.md | Bot accepts the submitted URL, but the attempted auth-refresh plus top-level POST does not produce a useful teacher-side mutation; observed score remained normal auto-score. | High | Rerank hypotheses |
| 2026-06-11T05:57:24Z | checkpoint recorded | analysis/checkpoint-analysis-20260611T055724490561Z-b47bb46d.md | Checkpoint for ANALYSIS | High | Use checkpoint to drive next decision |
| 2026-06-11T05:57:32Z | evaluator | analysis/evaluator-20260611T055732934169Z-2c022405.md | Do not proceed | High | Stop live retries; find a new source-backed primitive or trusted specific hint. |
| 2026-06-11T05:59:53Z | evaluator | analysis/evaluator-20260611T055953493988Z-e326e2c1.md | Proceed | High | Run harmless bot reachability port check, then restore Do not proceed unless a new primitive is identified. |
| 2026-06-11T06:02:06Z | checkpoint recorded | analysis/checkpoint-analysis-20260611T060206889798Z-711e2fee.md | Checkpoint for ANALYSIS | High | Use checkpoint to drive next decision |
| 2026-06-11T06:02:06Z | branch closed | hypothesis-board.md | Teacher bot runs inside the challenge container and expects internal port 1337; submitting edulearn.htb:30182 to the bot produced a server-side failure. | High | Rerank hypotheses |
| 2026-06-11T06:02:19Z | evaluator | analysis/evaluator-20260611T060219351249Z-9098f4ef.md | Do not proceed | High | Do not repeat live probes; build or identify one new primitive using the corrected bot port model. |
Key Findings
- 2026-06-11: New operator-supplied remote instance is
<TARGET>:30182. - 2026-06-11: Operator-supplied Downloads artifact
a12c7371-e2ee-4e9b-a80b-94b37779c389 (1).ziphas SHA256<hash redacted>, matching the existing workspace artifact infiles/; no duplicate archive was needed. - 2026-06-11: External operator access uses
<TARGET>:30182, but URLs submitted to the teacher bot must use the container-internal hostnames and port, e.g.http://edulearn.htb:1337/orhttp://sso.edulearn.htb:1337/.... Live check: internal:1337report returned success; external:30182report returned a server-side failure. - Latest effective gate remains blocked: do not run live remote exploit attempts until a new source-backed executable primitive or same-origin readable sink is found and a fresh
Proceedevaluator is recorded.
RAG / Advisory Memory
RAG output is advisory only. Record evaluated retrievals with:
scripts/challenge_harness.py rag-record <workspace> --query "..." --tag MATCHED|PARTIAL|MISSING|<secret redacted>|GENERIC --validation "..."Secrets/Flags
Raw flags and sensitive material stay in loot/ only. Use scripts/challenge_harness.py capture-flag to validate and record flag capture without printing the value.
Technical analogy
How to remember this solve
Think of the web app like a building with signs on every door. The solve usually comes from reading the map carefully, finding the door the app forgot to hide, then sending the exact request that proves you understand the route.
For SSOS, keep the mental model simple: identify the trusted assumption, prove it with the smallest safe test, then automate or repeat only the part that directly leads to the flag.