Signs of Triviality

Opinions, mostly my own, on the importance of being and other things.
[homepage] [index] [jschauma@netmeister.org] [@jschauma] [RSS]

Capturing specific SSL and TLS version packets using tcpdump(8)

March 8th, 2019

In the process of deprecating old and insecure versions of TLS, I wanted to capture all packets that were of TLS version < 1.2, including SSLv3 and even SSLv2. It turned out to be surprisingly more annoying than I had originally thought, as the TLS version is identified in different places for different packets. What's more, the version identification is effectively off by one due to the historical versioning of SSLv3 as 0x0300.

The filter I ended up with uses the logic described below:

First, we have to identify the correct offset for where the SSL/TLS payload starts. To do this, we borrow from this stackoverflow answer and note that the first nibble of the 13th byte * 4 is the size of the TCP header, becoming tcp[12] & 0xf0 >> 2. That is, the first byte of the payload is then "tcp[(tcp[12] & 0xf0 >> 2)]".

Next, reading up on Traffic Analysis of an SSL/TLS Session, we note that the first byte of the TLS Record indicates the record type:

0x14 Change Cipher Spec
16:54:10.217173 IP 166.84.7.99.443 > 172.135.142.201.59501: Flags [P.], seq 3536:3587, ack 356 [...]
        0x0000:  4500 0067 681b 4000 3706 f26d a654 0763  E..gh.@.7..m.T.c
        0x0010:  ac87 8ec9 01bb e86d 7ffd 3b9c d073 594a  .......m..;..sYJ
        0x0020:  8018 1065 e554 0000 0101 080a 0000 0003  ...e.T..........
        0x0030:  32ae 2b22 1403 0300 0101 1603 0300 28f8  2.+"..........(.
        0x0040:  2eb2 a5cd fc17 83c0 6861 cc45 3f88 2640  ........ha.E?.&@
        0x0050:  243e ef1d 609d 9d4e ef20 6e15 84bf 7138  $>..`..N..n...q8
        0x0060:  8032 ac2f 32cc 5e                        .2./2.^
0x15 Encrypted Alert
16:54:10.252569 IP 172.135.142.201.59501 > 166.84.7.99.443: Flags [P.], seq 467:498, ack 9412, [...]
        0x0000:  4500 0053 0000 4000 4006 519d ac87 8ec9  E..S..@.@.Q.....
        0x0010:  a654 0763 e86d 01bb d073 59b9 7ffd 5290  .T.c.m...sY...R.
        0x0020:  8018 0800 d1cd 0000 0101 080a 32ae 2b63  ............2.+c
        0x0030:  0000 0003 1503 0300 1a00 0000 0000 0000  ................
        0x0040:  023a 52a5 1166 1e1f c4c5 f89f d430 a81c  .:R..f.......0..
        0x0050:  daac a4 
0x16 Handshake
16:54:09.379121 IP 172.135.142.201.59501 > 166.84.7.99.443: Flags [P.], seq 1:230, ack 1, [...]
        0x0000:  4500 0119 0000 4000 4006 50d7 ac87 8ec9  E.....@.@.P.....
        0x0010:  a654 0763 e86d 01bb d073 57e7 7ffd 2dcd  .T.c.m...sW...-.
        0x0020:  8018 0804 499e 0000 0101 080a 32ae 27ff  ....I.......2.'.
        0x0030:  0000 0001 1603 0100 e001 0000 dc03 03e0  ................
        0x0040:  9241 f532 7405 d2b3 55eb 070c d202 56c0  .A.2t...U.....V.
        0x0050:  5798 de45 0196 864c 58af 381f 5ce6 2e00  W..E...LX.8.\...
        0x0060:  0054 cca9 cca8 ccaa c030 c02c c028 c024  .T.......0.,.(.$
        0x0070:  c014 c00a 009f 006b 0039 ff85 00c4 0088  .......k.9......
        0x0080:  0081 009d 003d 0035 00c0 0084 c02f c02b  .....=.5...../.+
        0x0090:  c027 c023 c013 c009 009e 0067 0033 00be  .'.#.......g.3..
        0x00a0:  0045 009c 003c 002f 00ba 0041 c012 c008  .E...<./...A....
        0x00b0:  0016 000a 00ff 0100 005f 0000 0017 0015  ........._......
        0x00c0:  0000 1277 7777 2e6e 6574 6d65 6973 7465  ...www.netmeiste
        0x00d0:  722e 6f72 6700 0b00 0201 0000 0a00 0800  r.org...........
        0x00e0:  0600 1d00 1700 1800 0d00 1c00 1a06 0106  ................
        0x00f0:  03ef ef05 0105 0304 0104 03ee eeed ed03  ................
        0x0100:  0103 0302 0102 0300 1000 0e00 0c02 6832  ..............h2
        0x0110:  0868 7474 702f 312e 31                   .http/1.1
0x17 Application Data
16:54:10.217805 IP 172.135.142.201.59501 > 166.84.7.99.443: Flags [P.], seq 356:467, ack 3587 [...]
        0x0000:  4500 00a3 0000 4000 4006 514d ac87 8ec9  E.....@.@.QM....
        0x0010:  a654 0763 e86d 01bb d073 594a 7ffd 3bcf  .T.c.m...sYJ..;.
        0x0020:  8018 0800 68d8 0000 0101 080a 32ae 2b41  ....h.......2.+A
        0x0030:  0000 0003 1703 0300 6a00 0000 0000 0000  ........j.......
        0x0040:  018a 5d6f 205d 01a9 81ce ae93 b992 24f4  ..]o.]........$.
        0x0050:  0dad 3ebf bab2 22a2 5949 dd75 13e8 527b  ..>...".YI.u..R{
        0x0060:  1181 1a32 7c2e 3873 6f2d 8c15 3e4b 5a1c  ...2|.8so-..>KZ.
        0x0070:  af5a 0c4a 25ed 2740 490d a384 1d00 9e6a  .Z.J%.'@I......j
        0x0080:  2556 408b a85b 6af1 6381 cac1 bdf1 ea0d  %V@..[j.c.......
        0x0090:  b675 c3f6 b6ed 38ee 1a01 9bb1 581f 203c  .u....8.....X..<
        0x00a0:  bd59 4a                                  .YJ

So if we're talking SSLv3 or TLS, then the second and third byte of the TLS Record indicate the version in use:

0x0300 SSLv3
0x0301 TLS 1.0
0x0302 TLS 1.1
0x0303 TLS 1.2
0x0304 TLS 1.3

So far, so good. But there's one more caveat: for presumably backwards compatibility and to appease assumed broken devices, if the packet is a handshake message (first byte == 0x16), then the record layer handshake version will be 0x0301 even though you may be speaking TLS 1.2. For handshake messages, you then need to look at the handshake protocol record, which begins at byte 6, where we find one byte indiciating the handshake type, three bytes length, and two bytes again describing the TLS version as above (byte 10 = 0x03, byte 11 = 0x03 for TLS 1.2 etc.). This is illustrated in the blue 0x0303 versus the orange 0x0301 in the above example. (For non-handshake packets, the TLS record version is accurate. And of course see this amazing annotation of a TLS connection for all the details.)

Wireshark screenshot showing TLS versions in the
handshake

Finally, for SSLv2 (as described here), the first couple of bytes are the length followed by the type of the message, followed by the SSL version number. That is, here we are not in a TLS (or SSLv3) message and the fourth and fifth byte are 0x0002:

17:48:20.036299 IP 172.135.142.201.59841 > 166.84.7.99.4433: Flags [P.],
seq 2293775046:2293775097, ack 474282231, [...]
        0x0000:  4500 0067 0000 4000 4006 5189 ac87 8ec9  E..g..@.@.Q.....
        0x0010:  a654 0763 e9c1 1151 88b8 3ac6 1c44 f8f7  .T.c...Q..:..D..
        0x0020:  8018 0804 762e 0000 0101 080a 32ca f346  ....v.......2..F
        0x0030:  0000 0001 8031 0100 0200 1800 0000 1007  .....1..........
        0x0040:  00c0 0500 8003 0080 0100 8008 0080 0600  ................
        0x0050:  4004 0080 0200 80bb 1486 c409 7b3d 969d  @...........{=..
        0x0060:  94db 4878 1c64 b5                        ..Hx.d.

Putting all this together, I then came up with:

  1. if first byte is 0x14, 0x15, or 0x17 and the next byte is 0x03, then we're talking TLS, but are not in the handshake. The TLS version is then determined by the third byte:
    (((tcp[((tcp[12] & 0xf0) >> 2)] = 0x14) ||
       (tcp[((tcp[12] & 0xf0) >> 2)] = 0x15) ||
       (tcp[((tcp[12] & 0xf0) >> 2)] = 0x17)) &&
      (tcp[((tcp[12] & 0xf0) >> 2)+1] = 0x03) &&
      (tcp[((tcp[12] & 0xf0) >> 2)+2] < 0x03)))
  2. if first byte is 0x16 and the next byte is 0x03, we are in the TLS handshake and we get the accurate TLS version from the 11th byte:
    ((tcp[((tcp[12] & 0xf0) >>2)] = 0x16) &&
     (tcp[((tcp[12] & 0xf0) >> 2)+1] = 0x03) &&
     (tcp[((tcp[12] & 0xf0) >> 2)+9] = 0x03) &&
     (tcp[((tcp[12] & 0xf0) >> 2)+10] < 0x03))
  3. finally, SSLv2: we exclude the valid TLS packets, then check for 0x0002:
    (((tcp[((tcp[12] & 0xf0) >> 2)] < 0x14) ||
     (tcp[((tcp[12] & 0xf0) >> 2)] > 0x18)) &&
     (tcp[((tcp[12] & 0xf0) >> 2)+3] = 0x00) &&
     (tcp[((tcp[12] & 0xf0) >> 2)+4] = 0x02))
  4. OR all those together on a single line to yield this expression:

     
    (((tcp[((tcp[12] & 0xf0) >> 2)] = 0x14) || (tcp[((tcp[12] & 0xf0) >> 2)] = 0x15) || (tcp[((tcp[12] & 0xf0) >> 2)] = 0x17)) && (tcp[((tcp[12] & 0xf0) >> 2)+1] = 0x03 && (tcp[((tcp[12] & 0xf0) >> 2)+2] < 0x03)))   ||   (tcp[((tcp[12] & 0xf0) >> 2)] = 0x16) && (tcp[((tcp[12] & 0xf0) >> 2)+2] < 0x03) && (tcp[((tcp[12] & 0xf0) >> 2)+9] = 0x03) && (tcp[((tcp[12] & 0xf0) >> 2)+10] < 0x03)    ||    (((tcp[((tcp[12] & 0xf0) >> 2)] < 0x14) || (tcp[((tcp[12] & 0xf0) >> 2)] > 0x18)) && (tcp[((tcp[12] & 0xf0) >> 2)+3] = 0x00) && (tcp[((tcp[12] & 0xf0) >> 2)+4] = 0x02))
     
    
    

    The annotated file is a bit more usable, though. You can use it with tcpdump(8) like so:

    $ sudo tcpdump "$(grep -v '^#' sslfilter)"

    March 8th, 2019


    See also:


[Writing Consistent Tools] [Index]