Surprise Factor
Surprise Factor is a sanitized challenge note from the local HTB archive, organized for quick review by category, difficulty, evidence flow, and reusable operator
Scenario
Surprise Factor attack path
Surprise Factor 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
Challenge parameters
Weak assumption
Recovered secret state
Proof captured
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/Surprise-Factor/writeup.md
- htb-challenge/Crypto/Surprise-Factor/notes.md
- htb-challenge/Crypto/Surprise-Factor/memory-summary.md
- htb-challenge/Crypto/Surprise-Factor/hypothesis-board.md
Technical Walkthrough
Writeup
Challenge
- Name: Surprise-Factor
- Category: Crypto
- Difficulty: Hard
- Mode: remote
Summary
Artifact Inventory
Reference analysis/artifact-inventory.json and summarize the relevant files or remote surface.
Analysis
Solve
Flag
Raw flag is stored in loot/flag.txt and intentionally not reproduced here.
Lessons
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: Surprise-Factor
- Category: Crypto
- Difficulty: Hard
- Mode: remote
- Remote instance: none
- Start time: 2026-06-15T04:58:42Z
- 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/a1d75b6a-cfdf-4383-b750-2f63e6d23612-1779463237.zip | 6585 | <hash redacted> | Zip archive data, at least v1.0 to extract, compression method=store | zip entries: 6 shown in artifact inventory JSON |
files/extracted/crypto_surprise_factor/bignum.py | 2493 | <hash redacted> | Python script text executable, ASCII text | |
files/extracted/crypto_surprise_factor/ec.py | 4571 | <hash redacted> | Python script text executable, ASCII text | |
files/extracted/crypto_surprise_factor/ecdsa.py | 4105 | <hash redacted> | Python script text executable, ASCII text | |
files/extracted/crypto_surprise_factor/server.py | 3538 | <hash redacted> | Python script text executable, ASCII text | |
files/extracted/crypto_surprise_factor/trace.py | 1452 | <hash redacted> | Python script text executable, ASCII text |
Evidence Ledger
| Time | Action | Output/File | Finding | Confidence | Next |
|---|---|---|---|---|---|
| 2026-06-15T04:58:42Z | harness init | challenge-state.json | Workspace initialized with deterministic state file | High | Inventory artifacts |
| 2026-06-15T04:59:00Z | artifact inventory | analysis/artifact-inventory.json | 0 artifact(s) inventoried | High | Build or update hypotheses |
| 2026-06-15T04:59:48Z | hypothesis recorded | hypothesis-board.md | ECDSA nonce leakage can be converted into a Hidden Number Problem key recovery | Medium | Capture bounded menu/protocol and 10-20 signed trace samples |
| 2026-06-15T04:59:48Z | hypothesis recorded | hypothesis-board.md | Trace noise masks reusable or biased nonce component across signatures | Low | Compare repeated signatures for same/different messages and summarize trace values |
| 2026-06-15T04:59:48Z | hypothesis recorded | hypothesis-board.md | Flag action is gated by recovered private key or forged ECDSA signature | Low | Fingerprint menu options and save transcript |
| 2026-06-15T04:59:48Z | checkpoint recorded | analysis/checkpoint-triage-20260615T045948669784Z-9e3ee1f2.md | Checkpoint for TRIAGE | High | Use checkpoint to drive next decision |
| 2026-06-15T05:02:42Z | RAG query | analysis/rag/rag-query-20260615T050232140764Z-47ae5eee.txt | RAG helper exited 0; output saved | Medium | Record retrieval tag and validation |
| 2026-06-15T05:03:05Z | RAG record | analysis/rag-records.md | Retrieved memory tagged MISSING | Medium | Validate or reject with live evidence |
| 2026-06-15T05:03:47Z | research record | analysis/research/research-records.md | Research tagged PARTIAL | Medium | Validate against current evidence |
| 2026-06-15T05:06:14Z | instrumentation plan | analysis/instrumentation-plan.md | Validate ECDSA P-256 signing behavior and determine whether returned trace/samples expose nonce leakage | High | Stop if trace remains empty and no nonce/message anomaly appears in the bounded sample set |
| 2026-06-15T05:07:35Z | checkpoint recorded | analysis/checkpoint-analysis-20260615T050735760257Z-b6012795.md | Checkpoint for ANALYSIS | High | Use checkpoint to drive next decision |
| 2026-06-15T05:09:33Z | checkpoint recorded | analysis/checkpoint-analysis-20260615T050933669910Z-d059b908.md | Checkpoint for ANALYSIS | High | Use checkpoint to drive next decision |
| 2026-06-15T05:09:33Z | hypothesis recorded | hypothesis-board.md | Server-chosen hash values leak PRNG state used for ECDSA nonces | Medium | Collect 80 signatures, untemper 624 32-bit words from hashes in both endian orders, and check prediction against the next returned hash |
| 2026-06-15T05:12:22Z | branch closed | hypothesis-board.md | Observed hash words are not contiguous MT19937 outputs, or hidden nonce/random draws are interleaved, or a different RNG is used | High | Rerank hypotheses |
| 2026-06-15T05:14:19Z | branch closed | hypothesis-board.md | Service ignores these trace/debug/leak/profiling field names and always returns trace=[] | High | Rerank hypotheses |
| 2026-06-15T05:14:35Z | local memory search | analysis/research/local-memory-search-20260615T051435757482Z-4abb2d0f.md | Found 10 safe prior-note result(s) | Medium | Record useful result or skip |
| 2026-06-15T05:14:59Z | evaluator | analysis/evaluator-20260615T051459741645Z-e7b424c3.md | Validate first | High | Pause exploit attempts and request/source the missing trace-leak clue; then update hard-plan.md before resuming. |
| 2026-06-15T05:42:32Z | checkpoint recorded | analysis/checkpoint-analysis-20260615T054232532008Z-ff00f33d.md | Checkpoint for ANALYSIS | High | Use checkpoint to drive next decision |
| 2026-06-15T05:42:32Z | branch closed | hypothesis-board.md | Trace output does not appear to be enabled by obvious boolean/string/numeric/nested values for likely trace/debug/leak fields | High | Rerank hypotheses |
| 2026-06-15T05:43:25Z | branch closed | hypothesis-board.md | No parser bypass or accidental accepted proof path was identified | High | Rerank hypotheses |
| 2026-06-15T05:43:25Z | checkpoint recorded | analysis/checkpoint-analysis-20260615T054325342193Z-5aa6f904.md | Checkpoint for ANALYSIS | High | Use checkpoint to drive next decision |
| 2026-06-15T05:45:08Z | hypothesis recorded | hypothesis-board.md | Exposed hashes are MT19937 output blocks interleaved with hidden ECDSA nonce blocks | Medium | Collect a longer single-connection hash stream and solve a GF(2) MT19937 skip-pattern model for observed 8-word blocks with 8 hidden words between |
| 2026-06-15T05:45:08Z | checkpoint recorded | analysis/checkpoint-analysis-20260615T054508717248Z-14d1710c.md | Checkpoint for ANALYSIS | High | Use checkpoint to drive next decision |
| 2026-06-15T08:58:16Z | branch closed | hypothesis-board.md | The full-rank GF(2) MT19937 model is inconsistent for all 624 offsets in both tested 32-bit word orders, so the exposed hashes are not exact Python MT blocks interleaved with one 256-bit nonce draw. | High | Rerank hypotheses |
| 2026-06-15T08:58:16Z | checkpoint recorded | analysis/checkpoint-analysis-20260615T085816608134Z-23bb30b7.md | Checkpoint for ANALYSIS | High | Use checkpoint to drive next decision |
| 2026-06-15T08:58:16Z | evaluator | analysis/evaluator-20260615T085816643245Z-60bcaef2.md | Validate first | High | source_or_hint_required_before_remote_submit |
| 2026-06-15T09:01:53Z | checkpoint recorded | analysis/checkpoint-analysis-20260615T090153403544Z-36f98160.md | Checkpoint for ANALYSIS | High | Use checkpoint to drive next decision |
| 2026-06-15T09:01:53Z | evaluator | analysis/evaluator-20260615T090153648948Z-2a735af5.md | Validate first | High | restart_remote_then_run_capped_schema_probe |
| 2026-06-15T09:24:33Z | checkpoint recorded | analysis/checkpoint-analysis-20260615T092433893841Z-927267a2.md | Checkpoint for ANALYSIS | High | Use checkpoint to drive next decision |
| 2026-06-15T10:04:21Z | branch closed | hypothesis-board.md | Malformed JSON types and edge-case fields did not expose source, stack traces, or non-empty trace on the reachable <TARGET>:31235 instance. | High | Rerank hypotheses |
| 2026-06-15T10:04:21Z | checkpoint recorded | analysis/checkpoint-analysis-20260615T100421698963Z-d511d6fd.md | Checkpoint for ANALYSIS | High | Use checkpoint to drive next decision |
| 2026-06-15T10:10:35Z | branch closed | hypothesis-board.md | The full-rank GF(2) MT19937 hidden-8 model is inconsistent across all offsets for both integer-style and randbytes().hex()-style 32-bit packings. | High | Rerank hypotheses |
| 2026-06-15T10:10:35Z | checkpoint recorded | analysis/checkpoint-analysis-20260615T101035963721Z-37a9be78.md | Checkpoint for ANALYSIS | High | Use checkpoint to drive next decision |
| 2026-06-15T10:14:42Z | branch closed | hypothesis-board.md | Disabling likely masking, blinding, noise, hardening, constant-time, randomization, and protection controls did not expose trace data or alter the response schema. | High | Rerank hypotheses |
| 2026-06-15T10:14:42Z | checkpoint recorded | analysis/checkpoint-analysis-20260615T101442024343Z-04bc6227.md | Checkpoint for ANALYSIS | High | Use checkpoint to drive next decision |
| 2026-06-15T10:16:05Z | branch closed | hypothesis-board.md | The nonce is not an obvious direct function of hash, r, s, byte-swapped hash, half-hash, xor, add, or subtract variants. | High | Rerank hypotheses |
| 2026-06-15T10:16:05Z | evaluator | analysis/evaluator-20260615T101605577153Z-7b94d3bf.md | Validate first | High | source_or_narrow_leak_hint_required |
| 2026-06-15T10:16:05Z | checkpoint recorded | analysis/checkpoint-analysis-20260615T101605604410Z-be2bed25.md | Checkpoint for ANALYSIS | High | Use checkpoint to drive next decision |
| 2026-06-15T10:28:19Z | artifact inventory | analysis/artifact-inventory.json | 1 artifact(s) inventoried | High | Build or update hypotheses |
| 2026-06-15T10:28:33Z | artifact inventory | analysis/artifact-inventory.json | 6 artifact(s) inventoried | High | Build or update hypotheses |
| 2026-06-15T10:29:01Z | source audit | analysis/source-audit.md | Source audit recorded | High | Gate before exploit |
| 2026-06-15T10:56:42Z | branch closed | hypothesis-board.md | Per-segment p256 reduction counts around double/add operations are statistically indistinguishable for scalar bits under coordinate/scalar blinding. | High | Rerank hypotheses |
| 2026-06-15T10:56:42Z | branch closed | hypothesis-board.md | Simple binary-division trace counts/ngrams do not provide reliable nonce bit-length constraints for a lattice attack. | High | Rerank hypotheses |
| 2026-06-15T10:56:42Z | checkpoint recorded | analysis/checkpoint-analysis-20260615T105642407471Z-a8d8a041.md | Checkpoint for ANALYSIS | High | Use checkpoint to drive next decision |
| 2026-06-15T11:36:46Z | evaluator | analysis/evaluator-20260615T113646059519Z-5d712c3e.md | Validate first | High | local_p256_binary_gcd_product_recovery |
| 2026-06-15T11:36:46Z | research record | analysis/research/research-records.md | Research tagged MATCHED | Medium | Validate against current evidence |
| 2026-06-15T11:36:46Z | checkpoint recorded | analysis/checkpoint-analysis-20260615T113646112791Z-bb78eee8.md | Checkpoint for ANALYSIS | High | Use checkpoint to drive next decision |
| 2026-06-15T12:47:38Z | research record | analysis/research/research-records.md | Research tagged PARTIAL | Medium | Validate against current evidence |
| 2026-06-15T12:47:38Z | evaluator | analysis/evaluator-20260615T124738334895Z-0dd4a8c5.md | Validate first | High | derive_scalable_partial_model_for_local_p256 |
| 2026-06-15T12:47:38Z | checkpoint recorded | analysis/checkpoint-analysis-20260615T124738415299Z-14406177.md | Checkpoint for ANALYSIS | High | Use checkpoint to drive next decision |
Key Findings
- Remote service accepts newline-delimited JSON objects.
- Valid actions are
signandsubmit; unknown actions returnaction must be 'sign', or 'submit'. signreturns P-256 ECDSA fields:hash,r,s,trace, and fixedpublic_key.- The fixed public key sits on NIST P-256 and captured signatures verify locally.
submitexpects private scalar fieldd;d=1returnsvalid: false.- Message/hash inputs appear ignored; repeated identical sign requests produce different server-chosen hashes.
traceis currently empty across default signing, common trace/debug/leak flags, multiline connections, and 90 bounded samples.- Contiguous MT19937 recovery from exposed hash chunks failed with 0/96 held-out word matches in both tested word orders.
- Exact hidden-8 interleaved MT19937 recovery failed across all 624 offsets in both tested word orders.
- Replacement instance
<TARGET>:31235is reachable and returns the same fixed P-256 public key. - Source archive was provided and extracted under
files/extracted/crypto_surprise_factor/. - Source confirms the real trace-enabling request field is
track, nottrace,debug, orleak. - Example:
{"action":"sign","track":["scalar_mul","point_double","point_add","p256_quasi_reduce"]}returns non-empty compact symbol traces. - Reachable-instance schema-error probe produced no non-empty trace, source, or stack leak.
- Mask/noise/security disabling probe tested 162 combinations and still returned
trace_len=0. - Expanded MT19937 recovery also failed for byte-swapped
randbytes().hex()-style packings. - Simple nonce-relation sanity checks found no consistent
dfor obviousk = hash/r/s, half-hash, byte-swapped hash, xor, add, or subtract variants. - Local labeled tests show scalar-multiplication p256-reduction segment counts do not predict masked scalar bits better than chance.
- Local binary-division trace count/ngram tests do not predict nonce bit length better than baseline.
- Public research now strongly matches the binary-GCD product-recovery route behind Mbed TLS <secret redacted>: recover unreduced
b = nonce_mask * nonce, factorb, enumerate nonce divisors, derived. - Local toy validation confirms the challenge's binary-GCD trace can uniquely recover the unreduced product for small moduli, and the pruned symbolic skeleton solver works through 40-bit products.
- Local full-trace residue validation now recovers exact products for toy traces, including a single 32-bit trace, but still caps on P-256 around iteration 17 with 524,288 residue pairs and only about 40 lifted bits.
- Local P-256 validation is not complete: current Python solvers branch too much, and the experimental Z3 solver is too slow/unreliable in its current formulation.
- Current effective state is
<secret redacted>; do not attemptsubmit(d)or lattice/RNG brute modeling without source, non-empty trace, or a narrow leakage hint.
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: Surprise-Factor
- Difficulty: Hard
- 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.
| Rank | Path | Evidence | Missing Proof | Cheapest Validation | Confidence | Status |
|---|---|---|---|---|---|---|
| 1 | ECDSA nonce leakage can be converted into a Hidden Number Problem key recovery | Scenario states ECDSA signing service with masked internals and noisy trace leakage | Need service protocol, curve, signature fields, message control, and trace format | Capture bounded menu/protocol and 10-20 signed trace samples | Medium | active |
| 2 | Trace noise masks reusable or biased nonce component across signatures | Challenge wording emphasizes noisy internals and a pattern in the noise | Need repeated signing behavior and trace statistics | Compare repeated signatures for same/different messages and summarize trace values | Low | active |
| 3 | Flag action is gated by recovered private key or forged ECDSA signature | Remote ECDSA signing-service challenge pattern commonly requires proof after key recovery | Need exact menu endpoint and proof format | Fingerprint menu options and save transcript | Low | active |
| 1 | Server-chosen hash values leak PRNG state used for ECDSA nonces | The service ignores message/hash inputs, returns a fresh random-looking 256-bit hash for each signature, and trace remains empty | Need prove whether exposed hash stream matches a reconstructable PRNG such as MT19937 and whether predicted PRNG outputs align with future hashes/nonces | Collect 80 signatures, untemper 624 32-bit words from hashes in both endian orders, and check prediction against the next returned hash | Medium | active |
| 1 | Exposed hashes are MT19937 output blocks interleaved with hidden ECDSA nonce blocks | Service ignores message inputs, returns fresh 256-bit hashes, trace is empty, submit(d) proves private-key target, and public HTB ECDSA writeups show Python random state has been used as the intended weak source in similar challenges | Need recover MT state from observed every-other 256-bit hash blocks or reject the skip pattern with a held-out prediction | Collect a longer single-connection hash stream and solve a GF(2) MT19937 skip-pattern model for observed 8-word blocks with 8 hidden words between | Medium | active |
Closed Branches
| Branch | Evidence Tested | Failure Output | Reason Closed | Revisit Condition |
|---|---|---|---|---|
| Contiguous MT19937 state recovery from exposed hash words | analysis/samples/sign-samples-90-mt-check.txt shows 0/96 predicted held-out words for both low/high word order | Observed hash words are not contiguous MT19937 outputs, or hidden nonce/random draws are interleaved, or a different RNG is used | Revisit only if source or trace evidence shows MT19937 with skipped/interleaved outputs | |
| Common trace-enabling JSON fields | analysis/remote/trace-field-probe-capped.jsonl tested 41 likely fields and ended with hits=[] | Service ignores these trace/debug/leak/profiling field names and always returns trace=[] | Revisit only with source artifact, official hint, or a specific field name clue | |
| Trace field value variants | analysis/remote/trace-value-variants-capped.jsonl tested 130 field/value variants and ended with hits=[] | Trace output does not appear to be enabled by obvious boolean/string/numeric/nested values for likely trace/debug/leak fields | Revisit only with source, exact parameter hint, or changed service behavior | |
| submit(d) parser/type-confusion bypass | analysis/remote/submit-parser-probe.jsonl shows normal integer/base-0 parsing and only valid:false for non-key numeric values | No parser bypass or accidental accepted proof path was identified | Revisit only if source shows comparison/coercion bug or if a candidate d is locally recovered | |
| Exact interleaved MT19937 hash/nonces with hidden 8-word nonce blocks across arbitrary output offset | analysis/samples/mt-interleaved-offset-sweep-train80-hidden8.txt | order=low: consistent_offsets=[]; order=high: consistent_offsets=[] | The full-rank GF(2) MT19937 model is inconsistent for all 624 offsets in both tested 32-bit word orders, so the exposed hashes are not exact Python MT blocks interleaved with one 256-bit nonce draw. | Only revisit if source or a narrow hint confirms Python MT19937 with a different packing/rejection model. |
| Bounded schema-error/source-leak probe on reachable instance | analysis/remote/schema-error-probe-<TARGET>-31235.jsonl | 23 cases; trace hits=[]; no stack/source leak; normal app errors or trace_len=0 signatures | Malformed JSON types and edge-case fields did not expose source, stack traces, or non-empty trace on the reachable <TARGET>:31235 instance. | Only revisit with a source-level clue about a specific schema field or different host behavior. |
| Python MT19937 hash/nonces with integer and randbytes hex packing | analysis/samples/mt-interleaved-offset-sweep-train80-hidden8-expanded-orders.txt | order=low/high/low_bswap/high_bswap all consistent_offsets=[] | The full-rank GF(2) MT19937 hidden-8 model is inconsistent across all offsets for both integer-style and randbytes().hex()-style 32-bit packings. | Only revisit if source confirms MT19937 with a different number of hidden draws, rejection behavior, or non-32-bit output extraction. |
| Mask/noise/security disabling input fields | analysis/remote/disable-mask-noise-probe-<TARGET>-31235.jsonl | 162 combinations tested; hits=[]; every signing response kept trace_len=0 | Disabling likely masking, blinding, noise, hardening, constant-time, randomization, and protection controls did not expose trace data or alter the response schema. | Only revisit with source or a precise parameter hint. |
| Simple nonce/hash relation candidates | analysis/local-model/simple-nonce-relation-check.txt | All tested relations produce unique d values across the first 20 samples and first candidates do not match the public key | The nonce is not an obvious direct function of hash, r, s, byte-swapped hash, half-hash, xor, add, or subtract variants. | Only revisit if source or hint indicates a nearby nonce relation. |
| Scalar multiplication p256_quasi_reduce segment count bit classifier | analysis/local-model/segment-bit-feature-test.txt; analysis/local-model/window-classifier-bit-test.txt | Local labeled test accuracy around 50.1%; segment counts do not predict masked scalar bits | Per-segment p256 reduction counts around double/add operations are statistically indistinguishable for scalar bits under coordinate/scalar blinding. | Only revisit with richer trace-value features or an external hint that scalar-mul trace is intended. |
| Binary-division ngram nonce bit-length predictor | analysis/local-model/division-ngram-predict-test.txt; analysis/local-model/division-product-feature-correlation.txt | n-gram regression test MAE no better than baseline; count correlations weak | Simple binary-division trace counts/ngrams do not provide reliable nonce bit-length constraints for a lattice attack. | Only revisit with a stronger inversion model for the full binary GCD trace. |
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 Surprise Factor, 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.