Challenge / Reversing

Simple Encryptor

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

Very EasyPublished 2025-08-07Sanitized local writeup

Scenario

Simple Encryptor attack path

Simple Encryptor 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.

Simple Encryptor sanitized attack graph

Walkthrough flow

01

Binary triage

02

Control-flow recovery

03

Key logic reconstruction

04

Proof captured

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/Simple-Encryptor/writeup.md
  • htb-challenge/Reversing/Simple-Encryptor/notes.md
  • htb-challenge/Reversing/Simple-Encryptor/memory-summary.md
  • htb-challenge/Reversing/Simple-Encryptor/hypothesis-board.md
  • HTB/_knowledge/exports/ctf-lightrag-latest-203412/documents/challenge__Reversing__Simple-Encryptor__memory-summary.md.2ccf0722e0.md
  • HTB/_knowledge/exports/ctf-lightrag-latest-203412/documents/challenge__Reversing__Simple-Encryptor__notes.md.1c08ff05fc.md

Technical Walkthrough

Writeup

Challenge

  • Name: Simple-Encryptor
  • Category: Reversing
  • Difficulty: Very Easy
  • Mode: file

Summary

The binary stores a 4-byte Unix time seed at the start of flag.enc, then uses glibc's rand() stream twice per plaintext byte: first to XOR the byte with rand() & 0xff, then to rotate the result left by rand() & 7. Replaying the same glibc PRNG sequence and reversing the operations (ROR then XOR) decrypts the flag deterministically.

The encrypted file starts with a 4-byte Unix timestamp seed. The encrypt binary uses that seed with libc srand(), then for each plaintext byte XORs with rand() & 0xff and rotates left by rand() & 7. Replaying the same PRNG and applying the inverse operations (ROR then XOR) decrypts the flag deterministically.

Artifact Inventory

  • files/a12c7354-6ee3-4584-912e-2f8b1c122ecf.zip — original encrypted archive.
  • analysis/<password redacted> — successful archive decryption validation.
  • analysis/extracted/rev_simpleencryptor/encrypt — ELF encryptor binary.
  • analysis/extracted/rev_simpleencryptor/flag.enc — encrypted flag blob.
  • analysis/objdump-disasm.txt — disassembly used to reconstruct the transform.
  • analysis/solve-result.txt — recovered flag before harness capture.
  • solve/solve.py — deterministic decryptor.
  • files/a12c7354-6ee3-4584-912e-2f8b1c122ecf.zip — original encrypted archive.
  • analysis/<password redacted> — successful archive decryption validation.
  • analysis/extracted/rev_simpleencryptor/encrypt — ELF encryptor binary.
  • analysis/extracted/rev_simpleencryptor/flag.enc — encrypted flag blob.
  • analysis/objdump-disasm.txt — disassembly used to reconstruct the transform.
  • analysis/solve-result.txt — recovered flag before harness capture.
  • solve/solve.py — deterministic decryptor.

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

Analysis

main() opens the input file, reads it into memory, calls time(NULL) and srand(seed), writes the 4-byte seed to the start of the output file, and then for each plaintext byte calls rand() twice: once to obtain the XOR byte and a second time to derive the rotate-left amount via & 7. The output byte is therefore ROL(plain ^ (rand1 & 0xff), rand2 & 7). Decryption must replay the exact glibc random() stream from the stored seed, then apply ROR(cipher, rand2 & 7) ^ (rand1 & 0xff).

main() opens flag, reads it fully, seeds srand(time(NULL)), and writes the seed as the first four bytes of flag.enc. It then iterates each plaintext byte, XORing it with rand() & 0xff, stores that intermediate byte, calls rand() again, masks the result with 7, and rotates the byte left by that count. To decrypt, read the seed prefix, replay the same libc PRNG sequence, rotate each ciphertext byte right by the second random value, and XOR with the first random value.

Solve

The solver reads the first four bytes of flag.enc as a little-endian Unix timestamp seed, reconstructs glibc's additive random() sequence, and for each ciphertext byte applies a right rotate by the second random value and then XORs with the first random value. The resulting plaintext is the HTB flag.

The solver reads the first four bytes of flag.enc as a little-endian Unix timestamp seed, initializes libc rand() with srand(seed), and for each encrypted byte computes plain = ROR(cipher, rand() & 7) XOR (previous rand() & 0xff). The resulting plaintext is the HTB flag.

Flag

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

Lessons

For Linux binaries, be careful not to assume the host platform's libc PRNG matches the target. The first attempt using macOS libc failed; the correct decryptor had to reproduce glibc's random() algorithm exactly before the byte-wise inverse transform produced the flag.

When an encryptor stores its PRNG seed alongside ciphertext, reversing often reduces to reconstructing the exact byte transform and replaying the same RNG. Pay close attention to operation order—here it was XOR first, then rotate-left—because the inverse must unwind that sequence exactly.

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: Simple-Encryptor
  • Category: Reversing
  • Difficulty: Very Easy
  • Mode: file
  • Remote instance: none
  • Start time: 2026-06-07T09:30:02Z
  • 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/a12c7354-6ee3-4584-912e-2f8b1c122ecf.zip3723<hash redacted>Zip archive data, at least v1.0 to extract, compression method=storezip entries: 3 shown in artifact inventory JSON

Evidence Ledger

TimeActionOutput/FileFindingConfidenceNext
2026-06-07T09:30:02Zharness initchallenge-state.jsonWorkspace initialized with deterministic state fileHighInventory artifacts
2026-06-07T09:30:02Zartifact inventoryanalysis/artifact-inventory.json1 artifact(s) inventoriedHighBuild or update hypotheses
2026-06-07T09:30:02Zsession bootstrapnotes.mdChallenge metadata, scenario, and prior context seeded into workspaceHighRecord initial hypothesis and research
2026-06-07T09:30:02Zhypothesis recordedhypothesis-board.mdinitial triage from supplied challenge metadataMediuminventory files / inspect app surface / map routes depending on category
2026-06-07T09:30:03Zresearch taskanalysis/research/task-20260607T093003010511Z-1a9ded45.mdResearch task created for advisory investigationMediumRecord research output
2026-06-07T12:28:01Zlocal memory recordanalysis/local-memory-records.mdPrior local notes reviewed as fallback/advisory contextMediumValidate against current evidence
2026-06-07T12:29:33Zresearch skipanalysis/research/research-skip.mdResearch intentionally skipped with recorded reasonMediumGate before exploit
2026-06-07T12:29:50Zcheckpoint recordedanalysis/checkpoint-analysis-20260607T122950517333Z-fd3a7e76.mdCheckpoint for ANALYSISHighUse checkpoint to drive next decision
2026-06-07T12:30:33Zcheckpoint recordedanalysis/checkpoint-analysis-20260607T123033289577Z-10d6d427.mdCheckpoint for ANALYSISHighUse checkpoint to drive next decision
2026-06-07T12:30:33ZReconstructed and inverted PRNG/XOR/ROL transformanalysis/objdump-disasm.txt, analysis/solve-result.txtThe binary seeds libc rand() with the first 4 ciphertext bytes interpreted as a Unix timestamp, XORs each plaintext byte with rand() & 0xff, then rotates left by rand() & 7. Replaying the same PRNG and reversing with ROR+XOR decrypts the flag.HighCapture the flag through the harness and complete the workspace.
2026-06-07T12:32:31Zflag captureloot/flag.txtHTB-format flag captured; raw value kept in loot onlyHighWrite solution and run completion gate
2026-06-07T12:32:31ZReconstructed glibc PRNG/XOR/ROL transform and decrypted flag.encanalysis/objdump-disasm.txt, analysis/solve-result.txtThe binary stores a 4-byte Unix time seed in the ciphertext, uses glibc rand() twice per plaintext byte (xor then rol), and writes the transformed bytes to flag.enc. Replaying glibc's additive random stream and applying ROR then XOR decrypts the flag.HighCapture the flag through the harness and complete the workspace.
2026-06-07T12:32:31Zcompletion gatechallenge-state.jsonCompletion gate passed; state marked COMPLETEHighOptional sanitized memory summary approval

Key Findings

-

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.

Scenario

On our regular checkups of our secret flag storage server we found out that we were hit by ransomware. The original flag data is nowhere to be found, but luckily we not only have the encrypted file but also the encryption program itself.

Operator Question

Analyze the provided reversing challenge artifacts, reverse the encryption program, decrypt flag.enc reproducibly, and capture the HTB flag.

Memory Summary

Metadata

  • Platform: HackTheBox Challenges
  • Category: Reversing
  • Challenge: Simple-Encryptor
  • Difficulty: Very 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.

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.

RankPathEvidenceMissing ProofCheapest ValidationConfidenceStatus
1initial triage from supplied challenge metadatachallenge name, category, difficulty, scenario, and remote target were provided by operatorinventory files / inspect app surface / map routes depending on categoryMediumActive

Closed Branches

BranchEvidence TestedFailure OutputReason ClosedRevisit Condition

Memory Summary

approval_required: true

Sanitized Memory Summary

Metadata

  • Platform: HackTheBox Challenges
  • Category: Reversing
  • Challenge: Simple-Encryptor
  • Difficulty: Very 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.

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: Simple-Encryptor
  • Category: Reversing
  • Difficulty: Very Easy
  • Mode: file
  • Remote instance: none
  • Start time: 2026-06-07T09:30:02Z
  • 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/a12c7354-6ee3-4584-912e-2f8b1c122ecf.zip3723<hash redacted>Zip archive data, at least v1.0 to extract, compression method=storezip entries: 3 shown in artifact inventory JSON

Evidence Ledger

TimeActionOutput/FileFindingConfidenceNext
2026-06-07T09:30:02Zharness initchallenge-state.jsonWorkspace initialized with deterministic state fileHighInventory artifacts
2026-06-07T09:30:02Zartifact inventoryanalysis/artifact-inventory.json1 artifact(s) inventoriedHighBuild or update hypotheses
2026-06-07T09:30:02Zsession bootstrapnotes.mdChallenge metadata, scenario, and prior context seeded into workspaceHighRecord initial hypothesis and research
2026-06-07T09:30:02Zhypothesis recordedhypothesis-board.mdinitial triage from supplied challenge metadataMediuminventory files / inspect app surface / map routes depending on category
2026-06-07T09:30:03Zresearch taskanalysis/research/task-20260607T093003010511Z-1a9ded45.mdResearch task created for advisory investigationMediumRecord research output
2026-06-07T12:28:01Zlocal memory recordanalysis/local-memory-records.mdPrior local notes reviewed as fallback/advisory contextMediumValidate against current evidence
2026-06-07T12:29:33Zresearch skipanalysis/research/research-skip.mdResearch intentionally skipped with recorded reasonMediumGate before exploit
2026-06-07T12:29:50Zcheckpoint recordedanalysis/checkpoint-analysis-20260607T122950517333Z-fd3a7e76.mdCheckpoint for ANALYSISHighUse checkpoint to drive next decision
2026-06-07T12:30:33Zcheckpoint recordedanalysis/checkpoint-analysis-20260607T123033289577Z-10d6d427.mdCheckpoint for ANALYSISHighUse checkpoint to drive next decision
2026-06-07T12: <REDACTED>, analysis/solve-result.txtThe binary seeds libc rand() with the first 4 ciphertext bytes interpreted as a Unix timestamp, XORs each plaintext byte with rand() & 0xff, then rotates left by rand() & 7. Replaying the same PRNG and reversing with ROR+XOR decrypts the flag.HighCapture the flag through the harness and complete the workspace.
2026-06-07T12: <REDACTED>
2026-06-07T12: <REDACTED>, analysis/solve-result.txtThe binary stores a 4-byte Unix time seed in the ciphertext, uses glibc rand() twice per plaintext byte (xor then rol), and writes the transformed bytes to flag.enc. Replaying glibc's additive random stream and applying ROR then XOR decrypts the flag.HighCapture the flag through the harness and complete the workspace.
2026-06-07T12:32:31Zcompletion gatechallenge-state.jsonCompletion gate passed; state marked COMPLETEHighOptional sanitized memory summary approval

Key Findings

-

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.

Scenario

On our regular checkups of our secret flag storage server we found out that we were hit by ransomware. The original flag data is nowhere to be found, but luckily we not only have the encrypted file but also the encryption program itself.

Operator Question

Analyze the provided reversing challenge artifacts, reverse the encryption program, decrypt flag.enc reproducibly, and capture the HTB flag.

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 Simple Encryptor, 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.