Saturday, April 30, 2011

Sniffing SPDY with ssldump

‹prev | My Chain | next›

Up today, I would like to mess about with SPDY over SSL. Last night, I was able to play around a bit with the eventmachine example implementation in the SPDY gem. I even got to inspect SPDY packets with tcpdump.

The only thing lacking from last night's experiment was SSL—a necessary component of SPDY. Tonight I hope to repeat that success with ssldump.

First, I re-enable the start_tls line in the example eventmachine code. I then open the application which now resides at: https://localhost:10000/.

I get a warning that the certificate is not trusted, but proceed anyway only to get... nothing. The browser never receives a result, though the event machine server kindly reports:
[:SPDY, :connection_closed]
This message indicates that the browser has unbound the connection:
  def unbind
p [:SPDY, :connection_closed]
end
Hrm...

Next up, I actually follow the instructions in the example server and start Chrome with the --use-spdy=ssl. According to the SPDY debugging page, this option "forces Chrome to always use SPDY, even if not negotiated over SSL". That actually does the trick:



Interesting. So if the debugging page is correct, then the reason this works is that, otherwise the connection is made while negotiating over plain text. That is something to investigate another day.

For today, if I am going to sniff SSL packets, I am going to need a server certificate. The whole point of SSL, of course, is to make packet sniffing impossible (or at least prohibitively difficult). It is possible, however, if you have the server's private key. To make one, I use openssl:
➜  spdy git:(master)     openssl genrsa -out key.pem 1024
Generating RSA private key, 1024 bit long modulus
.........................................................++++++
.........................++++++
e is 65537 (0x10001)
➜ spdy git:(master) ✗ openssl req -new -key key.pem -out request.pem
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:US
State or Province Name (full name) [Some-State]:Maryland
Locality Name (eg, city) []:Baltimore
Organization Name (eg, company) [Internet Widgits Pty Ltd]:
Organizational Unit Name (eg, section) []:
Common Name (eg, YOUR name) []:Chris Strom
Email Address []:chris@eeecomputes.com

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:password
An optional company name []:
➜ spdy git:(master) ✗ openssl x509 -req -days 30 -in request.pem -signkey key.pem -out cert.pem
Signature ok
subject=/C=US/ST=Maryland/L=Baltimore/O=Internet Widgits Pty Ltd/CN=Chris Strom/emailAddress=chris@eeecomputes.com
Getting Private key
To use that in eventmachine, I change the start_tls:
  def post_init
@parser = SPDY::Parser.new
@parser.on_headers_complete do |stream_id, associated_stream, priority, headers|
# ...
end

#start_tls
start_tls(:private_key_file => 'key.pem', :cert_chain_file => 'cert.pem', :verify_peer => false)
end
After restarting the server, I verify that I can still access the example SPDY site. Once I am confident that everything still works with the new certificate, I am ready to try out ssldump:
sudo ssldump -k key.pem -i lo -dX
The -k option uses the same private key file that I just generated and that eventmachine is now using. The example server is listening on localhost, so I need to sniff the packets on the loopback interface, lo. The -dX option tells ssldump to dump the packet contents in hex—ideally this is exactly what I did last night using tcpdump, but now I will be able to see an (almost) real SPDY conversation take place.

And, sure enough, that does the trick:
➜  examples git:(master) ✗ sudo ssldump -k key.pem -i lo -dX
New TCP connection #1: localhost.localdomain(35098) <-> localhost.localdomain(10000)
New TCP connection #2: localhost.localdomain(35099) <-> localhost.localdomain(10000)
1 1 0.0071 (0.0071) C>S Handshake
ClientHello
Version 3.1
...
3 7 0.0040 (0.0000) C>S Handshake3 8 0.0040 (0.0000) C>S application_data
---------------------------------------------------------------
00 2c 01 d3 fe 80 02 00 01 01 00 01 24 00 00 00
01 00 00 00 00 00 00 38 ea df a2 51 b2 62 e0 61
60 83 a4 17 06 7b b8 0b 75 30 2c d6 ae 40 17 cd
cd b1 2e b4 35 d0 b3 d4 d1 d2 d7 02 b3 2c 18 f8
50 73 2c 83 9c 67 b0 3f d4 3d 3a 60 07 81 d5 99
eb 40 d4 1b 33 f0 a3 e5 69 06 41 90 8b 75 a0 4e
d6 29 4e 49 ce 80 ab 81 25 03 06 be d4 3c dd d0
60 9d d4 3c a8 a5 bc 28 89 8d 81 23 2f 5f 17 2c
c2 c0 02 ca fc 0c fc a0 14 92 03 62 5a 01 73 a4
81 01 03 5b 2e b0 d4 c9 4f 61 60 76 77 0d 61 60
2b 28 4a 4c cf 4d 44 d2 c5 56 0c 24 73 53 19 58
33 4a 4a 0a 8a 19 98 41 61 c4 a8 cf 00 10 17 22
63 33 a4 fb e6 57 65 e6 e4 24 ea 9b ea 19 28 68
44 18 1a 5a 2b f8 64 e6 95 56 28 54 58 98 c5 9b
99 68 2a 38 02 c3 29 35 3c 35 c9 3b b3 44 df d4
d8 44 cf 18 a8 cc db 23 c4 d7 47 47 21 27 33 3b
55 c1 3d 35 39 3b 5f 53 c1 39 03 58 42 a5 ea 1b
1a e9 01 03 c5 c4 48 cf d0 48 21 38 31 2d b1 28
13 aa 89 81 1d 1a 4f 0c 1c b0 e8 03 00 00 00 ff
ff 00 00 00 ff ff
---------------------------------------------------------------
...
The fourth byte in bold indicates that this is a SYN_STREAM just like I saw last night.

That is a good place to stop for the night. Hopefully I will not have to rely on this technique much while working with SPDY, but it is good to know that it is available if needed.


SPDY protocol (draft 2)

Day #6

No comments:

Post a Comment