RSAisEasy
RSAisEasy is a sanitized challenge note from the local HTB archive, organized for quick review by category, difficulty, evidence flow, and reusable operator
Scenario
RSAisEasy attack path
RSAisEasy 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
Read the provided RSA generation script and identify...
Observe that the output leaks (n1 * E) + n2.
Compute gcd(n1, leak % n1) to recover the shared...
Compute p = n1 / q and decrypt the first ciphertext.
Divide the leak by q to get p * E + z.
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.
- Crypto/RSAisEasy/writeup.md
- htb-challenge/Crypto/RSAisEasy/notes.md
- htb-challenge/Crypto/RSAisEasy/memory-summary.md
- htb-challenge/Crypto/RSAisEasy/hypothesis-board.md
- HTB/_knowledge/exports/ctf-lightrag-latest-203412/documents/challenge__Crypto__RSAisEasy__memory-summary.md.11b2b707af.md
- HTB/_knowledge/exports/ctf-lightrag-latest-203412/documents/challenge__Crypto__RSAisEasy__notes.md.fddba09778.md
Technical Walkthrough
Writeup
Challenge
- Name: RSAisEasy
- Category: Crypto
- Difficulty: Easy
- Mode: file
Summary
The provided script uses two RSA moduli that share a prime: n1 = pq and n2 = qz. It does not print n2 directly, but it prints (n1 * E) + n2. Reducing this leak modulo n1 leaves n2 mod n1, which still shares the factor q with n1. From there, n1 can be factored, the first ciphertext can be decrypted, and the second modulus can be reconstructed enough to decrypt the second ciphertext.
Artifact Inventory
Reference analysis/artifact-inventory.json and summarize the relevant files or remote surface.
files/a12c734a-6ff7-4e31-8204-6d990803d383.zip: HTB ZIP archive.analysis/extracted/RSAisEasy.py: RSA parameter generation script.analysis/extracted/output.txt: public valuesn1,c1,c2, and(n1 * E) + n2.solve/solve.py: reproducible offline solver.
Analysis
- The script generates three 512-bit primes
p,q, andz. - It builds
n1 = pqandn2 = qz, so both moduli shareq. - Instead of printing
n2, it printsleak = (n1 * E) + n2. leak % n1removes the unknown multiple ofn1and leavesn2 mod n1.- Since
n2is a multiple ofq,gcd(n1, leak % n1)recoversq. p = n1 / q, soc1decrypts normally withphi(n1) = (p-1)(q-1).- Dividing the leak by
qgivesp * E + z. The remainder modulopgivesz, orz - pifz > p, so the solver testsremainderandremainder + p. - Once
zis recovered,n2 = q*zandc2decrypts normally.
Solve
Run:
python3 Crypto/RSAisEasy/solve/solve.pyThe script parses output.txt, recovers q with gcd, reconstructs z, decrypts both RSA ciphertexts, joins both plaintext fragments, and prints the flag for harness capture.
Flag
Raw flag is stored in loot/flag.txt and intentionally not reproduced here.
Lessons
- Shared-prime RSA failures are immediately exploitable with
gcd. - Hiding one modulus as
(known_modulus * random) + hidden_modulusdoes not protect the shared factor because modular reduction removes the random multiple. - When a hidden prime is added to a known-prime multiple, simple division/remainder reasoning can recover the missing prime with only one small candidate adjustment.
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: RSAisEasy
- Category: Crypto
- Difficulty: Easy
- Mode: file
- Remote instance: none
- Start time: 2026-06-10T10:33: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/a12c734a-6ff7-4e31-8204-6d990803d383.zip | 1385 | <hash redacted> | Zip archive data, at least v2.0 to extract, compression method=deflate | zip entries: 2 shown in artifact inventory JSON |
Evidence Ledger
| Time | Action | Output/File | Finding | Confidence | Next |
|---|---|---|---|---|---|
| 2026-06-10T10:33:14Z | harness init | challenge-state.json | Workspace initialized with deterministic state file | High | Inventory artifacts |
| 2026-06-10T10:33:14Z | artifact inventory | analysis/artifact-inventory.json | 1 artifact(s) inventoried | High | Build or update hypotheses |
| 2026-06-10T10:34:16Z | hypothesis recorded | hypothesis-board.md | Recover the shared RSA prime q from gcd(n1, ((n1 * E) + n2) mod n1), then factor n1 and reconstruct enough of n2 to decrypt both ciphertexts. | Medium | Parse output.txt, compute q with gcd, derive p, test z candidates from dividing leak/q by p, and decrypt c1/c2 with RSA private exponents. |
| 2026-06-10T10:34:16Z | research skip | analysis/research/research-skip.md | Research intentionally skipped with recorded reason | Medium | Gate before exploit |
| 2026-06-10T10:34:52Z | flag capture | loot/flag.txt | HTB-format flag captured; raw value kept in loot only | High | Write solution and run completion gate |
| 2026-06-10T10:34:52Z | checkpoint recorded | analysis/checkpoint-analysis-20260610T103452480975Z-80251ba6.md | Checkpoint for ANALYSIS | High | Use checkpoint to drive next decision |
| 2026-06-10T10:35:54Z | completion gate | challenge-state.json | Completion gate passed; state marked COMPLETE | High | Optional sanitized memory summary approval |
Key Findings
RSAisEasy.pygeneratesn1 = pqandn2 = qz, so the moduli share primeq.output.txtleaks(n1 * E) + n2instead ofn2directly.gcd(n1, leak % n1)recovers the shared primeq.p = n1 / qdecryptsc1.leak / q = p * E + z; testing the remainder modulopandremainder + precoversz.n2 = q*zdecryptsc2.solve/solve.pyreproduces the full offline solve and the harness captured the flag intoloot/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: RSAisEasy
- 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.
- Read the provided RSA generation script and identify that two moduli share a prime:
n1 = pq,n2 = qz. - Observe that the output leaks
(n1 * E) + n2. - Compute
gcd(n1, leak % n1)to recover the shared primeq. - Compute
p = n1 / qand decrypt the first ciphertext. - Divide the leak by
qto getp * E + z. - Recover
zby testing the quotient remainder modulopandremainder + p. - Reconstruct
n2 = q*zand decrypt the second ciphertext. - Join both plaintext fragments.
Reusable Lessons
- Shared-prime RSA challenges should start with
gcdchecks across any visible or partially visible modulus relationship. - A masked value of the form
(known_modulus * random) + hidden_modulusstill leaks the hidden modulus modulo the known one. - When primes have similar bit lengths, quotient/remainder relationships often leave only one or two candidates to test.
Dead Ends
- No external research, Sage, Sympy, or CTF RSA tooling was required.
- Brute force was unnecessary; the leak is algebraic.
Tool Quirks
- PyCryptodome was available and sufficient for
inverse,isPrime, andlong_to_bytes. - Local
sympyandsagewere unavailable, but not needed.
Evidence Paths
analysis/extracted/RSAisEasy.pyanalysis/extracted/output.txtanalysis/checkpoint-analysis-20260610T103452480975Z-80251ba6.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 the shared RSA prime q from gcd(n1, ((n1 * E) + n2) mod n1), then factor n1 and reconstruct enough of n2 to decrypt both ciphertexts. | RSAisEasy.py creates n1=pq and n2=qz, then prints (n1 * E) + n2; output.txt provides n1, c1, c2, and the combined leak. | Parse output.txt, compute q with gcd, derive p, test z candidates from dividing leak/q by p, and decrypt c1/c2 with RSA private exponents. | Medium | Active |
Closed Branches
| Branch | Evidence Tested | Failure Output | Reason Closed | Revisit Condition |
|---|
Memory Summary
approval_required: true
Sanitized Memory Summary
Metadata
- Platform: HackTheBox Challenges
- Category: Crypto
- Challenge: RSAisEasy
- 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.
- Read the provided RSA generation script and identify that two moduli share a prime:
n1 = pq,n2 = qz. - Observe that the output leaks
(n1 * E) + n2. - Compute
gcd(n1, leak % n1)to recover the shared primeq. - Compute
p = n1 / qand decrypt the first ciphertext. - Divide the leak by
qto getp * E + z. - Recover
zby testing the quotient remainder modulopandremainder + p. - Reconstruct
n2 = q*zand decrypt the second ciphertext. - Join both plaintext fragments.
Reusable Lessons
- Shared-prime RSA challenges should start with
gcdchecks across any visible or partially visible modulus relationship. - A masked value of the form
(known_modulus * random) + hidden_modulusstill leaks the hidden modulus modulo the known one. - When primes have similar bit lengths, quotient/remainder relationships often leave only one or two candidates to test.
Dead Ends
- No external research, Sage, Sympy, or CTF RSA tooling was required.
- Brute force was unnecessary; the leak is algebraic.
Tool Quirks
- PyCryptodome was available and sufficient for
inverse,isPrime, andlong_to_bytes. - Local
sympyandsagewere unavailable, but not needed.
Evidence Paths
analysis/extracted/RSAisEasy.pyanalysis/extracted/output.txtanalysis/checkpoint-analysis-20260610T103452480975Z-80251ba6.mdsolve/solve.pyloot/flag.txt
Ingestion Decision
- Proposed for LightRAG: yes
- Requires user approval before ingestion: yes
Notes
Notes
Scope
- Challenge: RSAisEasy
- Category: Crypto
- Difficulty: Easy
- Mode: file
- Remote instance: none
- Start time: 2026-06-10T10:33: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/a12c734a-6ff7-4e31-8204-6d990803d383.zip | 1385 | <hash redacted> | Zip archive data, at least v2.0 to extract, compression method=deflate | zip entries: 2 shown in artifact inventory JSON |
Evidence Ledger
| Time | Action | Output/File | Finding | Confidence | Next |
|---|---|---|---|---|---|
| 2026-06-10T10:33:14Z | harness init | challenge-state.json | Workspace initialized with deterministic state file | High | Inventory artifacts |
| 2026-06-10T10:33:14Z | artifact inventory | analysis/artifact-inventory.json | 1 artifact(s) inventoried | High | Build or update hypotheses |
| 2026-06-10T10:34:16Z | hypothesis recorded | hypothesis-board.md | Recover the shared RSA prime q from gcd(n1, ((n1 * E) + n2) mod n1), then factor n1 and reconstruct enough of n2 to decrypt both ciphertexts. | Medium | Parse output.txt, compute q with gcd, derive p, test z candidates from dividing leak/q by p, and decrypt c1/c2 with RSA private exponents. |
| 2026-06-10T10:34:16Z | research skip | analysis/research/research-skip.md | Research intentionally skipped with recorded reason | Medium | Gate before exploit |
| 2026-06-10T10: <REDACTED> | |||||
| 2026-06-10T10:34:52Z | checkpoint recorded | analysis/checkpoint-analysis-20260610T103452480975Z-80251ba6.md | Checkpoint for ANALYSIS | High | Use checkpoint to drive next decision |
| 2026-06-10T10:35:54Z | completion gate | challenge-state.json | Completion gate passed; state marked COMPLETE | High | Optional sanitized memory summary approval |
Key Findings
RSAisEasy.pygeneratesn1 = pqandn2 = qz, so the moduli share primeq.output.txtleaks(n1 * E) + n2instead ofn2directly.gcd(n1, leak % n1)recovers the shared primeq.p = n1 / qdecryptsc1.leak / q = p * E + z; testing the remainder modulopandremainder + precoversz.n2 = q*zdecryptsc2.solve/solve.pyreproduces the full offline solve and the harness captured the flag intoloot/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.
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 RSAisEasy, 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.