0xnhl

Domino Effect

/ Update
2 min read

The Domino Effect (Crypto) - Findings So Far#

Challenge#

  • Name: The Domino Effect
  • Service: nc chals2.apoorvctf.xyz 13337
  • Provided file: domino-challenge.py
  • Expected flag format: apoorvctf{...}

Given Source Analysis#

From domino-challenge.py:

  • Secret per connection:

    • self.secret_message = urandom(16).hex()
    • This is 16 random bytes represented as 32 hex ASCII chars.
    • Effective entropy: 128 bits.
  • Encryption endpoint:

    • Returns CBC ciphertext of the secret with random IV:
    • ct = iv || AES-CBC_k(secret_message)
    • For this secret length (32 bytes), ciphertext body is 32 bytes (2 blocks).
  • Padding oracle endpoint:

    • Decrypts user-provided ciphertext and checks PKCS#7 validity.
    • Returns noisy bit:
      • noisy_response = is_valid ^ (rng.random() > 0.45)
    • So the oracle bit is flipped with probability ~0.55.
  • Query budget:

    • MAX_QUERIES = 10_000
    • Session exits after hitting budget.
  • Flag check:

    • check requires exact equality with secret_message.
    • Wrong guess terminates session immediately.

Remote Verification#

Observed against chals2.apoorvctf.xyz:13337:

  • Banner differs slightly from local text but behavior matches challenge flow.
  • encrypt returns 48-byte total ciphertext (hex length 96):
    • 16-byte IV + 32-byte CBC ciphertext.
  • Invalid ciphertexts fed repeatedly to unpad show noisy boolean output roughly centered near expected bias.

Why This Appears Unsolvable As-Is#

The task structure implies noisy padding-oracle recovery of a 32-char hex secret. However:

  • Unknown data to recover: 128 bits.
  • Oracle noise after best inversion is still high (binary symmetric channel with crossover ~0.45).
  • With 10,000 oracle queries, total extractable information is insufficient to reliably identify a full 128-bit random secret.
  • Multi-session retries do not help because each new connection generates a fresh random secret.
  • Blind forcing check is infeasible; only one wrong check is allowed before session termination.

Tested Dead Ends#

  • Repeated unpad sampling on malformed ciphertext to estimate noise profile.
  • Considering standard CBC padding-oracle byte recovery under noisy majority voting.
  • Session reset / repeated connections for aggregation (not useful due to fresh secret each run).
  • Input-format edge cases (JSON/bad ct) do not expose extra leakage.

Current Conclusion#

Based on provided code and observed remote behavior, this challenge is likely misconfigured or buggy in deployment parameters (noise/limit/secret-size balance).

Most plausible issue: the noise threshold line

noisy_response = is_valid ^ (rng.random() > 0.45)
python

may be unintended for the chosen secret length and query budget.

Suggested Organizer Query#

Ask whether one of these differs on the intended instance:

  1. Noise threshold (e.g., > 0.55 instead of > 0.45),
  2. Query limit (MAX_QUERIES),
  3. Secret generation/length,
  4. Remote source parity with the distributed file.

Repro Notes#

Minimal interaction pattern:

{"option":"encrypt"}
{"option":"unpad","ct":"<hex>"}
{"option":"check","message":"<candidate>"}
json

Any wrong check ends the session.

Domino Effect
https://nahil.xyz/vault/writeups/apoorvctf2026/cryptography/domino-effect/
Author Nahil Rasheed
Published at March 24, 2026
Disclaimer This content is provided strictly for educational purposes only.