Skip to content

Commit 683ac73

Browse files
committed
Decrypting TLS 1.2 using a known master secret
1 parent aaf07b4 commit 683ac73

File tree

3 files changed

+153
-29
lines changed

3 files changed

+153
-29
lines changed

doc/notebooks/tls/notebook3_tls_compromised.ipynb

Lines changed: 151 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,17 @@
44
"cell_type": "markdown",
55
"metadata": {},
66
"source": [
7-
"# The lack of PFS: a danger to privacy"
7+
"# The lack of PFS: a danger to privacy\n",
8+
"\n",
9+
"With TLS 1.2 and earlier, some cipher suites do not provide Perfect Forward Secrecy. Without this property, an attacker compromising the server private key can easily decrypt TLS traffic.\n",
10+
"\n",
11+
"In the following example, Scapy is used to decrypt a comunication made without PFS using the ciphersuite `TLS_RSA_WITH_AES_128_CBC_SHA`, giving the server private key stored in `raw_data/pki/srv_key.pem`."
812
]
913
},
1014
{
1115
"cell_type": "code",
1216
"execution_count": null,
13-
"metadata": {
14-
"collapsed": true
15-
},
17+
"metadata": {},
1618
"outputs": [],
1719
"source": [
1820
"from scapy.all import *\n",
@@ -22,9 +24,7 @@
2224
{
2325
"cell_type": "code",
2426
"execution_count": null,
25-
"metadata": {
26-
"collapsed": false
27-
},
27+
"metadata": {},
2828
"outputs": [],
2929
"source": [
3030
"record1_str = open('raw_data/tls_session_compromised/01_cli.raw', 'rb').read()\n",
@@ -36,7 +36,6 @@
3636
"cell_type": "code",
3737
"execution_count": null,
3838
"metadata": {
39-
"collapsed": false,
4039
"scrolled": true
4140
},
4241
"outputs": [],
@@ -49,23 +48,19 @@
4948
{
5049
"cell_type": "code",
5150
"execution_count": null,
52-
"metadata": {
53-
"collapsed": true
54-
},
51+
"metadata": {},
5552
"outputs": [],
5653
"source": [
57-
"# Suppose we possess the private key of the server\n",
58-
"# Try registering it to the session\n",
59-
"#key = PrivKey('raw_data/pki/srv_key.pem')\n",
60-
"#record2.tls_session.server_rsa_key = key"
54+
"# Supposing that the private key of the server was stolen,\n",
55+
"# the traffic can be decoded by registering it to the Scapy TLS session\n",
56+
"key = PrivKey('raw_data/pki/srv_key.pem')\n",
57+
"record2.tls_session.server_rsa_key = key"
6158
]
6259
},
6360
{
6461
"cell_type": "code",
6562
"execution_count": null,
66-
"metadata": {
67-
"collapsed": false
68-
},
63+
"metadata": {},
6964
"outputs": [],
7065
"source": [
7166
"record3_str = open('raw_data/tls_session_compromised/03_cli.raw', 'rb').read()\n",
@@ -76,9 +71,7 @@
7671
{
7772
"cell_type": "code",
7873
"execution_count": null,
79-
"metadata": {
80-
"collapsed": false
81-
},
74+
"metadata": {},
8275
"outputs": [],
8376
"source": [
8477
"record4_str = open('raw_data/tls_session_compromised/04_srv.raw', 'rb').read()\n",
@@ -89,34 +82,163 @@
8982
{
9083
"cell_type": "code",
9184
"execution_count": null,
92-
"metadata": {
93-
"collapsed": false
94-
},
85+
"metadata": {},
9586
"outputs": [],
9687
"source": [
88+
"# This is the first TLS Record containing user data. If decryption works,\n",
89+
"# you should see the string \"To boldly go where no man has gone before...\" in plaintext.\n",
9790
"record5_str = open('raw_data/tls_session_compromised/05_cli.raw', 'rb').read()\n",
9891
"record5 = TLS(record5_str, tls_session=record4.tls_session.mirror())\n",
9992
"record5.show()"
10093
]
94+
},
95+
{
96+
"cell_type": "markdown",
97+
"metadata": {},
98+
"source": [
99+
"# Decrypting TLS Traffic Protected with PFS\n",
100+
"\n",
101+
"When PFS is in action, the only way to break TLS 1.2 is to possess decryption keys. They can be retrieved by dumping the process memory, or making the TLS library to write then into a [NSS Key Log](https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/Key_Log_Format) (as allowed by OpenSSL, Chrome or Firefox).\n",
102+
"\n",
103+
"The data used in the following examples was retrieved the following commands:\n",
104+
"```\n",
105+
"cd doc/notebooks/tls/raw_data/\n",
106+
"\n",
107+
"# Start a TLS 1.12 Server using the s_server\n",
108+
"sudo openssl s_server -accept localhost:443 -cert pki/srv_cert.pem -key pki/srv_key.pem -WWW -tls1_2\n",
109+
"\n",
110+
"# Sniff the network and write packets to a file\n",
111+
"sudo tcpdump -i lo -w tls_nss_example.pcap port 443\n",
112+
"\n",
113+
"# Connect to the server using s_client and retrieve the secrets.txt file\n",
114+
"openssl s_client -connect localhost:443 -keylogfile tls_nss_example.keys.txt\n",
115+
"```\n",
116+
"\n",
117+
"## Decrypt a PCAP files\n",
118+
"\n",
119+
"Scapy can parse NSS Key logs, and use the cryptographic material to decrypt TLS traffic from a pcap file."
120+
]
121+
},
122+
{
123+
"cell_type": "code",
124+
"execution_count": null,
125+
"metadata": {},
126+
"outputs": [],
127+
"source": [
128+
"load_layer(\"tls\")\n",
129+
"\n",
130+
"conf.tls_session_enable = True\n",
131+
"conf.tls_nss_filename = \"raw_data/tls_nss_example.keys.txt\"\n",
132+
"\n",
133+
"packets = rdpcap(\"raw_data/tls_nss_example.pcap\")"
134+
]
135+
},
136+
{
137+
"cell_type": "code",
138+
"execution_count": null,
139+
"metadata": {},
140+
"outputs": [],
141+
"source": [
142+
"# Display the HTTP GET query\n",
143+
"packets[11][TLS].show()"
144+
]
145+
},
146+
{
147+
"cell_type": "code",
148+
"execution_count": null,
149+
"metadata": {},
150+
"outputs": [],
151+
"source": [
152+
"# Display the answer containing the secret\n",
153+
"packets[13][TLS].show()"
154+
]
155+
},
156+
{
157+
"cell_type": "markdown",
158+
"metadata": {},
159+
"source": [
160+
"## Decrypt Manually\n",
161+
"\n",
162+
"Internally, the `conf.tls_session_enable` parameter makes Scapy follows TCP records, such as Client Hello or Server Hello, and updates `tlsSession` objects.\n",
163+
"\n",
164+
"Scapy inner behavior is illustrated by the following example."
165+
]
166+
},
167+
{
168+
"cell_type": "code",
169+
"execution_count": null,
170+
"metadata": {},
171+
"outputs": [],
172+
"source": [
173+
"# Read packets from a pcap\n",
174+
"load_layer(\"tls\")\n",
175+
"\n",
176+
"packets = rdpcap(\"raw_data/tls_nss_example.pcap\")\n",
177+
"\n",
178+
"# Load the keys from a NSS Key Log\n",
179+
"nss_keys = load_nss_keys(\"raw_data/tls_nss_example.keys.txt\")"
180+
]
181+
},
182+
{
183+
"cell_type": "code",
184+
"execution_count": null,
185+
"metadata": {},
186+
"outputs": [],
187+
"source": [
188+
"# Parse the Client Hello message from its raw bytes. This configures a new tlsSession object\n",
189+
"client_hello = TLS(raw(packets[3][TLS]))\n",
190+
"\n",
191+
"# Parse the Server Hello message, using the mirrored client_hello tlsSession object\n",
192+
"server_hello = TLS(raw(packets[5][TLS]), tls_session=client_hello.tls_session.mirror())\n",
193+
"\n",
194+
"# Configure the TLS master secret retrieved from the NSS Key Log\n",
195+
"server_hello.tls_session.master_secret = nss_keys[\"CLIENT_RANDOM\"][\"Secret\"]\n",
196+
"\n",
197+
"# Parse remaining TLS messages\n",
198+
"client_finished = TLS(raw(packets[7][TLS]), tls_session=server_hello.tls_session.mirror())\n",
199+
"server_finished = TLS(raw(packets[9][TLS]), tls_session=client_finished.tls_session.mirror())"
200+
]
201+
},
202+
{
203+
"cell_type": "code",
204+
"execution_count": null,
205+
"metadata": {},
206+
"outputs": [],
207+
"source": [
208+
"# Display the HTTP GET query\n",
209+
"http_query = TLS(raw(packets[11][TLS]), tls_session=server_finished.tls_session.mirror())\n",
210+
"http_query.show()"
211+
]
212+
},
213+
{
214+
"cell_type": "code",
215+
"execution_count": null,
216+
"metadata": {},
217+
"outputs": [],
218+
"source": [
219+
"# Display the answer containing the secret\n",
220+
"http_response = TLS(raw(packets[13][TLS]), tls_session=http_query.tls_session.mirror())\n",
221+
"http_response.show()"
222+
]
101223
}
102224
],
103225
"metadata": {
104226
"kernelspec": {
105-
"display_name": "Python 2",
227+
"display_name": "Python 3 (ipykernel)",
106228
"language": "python",
107-
"name": "python2"
229+
"name": "python3"
108230
},
109231
"language_info": {
110232
"codemirror_mode": {
111233
"name": "ipython",
112-
"version": 2
234+
"version": 3
113235
},
114236
"file_extension": ".py",
115237
"mimetype": "text/x-python",
116238
"name": "python",
117239
"nbconvert_exporter": "python",
118-
"pygments_lexer": "ipython2",
119-
"version": "2.7.13"
240+
"pygments_lexer": "ipython3",
241+
"version": "3.9.1"
120242
}
121243
},
122244
"nbformat": 4,
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# SSL/TLS secrets log file, generated by OpenSSL
2+
CLIENT_RANDOM c43c799f04ad31e397ee4fe14c8819a19bf5951bbc545cada407c6c7589e60ab b599798159244555ddd10d80b5552a37d327fd6e661f3520194c28ef6e8bb0af6e3fb4d4f9945a61e83a41f2345fa27a
4.11 KB
Binary file not shown.

0 commit comments

Comments
 (0)