Challenge / Reversing

RAuth

RAuth is a sanitized challenge note from the local HTB archive, organized for quick review by category, difficulty, evidence flow, and reusable operator

EasyPublished 2025-07-18Sanitized local writeup

Scenario

RAuth attack path

RAuth 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 Reversing evidence, validation, and reusable operator lessons.

RAuth sanitized attack graph

Walkthrough flow

01

Extract the HTB ZIP with the standard challenge...

02

Identify the extracted artifact as an unstripped...

03

Use strings and symbol output to locate the login...

04

Disassemble rauth::main and trace the input...

05

Observe that the submitted auth phrase is copied into...

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.

100% coverage
Evidence verdict

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.

  • Reversing/RAuth/writeup.md
  • htb-challenge/Reversing/RAuth/notes.md
  • htb-challenge/Reversing/RAuth/memory-summary.md
  • htb-challenge/Reversing/RAuth/hypothesis-board.md
  • HTB/_knowledge/exports/ctf-lightrag-latest-203412/documents/challenge__Reversing__RAuth__memory-summary.md.a6806545a1.md
  • HTB/_knowledge/exports/ctf-lightrag-latest-203412/documents/challenge__Reversing__RAuth__notes.md.c808cd24ea.md

Technical Walkthrough

Writeup

Challenge

  • Name: RAuth
  • Category: Reversing
  • Difficulty: Easy
  • Mode: hybrid

Summary

The provided artifact is an unstripped Rust ELF with debug information. Static disassembly of rauth::main showed that user input is encrypted with Salsa20 using fixed constants and compared against a 32-byte ciphertext in .rodata. Decrypting that comparison constant recovers the required auth phrase, which the solver submits to the remote instance to retrieve the flag.

Artifact Inventory

Reference analysis/artifact-inventory.json and summarize the relevant files or remote surface.

  • files/a12c7368-9335-4856-9dc7-e230b2f27c1f.zip: HTB ZIP archive.
  • analysis/extracted/challenge/rauth: 64-bit Linux PIE ELF, dynamically linked, unstripped, with debug info.
  • analysis/main-disasm.txt: disassembly of _ZN5rauth4main....
  • analysis/rodata-dump.txt: .rodata constants used by the validation routine.
  • solve/solve.py: reproducible solver and remote submitter.

Analysis

  1. strings identifies Rust prompts for a secure login portal and success/failure messages.
  2. nm identifies _ZN5rauth4main17h... at 0x6460.
  3. The main routine loads a 32-byte ASCII Salsa20 key from .rodata.
  4. It loads an 8-byte ASCII nonce into the Salsa20 core.
  5. It copies a 32-byte binary comparison constant from .rodata.
  6. The submitted input is copied into a vector, Salsa20 keystream is applied to it, and the encrypted result must match the 32-byte comparison constant.
  7. Therefore, decrypting the constant with the same Salsa20 key/nonce gives the required auth phrase.
  8. The solver submits that phrase to <TARGET>:32098 and extracts the HTB-format flag from the response.

Solve

Run:

bash
python3 Reversing/RAuth/solve/solve.py --host <TARGET> --port 32098

The script reconstructs the Salsa20 constants from the binary analysis, decrypts the expected ciphertext to recover the auth phrase, submits it to the remote service, and prints only the HTB-format flag for harness capture.

Flag

Raw flag is stored in loot/flag.txt and intentionally not reproduced here.

Lessons

  • Rust binaries can still expose clear validation logic when debug info and symbol names remain.
  • Stream-cipher validation can be inverted when the key, nonce, and expected ciphertext are static.
  • For hybrid reversing challenges, solve locally first and keep remote interaction to a single validated submission.

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: RAuth
  • Category: Reversing
  • Difficulty: Easy
  • Mode: hybrid
  • Remote instance: none
  • Start time: 2026-06-10T10:38: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

FileSizeSHA256TypeNotes
files/a12c7368-9335-4856-9dc7-e230b2f27c1f.zip2255156<hash redacted>Zip archive data, at least v2.0 to extract, compression method=deflatezip entries: 1 shown in artifact inventory JSON

Evidence Ledger

TimeActionOutput/FileFindingConfidenceNext
2026-06-10T10:38:14Zharness initchallenge-state.jsonWorkspace initialized with deterministic state fileHighInventory artifacts
2026-06-10T10:38:14Zartifact inventoryanalysis/artifact-inventory.json1 artifact(s) inventoriedHighBuild or update hypotheses
2026-06-10T10:38:30Zhypothesis recordedhypothesis-board.mdStatic reverse the Rust ELF authentication path to recover the auth phrase, then submit it to the remote service at <TARGET>:32098 to obtain the flag.MediumRun file/strings/objdump on rauth, search for prompts, error strings, encoded constants, and Rust symbol/function remnants around auth phrase validation.
2026-06-10T10:38:46Zresearch skipanalysis/research/research-skip.mdResearch intentionally skipped with recorded reasonMediumGate before exploit
2026-06-10T10:39:34Zcheckpoint recordedanalysis/checkpoint-analysis-20260610T103934827433Z-7763463d.mdCheckpoint for ANALYSISHighUse checkpoint to drive next decision
2026-06-10T10:39:46Zsource auditanalysis/source-audit.mdSource audit recordedHighGate before exploit
2026-06-10T10:40:14Zlocal memory recordanalysis/local-memory-records.mdPrior local notes reviewed as fallback/advisory contextMediumValidate against current evidence
2026-06-10T10:40:14Zevaluatoranalysis/evaluator-20260610T104014275491Z-3b944ec5.mdProceedHighFollow evaluator decision
2026-06-10T10:40:43Zflag captureloot/flag.txtHTB-format flag captured; raw value kept in loot onlyHighWrite solution and run completion gate
2026-06-10T10:43:29Zcompletion gatechallenge-state.jsonCompletion gate passed; state marked COMPLETEHighOptional sanitized memory summary approval

Key Findings

  • The archive extracts to a single 64-bit Linux Rust ELF named rauth.
  • The binary is not stripped and keeps enough Rust symbol/debug context to locate rauth::main quickly.
  • Static analysis of rauth::main shows the entered auth phrase is encrypted with Salsa20, then compared with a fixed 32-byte .rodata constant.
  • The Salsa20 key, nonce, and expected ciphertext are all static in the binary, so the comparison can be inverted locally.
  • solve/solve.py decrypts the expected ciphertext to recover the required auth phrase and submits it once to <TARGET>:32098.
  • The remote service returned an HTB-format flag, captured by the harness into loot/flag.txt.

RAG / Advisory Memory

RAG output is advisory only. Record evaluated retrievals with:

bash
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: Reversing
  • Challenge: RAuth
  • Difficulty: Easy
  • Source workspace: <local workspace>

Validated Solve Chain

Concepts only. Do not include raw flags, reusable credentials, tokens, cookies, private keys, or live secrets.

  1. Extract the HTB ZIP with the standard challenge archive phrase.
  2. Identify the extracted artifact as an unstripped 64-bit Linux Rust ELF with debug information.
  3. Use strings and symbol output to locate the login prompts and rauth::main.
  4. Disassemble rauth::main and trace the input validation flow.
  5. Observe that the submitted auth phrase is copied into a buffer, encrypted with Salsa20, and compared to a fixed 32-byte ciphertext.
  6. Recover the static Salsa20 key, nonce, and comparison ciphertext from .rodata and nearby immediates.
  7. Decrypt the comparison ciphertext locally to recover the required auth phrase.
  8. Submit the recovered phrase to the remote TCP service and capture the returned HTB-format flag into loot/.

Reusable Lessons

  • Rust does not prevent static reversing when validation constants and debug symbols remain in the binary.
  • Stream-cipher checks are reversible when the key, nonce, and expected ciphertext are static and attacker-visible.
  • For remote reversing challenges, prefer proving the transform locally before touching the service.
  • Store raw flags and recovered phrases only in loot/; writeups and memory summaries should keep the method but omit secrets.

Dead Ends

  • No meaningful dead end. Basic static analysis was sufficient once the main routine was located.

Tool Quirks

  • readelf, gdb, radare2, rizin, ghidra, binwalk, and angr were unavailable locally, so the solve used file, strings, nm, objdump, hexdump tooling, and Python.
  • The challenge state lint can treat bare secret-like words conservatively; use neutral wording such as "auth phrase" in non-loot documentation.

Evidence Paths

  • analysis/extracted/challenge/rauth
  • analysis/main-disasm.txt
  • analysis/rodata-dump.txt
  • analysis/source-audit.md
  • solve/solve.py
  • loot/flag.txt

Ingestion Decision

  • Proposed for LightRAG: yes
  • 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.

RankPathEvidenceMissing ProofCheapest ValidationConfidenceStatus
1Static reverse the Rust ELF authentication path to recover the auth phrase, then submit it to the remote service at <TARGET>:32098 to obtain the flag.ZIP contains a single Rust-themed auth binary named rauth; challenge asks to retrieve the auth phrase from the Rust authentication implementation.Run file/strings/objdump on rauth, search for prompts, error strings, encoded constants, and Rust symbol/function remnants around auth phrase validation.MediumActive

Closed Branches

BranchEvidence TestedFailure OutputReason ClosedRevisit Condition

Memory Summary

approval_required: true

Sanitized Memory Summary

Metadata

  • Platform: HackTheBox Challenges
  • Category: Reversing
  • Challenge: RAuth
  • Difficulty: Easy
  • Source workspace: <local workspace>

Validated Solve Chain

Concepts only. Do not include raw flags, reusable credentials, tokens, cookies, private keys, or live secrets.

  1. Extract the HTB ZIP with the standard challenge archive phrase.
  2. Identify the extracted artifact as an unstripped 64-bit Linux Rust ELF with debug information.
  3. Use strings and symbol output to locate the login prompts and rauth::main.
  4. Disassemble rauth::main and trace the input validation flow.
  5. Observe that the submitted auth phrase is copied into a buffer, encrypted with Salsa20, and compared to a fixed 32-byte ciphertext.
  6. Recover the static Salsa20 key, nonce, and comparison ciphertext from .rodata and nearby immediates.
  7. Decrypt the comparison ciphertext locally to recover the required auth phrase.
  8. Submit the recovered phrase to the remote TCP service and capture the returned HTB-format flag into loot/.

Reusable Lessons

  • Rust does not prevent static reversing when validation constants and debug symbols remain in the binary.
  • Stream-cipher checks are reversible when the key, nonce, and expected ciphertext are static and attacker-visible.
  • For remote reversing challenges, prefer proving the transform locally before touching the service.
  • Store raw flags and recovered phrases only in loot/; writeups and memory summaries should keep the method but omit secrets.

Dead Ends

  • No meaningful dead end. Basic static analysis was sufficient once the main routine was located.

Tool Quirks

  • readelf, gdb, radare2, rizin, ghidra, binwalk, and angr were unavailable locally, so the solve used file, strings, nm, objdump, hexdump tooling, and Python.
  • The challenge state lint can treat bare secret-like words conservatively; use neutral wording such as "auth phrase" in non-loot documentation.

Evidence Paths

  • analysis/extracted/challenge/rauth
  • analysis/main-disasm.txt
  • analysis/rodata-dump.txt
  • analysis/source-audit.md
  • solve/solve.py
  • loot/flag.txt

Ingestion Decision

  • Proposed for LightRAG: yes
  • Requires user approval before ingestion: yes

Notes

Notes

Scope

  • Challenge: RAuth
  • Category: Reversing
  • Difficulty: Easy
  • Mode: hybrid
  • Remote instance: none
  • Start time: 2026-06-10T10:38: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

FileSizeSHA256TypeNotes
files/a12c7368-9335-4856-9dc7-e230b2f27c1f.zip2255156<hash redacted>Zip archive data, at least v2.0 to extract, compression method=deflatezip entries: 1 shown in artifact inventory JSON

Evidence Ledger

TimeActionOutput/FileFindingConfidenceNext
2026-06-10T10:38:14Zharness initchallenge-state.jsonWorkspace initialized with deterministic state fileHighInventory artifacts
2026-06-10T10:38:14Zartifact inventoryanalysis/artifact-inventory.json1 artifact(s) inventoriedHighBuild or update hypotheses
2026-06-10T10: <REDACTED>, then submit it to the remote service at <TARGET>: <REDACTED>, search for prompts, error strings, encoded constants, and Rust symbol/function remnants around auth phrase validation.
2026-06-10T10:38:46Zresearch skipanalysis/research/research-skip.mdResearch intentionally skipped with recorded reasonMediumGate before exploit
2026-06-10T10:39:34Zcheckpoint recordedanalysis/checkpoint-analysis-20260610T103934827433Z-7763463d.mdCheckpoint for ANALYSISHighUse checkpoint to drive next decision
2026-06-10T10:39:46Zsource auditanalysis/source-audit.mdSource audit recordedHighGate before exploit
2026-06-10T10:40:14Zlocal memory recordanalysis/local-memory-records.mdPrior local notes reviewed as fallback/advisory contextMediumValidate against current evidence
2026-06-10T10:40:14Zevaluatoranalysis/evaluator-20260610T104014275491Z-3b944ec5.mdProceedHighFollow evaluator decision
2026-06-10T10: <REDACTED>
2026-06-10T10:43:29Zcompletion gatechallenge-state.jsonCompletion gate passed; state marked COMPLETEHighOptional sanitized memory summary approval

Key Findings

  • The archive extracts to a single 64-bit Linux Rust ELF named rauth.
  • The binary is not stripped and keeps enough Rust symbol/debug context to locate rauth::main quickly.
  • Static analysis of rauth::main shows the entered auth phrase is encrypted with Salsa20, then compared with a fixed 32-byte .rodata constant.
  • The Salsa20 key, nonce, and expected ciphertext are all static in the binary, so the comparison can be inverted locally.
  • solve/solve.py decrypts the expected ciphertext to recover the required auth phrase and submits it once to <TARGET>:32098.
  • The remote service returned an HTB-format flag, captured by the harness into loot/flag.txt.

RAG / Advisory Memory

RAG output is advisory only. Record evaluated retrievals with:

bash
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 it like taking apart a small appliance on a workbench. You do not need every screw at once; you trace the control path and rebuild just enough logic to make it reveal the answer.

For RAuth, 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.