# BreizhCTF 2026 - Totally Secure


# Totally Secure

- Difficulty: Easy
- Category: Forensic
- Author: Zlippy

## Description

> A friend set up his own "secure" server using copy-paste and ChatGPT prompts. He's very proud and keeps saying everything is "encrypted" and nobody can see anything on the network.
>
> He shared a network capture and some files recovered from his machine, convinced you won't understand a thing. Your mission: prove him wrong and recover what was traveling through his supposedly unbreakable communications.

Files:
- `traffic.pcapng` — network capture of the client browsing to the server
- `triage_server.tar.gz` — forensic dump of the server (configs, processes, logs, filesystem...)

## Solve

### Step 1 — Map the network traffic

List all TCP conversations from the capture:

```bash
tshark -r traffic.pcapng -q -z conv,tcp
```

One IP stands out: `10.90.35.19`, on the same subnet as the client (`10.90.35.143`). That's the "secure" server. Filtering on it:

```bash
tshark -r traffic.pcapng | grep "10.90.35.19"
```

```
 2987 16.625631355 10.90.35.143 → 10.90.35.19  TCP 74 39470 → 443 [SYN] Seq=0 Win=64240 Len=0 MSS=1460 SACK_PERM TSval=697595375 TSecr=0 WS=128
 2988 16.626162727  10.90.35.19 → 10.90.35.143 TCP 74 443 → 39470 [SYN, ACK] Seq=0 Ack=1 Win=65160 Len=0 MSS=1460 SACK_PERM TSval=759725241 TSecr=697595375 WS=128
 2989 16.626210132 10.90.35.143 → 10.90.35.19  TCP 66 39470 → 443 [ACK] Seq=1 Ack=1 Win=64256 Len=0 TSval=697595376 TSecr=759725241
 2990 16.630862909 10.90.35.143 → 10.90.35.19  TLSv1 226 Client Hello (SNI=pleasedontdothat.local)
 2991 16.631297136  10.90.35.19 → 10.90.35.143 TCP 66 443 → 39470 [ACK] Seq=1 Ack=161 Win=65024 Len=0 TSval=759725246 TSecr=697595381
 2992 16.632325670  10.90.35.19 → 10.90.35.143 TLSv1.2 1092 Server Hello, Certificate, Server Hello Done
 2993 16.632349897 10.90.35.143 → 10.90.35.19  TCP 66 39470 → 443 [ACK] Seq=161 Ack=1027 Win=67200 Len=0 TSval=697595382 TSecr=759725247
 2994 16.633977211 10.90.35.143 → 10.90.35.19  TLSv1.2 412 Client Key Exchange, Change Cipher Spec, Encrypted Handshake Message
 2995 16.637547905  10.90.35.19 → 10.90.35.143 TLSv1.2 145 Change Cipher Spec, Encrypted Handshake Message
 2996 16.637802245 10.90.35.143 → 10.90.35.19  TLSv1.2 203 Application Data
 2997 16.638399440  10.90.35.19 → 10.90.35.143 TLSv1.2 3883 Application Data
 2998 16.638489182 10.90.35.143 → 10.90.35.19  TCP 66 39470 → 443 [ACK] Seq=644 Ack=4923 Win=63488 Len=0 TSval=697595388 TSecr=759725253
 2999 16.638914193 10.90.35.143 → 10.90.35.19  TLSv1.2 123 Encrypted Alert
 3000 16.639032578 10.90.35.143 → 10.90.35.19  TCP 66 39470 → 443 [FIN, ACK] Seq=701 Ack=4923 Win=63488 Len=0 TSval=697595389 TSecr=759725253
 3001 16.639297375  10.90.35.19 → 10.90.35.143 TCP 66 443 → 39470 [FIN, ACK] Seq=4923 Ack=702 Win=64640 Len=0 TSval=759725254 TSecr=697595389
 3002 16.639322289 10.90.35.143 → 10.90.35.19  TCP 66 39470 → 443 [ACK] Seq=702 Ack=4924 Win=63488 Len=0 TSval=697595389 TSecr=759725254
```

The Client Hello reveals the SNI **`pleasedontdothat.local`**.

> **What is SNI?** The SNI (*Server Name Indication*) is a TLS extension that lets the client announce the target domain **before** the session is encrypted. It is **always visible in plaintext** in a network capture, even in TLSv1.3. Here, `pleasedontdothat.local` is readable without decrypting anything.

### Step 2 — Identify the negotiated cipher suite

Inspect the Server Hello:

```bash
tshark -r traffic.pcapng \
  -Y "ip.addr == 10.90.35.19 && tls.handshake.type == 2" \
  -V | grep "Cipher Suite"
```

```
Cipher Suite: TLS_RSA_WITH_AES_128_CBC_SHA (0x002f)
```

This is where everything falls apart. `TLS_RSA_WITH_AES_128_CBC_SHA` uses a **static RSA key exchange**:
- The client generates a pre-master secret
- It encrypts it with the **server's public key** and sends it
- The server decrypts it with its **RSA private key**
- Both sides derive session keys from it

**Direct consequence:** anyone who has the server's private RSA key can decrypt *any past captured session*. This is the opposite of **Perfect Forward Secrecy (PFS)**, provided by ECDHE/DHE where session keys are ephemeral and independent of the private key.

Confirmed in the server's nginx config from the triage:

`triage_server/[root]/etc/nginx/sites-available/chall`:

```nginx
ssl_protocols TLSv1.2;
ssl_ciphers "AES128-SHA:AES256-SHA:AES128-SHA256:AES256-SHA256:DES-CBC3-SHA";
ssl_prefer_server_ciphers on;
ssl_session_cache off;
ssl_session_tickets on;
```

Every cipher listed is an RSA variant with no PFS.

### Step 3 — Retrieve the private key from the triage

The triage contains a copy of the server's filesystem. The nginx SSL files are right there:

```bash
ls triage_server/[root]/etc/nginx/ssl/
# server.crt  server.key
```

The RSA private key is sitting in plain sight in `server.key`.

### Step 4 — Decrypt the TLS traffic

`tshark` natively supports TLS RSA decryption via `tls.keys_list`. It uses the private key to decrypt the pre-master secret from the handshake, then reconstructs the session keys to decrypt the application data.

```bash
cp "triage_server/[root]/etc/nginx/ssl/server.key" .

tshark -r traffic.pcapng \
  -o "tls.keys_list:10.90.35.19,443,http,server.key" \
  -Y "ip.addr == 10.90.35.19 && http.response" \
  -T fields -e http.file_data \
  | python3 -c "import sys; print(bytes.fromhex(sys.stdin.read().strip()).decode())"
```

- `-o "tls.keys_list:IP,PORT,PROTO,FILE"` — provides the RSA key to tshark for TLS decryption
- `-Y "... && http.response"` — filters HTTP packets after decryption (without the key, tshark only sees opaque `Application Data`)
- `-T fields -e http.file_data` — extracts only the HTTP response body
- The Python pipe converts hex output from tshark into readable text

The decrypted HTTP response is an HTML page containing:

```html
<div class="seccccret-value">BZHCTF{Pl34se_D0nT_D0_Th4t_1n_Pr0d!!!!}</div>
```

> **Takeaway:** The problem is not TLS itself, but the **misconfiguration**. Using RSA cipher suites without PFS means the server's private key is a master key protecting all past and future traffic. A single leak (server compromise, forensic triage) retroactively compromises every captured session. The fix: use TLS 1.3 or enforce ECDHE/DHE-only cipher suites.

**Flag : `BZHCTF{Pl34se_D0nT_D0_Th4t_1n_Pr0d!!!!}`**

