Challenge / Reversing

FFModule

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

MediumPublished 2025-07-07Sanitized local writeup

Scenario

FFModule attack path

FFModule 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.

FFModule sanitized attack graph

Walkthrough flow

01

Inventory the archive and identify a PE32+ x86-64...

02

Use imports and strings to identify a Firefox...

03

Reverse the loader path: it searches for firefox.exe,...

04

Extract the decrypted payload statically instead of...

05

Disassemble the payload and find the PR_Write hook...

Source coverage

High source coverage

Status: complete. This article is generated from 4 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/FFModule/writeup.md
  • htb-challenge/Reversing/FFModule/notes.md
  • htb-challenge/Reversing/FFModule/memory-summary.md
  • htb-challenge/Reversing/FFModule/hypothesis-board.md

Technical Walkthrough

Writeup

Challenge

  • Name: FFModule
  • Category: Reversing
  • Difficulty: Medium
  • Mode: file

Summary

FFModule is a Windows x64 Firefox malware module. The executable does not store

the flag as plaintext. Instead, it decrypts an embedded second-stage payload,

injects it into firefox.exe, and that payload contains a smaller encrypted

buffer.

The solve is fully static: extract the injected payload, locate the inline

32-byte encrypted buffer, and reproduce the payload's byte transform.

Artifact Inventory

  • files/a12c737e-e12c-465a-9797-a25458b787e9.zip: original HTB archive.
  • files/extracted/rev_ffmodule/ffmodule.exe: PE32+ x86-64 Windows executable.

Analysis

Baseline imports showed a process injection pattern:

  • CreateToolhelp32Snapshot, Process32First, Process32Next
  • OpenProcess
  • VirtualAllocEx
  • WriteProcessMemory
  • VirtualProtectEx
  • CreateRemoteThread

The only useful plaintext strings were firefox.exe and the Firefox hook

banner. Disassembly around the string references showed that the program

searches for Firefox, decrypts 0x5a4 bytes from .data with XOR 0x72, and

injects the decrypted bytes.

The extracted second-stage payload contains its own resolver and hooks Firefox

PR_Write. It checks whether the outbound buffer starts with POST. Near the

network/send logic, the payload has a call +0x20 pattern; the 32 bytes after

that call are an encrypted inline buffer. The following decoder subtracts

0x13, rotates left by 3, then XORs with 0x42.

Public research was used only as a community hint and was validated against the

current artifact before use:

  • analysis/research/public-ffmodule-writeup-note.md

Local evidence:

  • analysis/objdump-p.txt
  • analysis/objdump-d.txt
  • analysis/payload-xor72.bin
  • analysis/payload-capstone.txt
  • analysis/payload-strings.txt

Solve

Run:

bash
python3 solve/solve.py --output loot/flag-candidate.txt
cd <local workspace>
python3 scripts/challenge_harness.py capture-flag Reversing/FFModule --from loot/flag-candidate.txt

The solver parses the PE section table, extracts .data, applies the loader's

XOR 0x72 stage, locates the inline 32-byte encrypted buffer, applies the

payload decoder, and writes the recovered flag candidate.

Flag

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

Lessons

  • Malware-style reversing challenges often hide the real challenge logic in an

injected stage rather than the initial executable.

  • Sparse strings are still enough to anchor analysis when paired with import

patterns and xrefs.

  • Do not execute the sample unless necessary; this solve was completed with

static extraction and a small decoder.

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: FFModule
  • Category: Reversing
  • Difficulty: Medium
  • Mode: file
  • Remote instance: none
  • Start time: 2026-06-13T02:05:12Z
  • 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/a12c737e-e12c-465a-9797-a25458b787e9.zip50517<hash redacted>Zip archive data, at least v1.0 to extract, compression method=storezip entries: 2 shown in artifact inventory JSON
files/extracted/rev_ffmodule/ffmodule.exe98304<hash redacted>PE32+ executable (console) x86-64, for MS Windows

Evidence Ledger

TimeActionOutput/FileFindingConfidenceNext
2026-06-13T02:05:12Zharness initchallenge-state.jsonWorkspace initialized with deterministic state fileHighInventory artifacts
2026-06-13T02:05:32Zartifact inventoryanalysis/artifact-inventory.json2 artifact(s) inventoriedHighBuild or update hypotheses
2026-06-13T02:05:46Zhypothesis recordedhypothesis-board.mdReverse ffmodule.exe as a Firefox credential-stealing module; identify how it locates/decrypts embedded or simulated Firefox secrets and recover the HTB flag from local artifacts.MediumRun file/strings/import inspection, identify PE/.NET/native packing, then build a local decoder or emulator for the secret-recovery logic.
2026-06-13T02:05:46Zcheckpoint recordedanalysis/checkpoint-triage-20260613T020546535260Z-4ba29e66.mdCheckpoint for TRIAGEHighUse checkpoint to drive next decision
2026-06-13T02:06:25ZRAG queryanalysis/rag/rag-query-20260613T020606294456Z-272ac0de.txtRAG helper exited 0; output savedMediumRecord retrieval tag and validation
2026-06-13T02:08:37Zresearch recordanalysis/research/research-records.mdResearch tagged GENERICMediumValidate against current evidence
2026-06-13T02:08:37Zinstrumentation plananalysis/instrumentation-plan.mdRecover the secret from the injected Firefox module payload without executing untrusted malware.HighStop if static extraction does not yield a payload-level decoder path; do not execute the PE or shellcode on the host.
2026-06-13T02:11:53Zevaluatoranalysis/evaluator-20260613T021153899282Z-56b0cfcd.mdProceedHighcapture-flag from loot/flag-candidate.txt
2026-06-13T02:12:17Zresearch recordanalysis/research/research-records.mdResearch tagged MATCHEDMediumValidate against current evidence
2026-06-13T02:12:26Zflag captureloot/flag.txtHTB-format flag captured; raw value kept in loot onlyHighWrite solution and run completion gate
2026-06-13T02:13:36Zcompletion gatechallenge-state.jsonCompletion gate passed; state marked COMPLETEHighOptional sanitized memory summary approval

Key Findings

  • ffmodule.exe is a PE32+ x86-64 Windows console executable.
  • Imports include process-injection APIs: OpenProcess, VirtualAllocEx,

WriteProcessMemory, VirtualProtectEx, and CreateRemoteThread.

  • The module searches for firefox.exe and prints/logs the Firefox hook banner.
  • The loader decrypts the first 0x5a4 bytes of .data with XOR 0x72, then

injects that second-stage payload into Firefox.

  • The second-stage payload hooks Firefox PR_Write; it checks outbound buffers

for POST and contains an inline 32-byte encrypted buffer.

  • The inline buffer decodes with the payload's byte transform:

rotate-left by 3 after subtracting 0x13, then XOR with 0x42.

  • solve/solve.py recovers the flag from the current executable without

executing the PE or payload.

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: FFModule
  • 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. Inventory the archive and identify a PE32+ x86-64 Windows executable.
  2. Use imports and strings to identify a Firefox process-injection loader.
  3. Reverse the loader path: it searches for firefox.exe, XOR-decrypts the

first 0x5a4 bytes of .data with 0x72, and injects that payload.

  1. Extract the decrypted payload statically instead of executing the sample.
  2. Disassemble the payload and find the PR_Write hook plus POST check.
  3. Locate the inline 32-byte encrypted buffer and apply the payload decoder:

subtract 0x13, rotate left by 3, then XOR with 0x42.

  1. Validate the recovered candidate through the challenge harness.

Reusable Lessons

  • For malware-module reversing, inspect the first stage for loader/decryptor

behavior before assuming the flag is in the main executable.

  • Process-injection imports plus a browser process string are strong indicators

that the meaningful code lives in an injected payload.

  • A community writeup can be used as a hint, but only after matching the exact

constants, offsets, and decoder behavior in the current artifact.

Dead Ends

  • Plain strings on the main PE did not reveal the flag.
  • RAG memory returned generic Firefox malware guidance only.
  • Raw objdump on macOS could not disassemble a flat binary payload; Capstone

was installed locally under the workspace for payload disassembly.

Tool Quirks

  • The Apple/Xcode objdump did not support GNU-style raw binary disassembly

arguments, so capstone was installed into .deps/.

  • The raw sample was not executed; the solve uses static PE parsing and payload

extraction only.

Evidence Paths

  • analysis/objdump-p.txt
  • analysis/objdump-d.txt
  • analysis/payload-xor72.bin
  • analysis/payload-capstone.txt
  • analysis/research/public-ffmodule-writeup-note.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
1Reverse ffmodule.exe as a Firefox credential-stealing module; identify how it locates/decrypts embedded or simulated Firefox secrets and recover the HTB flag from local artifacts.Archive contains only ffmodule.exe and the scenario explicitly says a Jupiter banking-malware module steals Firefox user secrets.Run file/strings/import inspection, identify PE/.NET/native packing, then build a local decoder or emulator for the secret-recovery logic.MediumActive

Closed Branches

BranchEvidence TestedFailure OutputReason ClosedRevisit Condition

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 FFModule, 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.