Chapter: 4 (TLS)
Duration: 90 minutes
Tools: openssl s_client, Wireshark, SSLKEYLOGFILE, mitmproxy
Points: 10
Objectives
- Capture a TLS 1.3 handshake and decrypt it using session key export
- Identify each TLS record type in the capture
- Compare TLS 1.2 and TLS 1.3 handshake structure
- Intercept HTTPS traffic using mitmproxy and inspect the fabricated certificate
Part 1: Capture and Decrypt a TLS 1.3 Handshake (35 min)
1.1 Capture with session key export
mkdir -p /tmp/tls-lab
# Start a packet capture in background
sudo tcpdump -i eth0 -n "tcp port 443" -w /tmp/tls-lab/tls13.pcap &
TLS_PID=$!
# Connect with TLS 1.3 and export session keys
SSLKEYLOGFILE=/tmp/tls-lab/session-keys.log \
openssl s_client -connect cloudflare.com:443 -tls1_3 \
-msg -brief < /dev/null 2>&1 | tee /tmp/tls-lab/handshake-output.txt
# Stop capture
sudo kill $TLS_PID
1.2 Configure Wireshark to decrypt with session keys
- Open
/tmp/tls-lab/tls13.pcapin Wireshark - Go to Edit -> Preferences -> Protocols -> TLS
- Set (Pre)-Master-Secret log filename to
/tmp/tls-lab/session-keys.log - The encrypted handshake records will now be decrypted
Observe the following TLS records in order:
| TLS Record Type | Wireshark display | Encrypted? |
|---|---|---|
| ClientHello | TLS Client Hello | No (plaintext) |
| ServerHello | TLS Server Hello | No (plaintext) |
| EncryptedExtensions | Encrypted Handshake | Yes (with key: decrypted) |
| Certificate | Certificate | Yes |
| CertificateVerify | Certificate Verify | Yes |
| Finished (server) | Finished | Yes |
| Finished (client) | Finished | Yes |
| Application Data | Application Data | Yes |
Record:
- What cipher suite was negotiated? (Look in the ServerHello)
- What named group (curve) was used for key_share? (Look in ClientHello extensions)
- How many bytes is the server Certificate record?
- What is the server's certificate CN (Common Name) / SAN?
- How many TLS round trips (RTTs) occurred before the first Application Data record?
1.3 Inspect the ClientHello key_share extension
In Wireshark, expand the Client Hello -> Extension: key_share fields.
Record:
- What is the key_share group (e.g., x25519, secp256r1)?
- What is the length (bytes) of the client's DH public key in the key_share?
- RFC 8446 §4.2.8 defines the key_share extension. Based on the captured data, can you identify the key_share_entry structure (group + key_exchange)?
Part 2: TLS 1.2 vs TLS 1.3 Comparison (15 min)
# Capture TLS 1.2 connection (force TLS 1.2)
sudo tcpdump -i eth0 -n "tcp port 443" -w /tmp/tls-lab/tls12.pcap &
SSLKEYLOGFILE=/tmp/tls-lab/session-keys-12.log \
openssl s_client -connect cloudflare.com:443 -tls1_2 \
-msg -brief < /dev/null 2>&1 | tee /tmp/tls-lab/handshake12-output.txt
sudo kill %1
Open /tmp/tls-lab/tls12.pcap in Wireshark (with session keys).
Record (fill in the comparison table):
| Dimension | TLS 1.2 | TLS 1.3 |
|---|---|---|
| Total RTTs before application data | ||
| Is Certificate visible before key exchange? | ||
| Key exchange algorithm in ServerKeyExchange | ||
| Is RSA key exchange an option? | ||
| Is the server Certificate encrypted? | ||
| Number of TLS record types in handshake |
Part 3: Certificate Chain Inspection (15 min)
# Show full certificate chain
openssl s_client -connect cloudflare.com:443 -showcerts < /dev/null 2>&1 > /tmp/tls-lab/cert-chain.txt
# Parse the leaf certificate
openssl s_client -connect cloudflare.com:443 < /dev/null 2>&1 | \
openssl x509 -noout -text | grep -A 5 "Subject:"
# Check OCSP stapling
openssl s_client -connect cloudflare.com:443 -status < /dev/null 2>&1 | \
grep -A 10 "OCSP Response"
Record:
- How many certificates are in the chain (leaf + intermediates)?
- Who is the Issuer of the leaf certificate?
- What is the certificate's notBefore and notAfter?
- Is OCSP stapling present? What does the OCSP response status show?
- What Subject Alternative Names (SANs) does the leaf certificate cover?
Part 4: mitmproxy Interception (25 min)
4.1 Start mitmproxy
# Start mitmproxy in transparent mode
mitmproxy --listen-port 8080 &
# Configure curl to use the proxy
export https_proxy=http://127.0.0.1:8080
4.2 Install mitmproxy CA certificate
# On first run, mitmproxy generates a CA cert at ~/.mitmproxy/mitmproxy-ca-cert.pem
# Trust it for this test session
export SSL_CERT_FILE=~/.mitmproxy/mitmproxy-ca-cert.pem
4.3 Intercept a request
# Make a request through the proxy
curl -v --proxy http://127.0.0.1:8080 \
--cacert ~/.mitmproxy/mitmproxy-ca-cert.pem \
https://httpbin.org/get 2>&1 | head -40
In the mitmproxy terminal: observe the intercepted connection. Press Enter to inspect the request.
4.4 Inspect the fabricated certificate
# View the certificate mitmproxy presents to the client
echo | openssl s_client -connect 127.0.0.1:8080 \
-servername httpbin.org 2>&1 | openssl x509 -noout -text | \
grep -E "(Subject|Issuer|SAN|Not Before|Not After)" | head -20
Record:
- What is the Issuer of the certificate mitmproxy presents? (Compare to the real httpbin.org cert)
- What is the Subject CN of the fabricated certificate?
- What would a user see in their browser if the mitmproxy CA was NOT trusted?
- In what real-world scenario would users have the mitmproxy CA pre-installed without realizing it?
Lab Report
- Why does TLS 1.3 encrypt the Certificate record but TLS 1.2 does not? What security property does this provide?
- Explain how SSLKEYLOGFILE works. Why is this a security risk in production environments?
- A corporate IT department deploys an SSL inspection proxy (similar to mitmproxy) on all employee machines. They pre-install the proxy CA certificate via group policy. A developer's mobile banking app fails to connect through corporate WiFi. What is the likely cause, and what would fix it?
Grading (10 points)
| Item | Points |
|---|---|
| TLS 1.3 capture decrypted; all 7 record types identified | 2 |
| key_share extension correctly analyzed (group, key size) | 2 |
| TLS 1.2 vs 1.3 comparison table complete and accurate | 2 |
| Certificate chain inspection: issuer, OCSP, SANs correctly recorded | 2 |
| mitmproxy: fabricated cert issuer identified; explains browser error | 2 |