Skip to main content

protocol_analysis

Challenge

Imported from local notes.md.

Solution

Original Notes

protocol_analysis

Challenge Summary

  • Given: starting_files/Protocol_Analysis_chals.pdf, which documents nine live protocol-analysis challenges backed by https://protocols.live.
  • Goal: recover every stage flag and store the full set in flag.txt.
  • Constraints: each protocol instance is stateful and destroyed on invalid requests, so the solve path needs to be scripted and replayable.

Initial Recon / Triage

  • Extracted the PDF with pdftotext -layout into artifacts/manual.txt to get the exact protocol scripts and utility descriptions.
  • Probed /model/{n}, /alice, /bob, and the /util/* helpers to confirm live message semantics.
  • Confirmed several implementation quirks that matter for solving:
    • asym_encrypt works with the public key even though the PDF text is misleading.
    • asym_sign requires the private key.
    • asym_decrypt works for generated key pairs but not for entity public keys from the live service.
    • Alice and Bob validate message shape strictly, not just content.

Hypotheses & Approach

  • Hypothesis 1: most stages could be solved by directly following the protocol transcript while substituting chosen names, keys, or ciphertexts where the script is weak.
  • Hypothesis 2: the harder stages would need targeted debugging against the live API because the manual is not a complete description of the implementation.
  • Approach: build one reusable Python client in artifacts/solve_protocol_analysis.py, then add focused debug scripts for stages 6, 8, and 9 where the live behavior diverged from the first-pass interpretation.

Execution Steps (Reproducible)

Setup

Commands:

cd /root/dawg2026CTF/protocol_analysis
pdftotext -layout starting_files/Protocol_Analysis_chals.pdf artifacts/manual.txt
python3 artifacts/solve_protocol_analysis.py

Results:

  • artifacts/manual.txt contains the nine protocol descriptions in plain text.
  • artifacts/solve_protocol_analysis.py now recovers all nine flags in one run.

Stage 1

  • Relay Alice's plaintext request directly to Bob and read the returned flag.

Stage 2

  • Bob only checks the claimed sender name in the request, so send the request directly to Bob with n:charlie in Alice's place.

Stage 3

  • Bob has no initiator on the other side, so send the expected request directly to Bob and collect the response.

Stage 4

  • Ask Alice for the symmetric key and nonce, relay the request to Bob, then decrypt Bob's ciphertext with /util/sym_decrypt.

Stage 5

  • Generate an attacker key pair, swap Alice's advertised public key for the attacker public key, forward the message to Bob, and decrypt Bob's response with the attacker private key.

Stage 6

  • Impersonate mallory with a valid attacker key/cert to get Alice to encrypt her initial message to the attacker.
  • Re-encrypt that plaintext to Bob, forward Bob's reply back through Alice, and relay the recovered nonce back to Bob.
  • The final ciphertext is decrypted with:
    • key = hash_text(n_a + n_b)
    • nonce = first 24 hex characters of that hash
  • This derivation was confirmed in artifacts/debug_stage6.py before being folded into the main solver.

Stage 7

  • Generate a Mallory key pair and cert.
  • Use Bob's returned n_b and Alice's original n_a to sign n:mallory|d:<n_b>|d:<n_a> with the Mallory private key.
  • Send the forged identity bundle to Alice, then relay Alice's final signature to Bob.

Stage 8

  • Start both sides to obtain alice_start and bob_start.
  • Send alice_start|d:<chosen_nonce> to Bob to obtain Bob's nonce.
  • Reflect Bob's own opening tuple into Alice by sending bob_start|d:<bob_nonce> to Alice.
  • Relay Alice's response directly back to Bob.
  • The live implementation accepts the signature only in this form; using alice_start in both legs causes Invalid sig.

Stage 9

  • Generate a Mallory key pair and valid cert.
  • Ask Bob for his doubly encrypted flag blob by sending Alice's initial message.
  • Use Alice as a decryption/re-encryption oracle twice:
    • wrap the Bob ciphertext as d:<cipher>|n:mallory
    • encrypt that wrapper to Alice's public key
    • send k:<mallory_pub>|n:mallory|d:<mallory_cert>|d:<wrapped>|n:alice to Alice
    • decrypt Alice's returned Mallory-layer ciphertext twice with the attacker key
  • The first unwrap exposes Bob's inner Alice-layer ciphertext; repeating the same oracle flow on that inner ciphertext yields the final plaintext flag.
  • The trailing |n:alice field is required. Without it, Alice rejects the crafted message as structurally invalid.

Artifacts Produced

  • artifacts/manual.txt: extracted protocol manual.
  • artifacts/solve_protocol_analysis.py: final end-to-end solver for stages 1 through 9.
  • artifacts/debug_stage6.py: stage-6 key-derivation debugger used to verify the final hash_text-based decrypt.
  • artifacts/debug_stage8.py: stage-8 debugger used to confirm the working reflection path.
  • artifacts/debug_stage9.py: stage-9 oracle debugger used to confirm the required message shape and two-round unwrap.

Flag

stage 1: DawgCTF{PR0T0C0LS_R_3ZPZ}
stage 2: DawgCTF{CH4NG3_0F_PL4N5}
stage 3: DawgCTF{N0_0N3_3LS3_H0M3}
stage 4: DawgCTF{N0T_S0_S3CR3T_K3Y}
stage 5: DawgCTF{C3RT1F13D_1NS3CUR3}
stage 6: DawgCTF{FORM3RLY_S3CUR3}
stage 7: DawgCTF{F33L1NG_1NS3CUR3}
stage 8: DawgCTF{4SK_4ND_U_SH4LL_R3C31V3}
stage 9: DawgCTF{ST4R3_1NTO_TH3_VO1D}