ReMeeting The Wheel
ReMeeting The Wheel is a sanitized challenge note from the local HTB archive, organized for quick review by category, difficulty, evidence flow, and reusable operator
Scenario
ReMeeting The Wheel attack path
ReMeeting The Wheel 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 Crypto evidence, validation, and reusable operator lessons.
Walkthrough flow
Audit the provided Python source and identify that...
Use RSA multiplicativity to rewrite the encrypted...
Build a meet-in-the-middle table over the small...
Derive the AES key with SHA-256 over the decimal...
Decrypt the AES-ECB ciphertext and capture the proof...
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.
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.
- Crypto/ReMeeting-the-Wheel/writeup.md
- htb-challenge/Crypto/ReMeeting-the-Wheel/notes.md
- htb-challenge/Crypto/ReMeeting-the-Wheel/memory-summary.md
- htb-challenge/Crypto/ReMeeting-the-Wheel/hypothesis-board.md
Technical Walkthrough
Writeup
Challenge
- Name: ReMeeting-the-Wheel
- Category: Crypto
- Difficulty: Medium
- Mode: file
Summary
The challenge combines RSA and AES incorrectly. The AES seed is not random enough: it is the product of two roughly 20-bit values. RSA hides the product, but the multiplicative structure remains usable. A meet-in-the-middle over the two factor ranges recovers the seed, which derives the AES key used to decrypt the flag.
Artifact Inventory
analysis/artifact-inventory.jsonrecords the original ZIP,source.py, andoutput.txt.files/extracted/crypto_remeeting_the_wheel/source.pycontains the generation and encryption logic.files/extracted/crypto_remeeting_the_wheel/output.txtcontains the RSA public key, encrypted AES seed, and encrypted secret.
Analysis
source.py defines the AES seed as:
k = key1 * key2Both factors are selected from the same small range, [2^20, 2^21]. The seed is then RSA-encrypted and the flag is encrypted with AES-ECB using sha256(str(k)).
RSA encryption is multiplicative:
c = (key1 * key2)^e mod n
c = key1^e * key2^e mod nThat means the seed can be recovered with a meet-in-the-middle:
- Compute and store
x^e mod nfor every candidatexin the factor range. - For each candidate
y, computec * inverse(y^e) mod n. - A table hit gives the matching pair and therefore the AES seed.
- Hash the decimal string representation of the seed with SHA-256 and decrypt the AES-ECB ciphertext.
Solve
The reproducible solver is solve/solve.py.
It parses output.txt, performs the meet-in-the-middle recovery, derives the AES key exactly as source.py does, decrypts the ciphertext, and writes the candidate flag to loot/flag-candidate.txt.
The flag was then captured through:
python3 scripts/challenge_harness.py capture-flag Crypto/ReMeeting-the-Wheel --from loot/flag-candidate.txtFlag
Raw flag is stored in loot/flag.txt and intentionally not reproduced here.
Lessons
- RSA should not be used as a raw trapdoor permutation over structured, tiny messages.
- If a plaintext is a product of two values from a small range, RSA multiplicativity enables a meet-in-the-middle recovery.
- Reproducing the exact key-derivation input format matters; here AES uses
sha256(str(k).encode()).
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: ReMeeting-the-Wheel
- Category: Crypto
- Difficulty: Medium
- Mode: file
- Remote instance: none
- Start time: 2026-06-12T21:36: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/a12c7354-8939-45e3-b062-b553a6d4ab36.zip | 1977 | <hash redacted> | Zip archive data, at least v1.0 to extract, compression method=store | zip entries: 3 shown in artifact inventory JSON |
files/extracted/crypto_remeeting_the_wheel/output.txt | 952 | <hash redacted> | ASCII text, with very long lines (317) | |
files/extracted/crypto_remeeting_the_wheel/source.py | 1748 | <hash redacted> | Python script text executable, ASCII text |
Evidence Ledger
| Time | Action | Output/File | Finding | Confidence | Next |
|---|---|---|---|---|---|
| 2026-06-12T21:36:14Z | harness init | challenge-state.json | Workspace initialized with deterministic state file | High | Inventory artifacts |
| 2026-06-12T21:36:30Z | artifact inventory | analysis/artifact-inventory.json | 3 artifact(s) inventoried | High | Build or update hypotheses |
| 2026-06-12T21:37:04Z | hypothesis recorded | hypothesis-board.md | Recover AES seed k without factoring RSA by using k = key1 key2 where key1 and key2 are ~20-bit. Since c = (key1^e key2^e) mod n, build a meet-in-the-middle table of x^e mod n for the 20-bit factor range and find the matching pair. | Medium | Run Python MITM over the factor interval, derive sha256(str(k)) as AES key, decrypt the secret, and check for HTB flag format. |
| 2026-06-12T21:37:52Z | hypothesis recorded | hypothesis-board.md | Recover AES seed k without factoring RSA by using k = key1 key2 where key1 and key2 are ~20-bit. Since c = (key1^e key2^e) mod n, build a meet-in-the-middle table of x^e mod n for the 20-bit factor range and find the matching pair. | Medium | Run Python MITM over the factor interval, derive sha256(str(k)) as AES key, decrypt the secret, and check for HTB flag format. |
| 2026-06-12T21:38:00Z | research record | analysis/research/research-records.md | Research tagged MATCHED | Medium | Validate against current evidence |
| 2026-06-12T21:39:56Z | flag capture | loot/flag.txt | HTB-format flag captured; raw value kept in loot only | High | Write solution and run completion gate |
| 2026-06-12T21:40:51Z | completion gate | challenge-state.json | Completion gate passed; state marked COMPLETE | High | Optional sanitized memory summary approval |
Key Findings
source.pygenerates the AES seed as the product of two random integers in the interval[2^20, 2^21].- RSA encrypts that small structured product directly. Since
c = (ab)^e mod n, the product can be recovered without factoringnby matchinga^e mod nagainstc inverse(b^e) mod n. - The recovered product is hashed with SHA-256 as shown in
source.py, then used as the AES-ECB key to decryptEncrypted Secret. solve/solve.pyimplements this full path and writes the flag candidate toloot/flag-candidate.txt; the harness-captured flag is inloot/flag.txt.
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: Crypto
- Challenge: ReMeeting-the-Wheel
- 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.
- Audit the provided Python source and identify that the AES seed is the product of two roughly 20-bit random values.
- Use RSA multiplicativity to rewrite the encrypted seed as a product of two modular exponentiations.
- Build a meet-in-the-middle table over the small factor interval to recover the seed without factoring the RSA modulus.
- Derive the AES key with SHA-256 over the decimal string form of the recovered seed.
- Decrypt the AES-ECB ciphertext and capture the flag through the harness.
Reusable Lessons
- Raw RSA over small structured messages is vulnerable even when the modulus is large.
- Product-form plaintexts can often be split with meet-in-the-middle when each factor lives in a small range.
- Match key derivation byte-for-byte with the source; decimal string hashing differs from hashing integer bytes.
Dead Ends
- Factoring the 1024-bit RSA modulus is unnecessary and not the intended path.
- Brute forcing the full product range directly is wasteful; split the product into its two small factors.
Tool Quirks
- Python and PyCryptodome were sufficient. Sage/Z3 were not needed.
- The MITM search runs about one million modular exponentiations per side and took under a minute locally.
Evidence Paths
files/extracted/crypto_remeeting_the_wheel/source.pyfiles/extracted/crypto_remeeting_the_wheel/output.txtanalysis/research/research-records.mdsolve/solve.pyloot/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.
| Rank | Path | Evidence | Missing Proof | Cheapest Validation | Confidence | Status |
|---|---|---|---|---|---|---|
| 1 | Recover AES seed k without factoring RSA by using k = key1 key2 where key1 and key2 are ~20-bit. Since c = (key1^e key2^e) mod n, build a meet-in-the-middle table of x^e mod n for the 20-bit factor range and find the matching pair. | source.py AESGen chooses two randint values in [2^20, 2^21] and multiplies them into k; output.txt gives n, e, RSA ciphertext for k, and AES-ECB ciphertext for the flag. | Run Python MITM over the factor interval, derive sha256(str(k)) as AES key, decrypt the secret, and check for HTB flag format. | Medium | Active | |
| 1 | Recover AES seed k without factoring RSA by using k = key1 key2 where key1 and key2 are ~20-bit. Since c = (key1^e key2^e) mod n, build a meet-in-the-middle table of x^e mod n for the 20-bit factor range and find the matching pair. | source.py AESGen chooses two randint values in [2^20, 2^21] and multiplies them into k; output.txt gives n, e, RSA ciphertext for k, and AES-ECB ciphertext for the flag. | Run Python MITM over the factor interval, derive sha256(str(k)) as AES key, decrypt the secret, and check for HTB flag format. | Medium | Active |
Closed Branches
| Branch | Evidence Tested | Failure Output | Reason Closed | Revisit Condition |
|---|
Technical analogy
How to remember this solve
Think of the challenge like a locked box where the lock is mathematical but slightly flawed. The goal is not to smash the box; it is to notice which part of the lock repeats, leaks, or trusts the wrong assumption.
For ReMeeting The Wheel, 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.