Protocol Analysis 4: Real Security!
Protocol Analysis 4: Real Security!
Platform: Metactf | Category: Cryptography | Type: Challenge | Difficulty: Easy | OS: NA | Author: D3v0o0Nu11 | Date: 2026-04-10 | Status: Solved Techniques: chosen_key_attack, chosen_nonce_attack, ciphertext_decryption, direct_bob_trigger, oracle_recon
Summary
Task: a protocol challenge uses attacker-provided symmetric encryption parameters inside an Alice-to-Bob request, and the live service accepts a slightly different string than the PDF shows. Solution: query Alice to recover the exact accepted text, choose a known key and nonce, send them directly to Bob, then decrypt the returned ciphertext with the utility endpoint.
Recon
Port scan
bash read-only • copy allowednmap -p- -sV -sC <TARGET> --min-rate 1000 -Pn
| Port | Service | Version | Notes |
|---|---|---|---|
| <PORT> | <SVC> | <VER> | <notes> |
Enumeration highlights
- Event:
metactf| ID:20260410_metactf_protocol_analysis_4_real_security - Tags: protocol_analysis, plaintext_protocol, alice_and_bob, symmetric_encryption, chosen_key, chosen_nonce
- Indicators: the service exposes both protocol endpoints and a symmetric decrypt utility, Alice reveals the exact accepted request text after any syntactically valid probe, Bob accepts attacker-supplied key and nonce material, the live accepted string differs slightly from the visible PDF text, the accepted request contains the typo symetric
- Source:
20260410_metactf_protocol_analysis_4_real_security.md
Foothold
Vulnerability / Misconfiguration
- chosen_key_attack
- chosen_nonce_attack
- ciphertext_decryption
- direct_bob_trigger
- oracle_recon
bash read-only • copy allowed<command>
Exploitation
- See original writeup content for detailed exploitation.
Privilege Escalation
Enumeration
bash read-only • copy allowedsudo -l find / -perm -4000 2>/dev/null getcap -r / 2>/dev/null cat /etc/crontab ps aux
Exploitation
- N/A for challenge-type writeup; see exploitation above.
- Flag obtained via challenge solve.
bash read-only • copy allowed<command>
Flags
| Flag | Location | Value |
|---|---|---|
| flag | REDACTED |
Key Takeaways / Lessons
- chosen_key_attack
- chosen_nonce_attack
- ciphertext_decryption
- direct_bob_trigger
- oracle_recon
- Tags: protocol_analysis, plaintext_protocol, alice_and_bob, symmetric_encryption, chosen_key, chosen_nonce
Original Writeup
<details><summary>Click to expand original content</summary>Protocol Analysis 4: Real Security! — metactf
Description
The protocol manual describes an Alice-to-Bob request for sending the flag encrypted under a symmetric key and nonce.
English summary: the challenge provides a protocol PDF and a live service. The important twist is that the real accepted Bob request is not exactly the visible PDF wording, so we first need to recover Alice's actual message from the service and then abuse Bob's trust in attacker-chosen key material.
Summary
This challenge is a broken secure-channel design. Alice's first message includes a symmetric key and nonce, and Bob blindly trusts those values when encrypting the flag. By querying /alice with any syntactically valid placeholder such as t:hi, we learn the exact string Bob will accept, including the typo symetric. We can then talk to Bob directly, choose our own known key and nonce, receive the ciphertext, and decrypt it with /util/sym_decrypt.
Recon
The shared manual explains the protocol shape, but the live service is stricter than the visible PDF text: the accepted string must match Alice's real output exactly.
Sending any syntactically valid placeholder message to /alice, for example t:hi, causes Alice to reveal her actual first message:
text read-only • copy allowedt:Hello|n:bob|t:this is|n:alice|t:send me the flag encrypted under this symetric key and nonce|k:<key>|d:<nonce>
The critical detail is the typo: the accepted request contains symetric, not symmetric. The text also has to be exactly send me the flag encrypted under this symetric key and nonce.
Vulnerability
Bob uses key and nonce values taken directly from the incoming message. There is no authentication, no trusted key exchange, and no restriction that those parameters must come from a legitimate Alice-controlled process. That means the attacker can choose a known symmetric key and nonce, send the full forged Alice request directly to Bob, and force Bob to encrypt the flag under attacker-controlled parameters.
This turns the protocol into a chosen-key / chosen-nonce attack: confidentiality fails because the recipient encrypts sensitive data with values fully controlled by the attacker.
Exploitation
I used the following known values:
- key =
00repeated 32 bytes - nonce =
11repeated 12 bytes
That gives this working Bob request:
text read-only • copy allowedt:Hello|n:bob|t:this is|n:alice|t:send me the flag encrypted under this symetric key and nonce|k:0000000000000000000000000000000000000000000000000000000000000000|d:111111111111111111111111
Bob responds in the format:
text read-only • copy allowedt:here it is|d:<ciphertext>
Finally, submit that ciphertext to /util/sym_decrypt using the same chosen key and nonce. Because we selected the parameters ourselves, the utility cleanly decrypts the ciphertext and returns the flag.
Working solver:
</details>python read-only • copy allowed#!/usr/bin/env python3 import requests BASE = "https://protocols.live" KEY = "00" * 32 NONCE = "11" * 12 def main(): session = requests.Session() create = session.post(f"{BASE}/model/4") create.raise_for_status() conn_id = create.json()["conn_id"] alice = session.post( f"{BASE}/alice", json={"conn_id": conn_id, "content": "t:hi"}, ) alice.raise_for_status() print("Alice says:", alice.json()["content"]) forged = ( "t:Hello|n:bob|t:this is|n:alice|" "t:send me the flag encrypted under this symetric key and nonce|" f"k:{KEY}|d:{NONCE}" ) bob = session.post( f"{BASE}/bob", json={"conn_id": conn_id, "content": forged}, ) bob.raise_for_status() bob_content = bob.json()["content"] print("Bob says:", bob_content) ciphertext = bob_content.split("|d:", 1)[1] dec = session.post( f"{BASE}/util/sym_decrypt", json={"conn_id": conn_id, "content": f"k:{KEY}|d:{NONCE}|d:{ciphertext}"}, ) dec.raise_for_status() print(dec.json()) if __name__ == "__main__": main()
signed by XESXOR