tintinweb / scapy-ssl_tls

SSL/TLS layers for scapy the interactive packet manipulation tool
GNU General Public License v2.0
419 stars 156 forks source link

Dissect TLSCertificate() payload with scapy built-in x509Cert #14

Closed tintinweb closed 9 years ago

tintinweb commented 9 years ago

TLSCertificate() payload was not dissected and ended up being raw data. Luckily scapy provides an ASN1.BER parser with definitions for x509Cert that we can try to apply in order to nicely show certificate fields. This also allows us to easily pull the pubkey from the certificate without having to look for certain ASN.1 sequences which should be less error prone as long as the received raw certificate data is complete. If it is not complete x509Cert type will resolve to Raw(), therefore we only gain information and do not lose anything.

TLSCertificate.data is now of type x509Cert but can be accessed as if it was a raw string if required.

Also let the old pubkey extraction code in place as a fallback as this method should also be able to extract the pubkey from incomplete/erroneous and usually too short ASN.1 sequences (recv. buffer!).

old output:

###[ TLS Record ]###
  content_type= handshake
  version   = TLS_1_0
  length    = None
###[ TLS Handshake ]###
     type      = certificate
     length    = None
###[ TLS Certificate List ]###
        length    = None
        \certificates\
         |###[ TLS Certificate ]###
         |  length    = None
         |  data      = '........'

new output:

###[ TLS Record ]###
  content_type= handshake
  version   = TLS_1_0
  length    = None
###[ TLS Handshake ]###
     type      = certificate
     length    = None
###[ TLS Certificate List ]###
        length    = None
        \certificates\
         |###[ TLS Certificate ]###
         |  length    = None
         |  \data      \
         |   |###[ X509Cert ]###
         |   |  version   = <ASN1_INTEGER[2L]>
         |   |  sn        = <ASN1_INTEGER[13397879971383713459L]>
         |   |  sign_algo = <ASN1_OID['.1.2.840.113549.1.1.5']>
         |   |  sa_value  = <ASN1_NULL[0L]>
         |   |  \issuer    \
         |   |   |###[ X509RDN ]###
         |   |   |  oid       = <ASN1_OID['.2.5.4.6']>
         |   |   |  value     = <ASN1_PRINTABLE_STRING['UK']>
         |   |   |###[ X509RDN ]###
         |   |   |  oid       = <ASN1_OID['.2.5.4.10']>
         |   |   |  value     = <ASN1_BADTAG[<ASN1_DECODING_ERROR['\x0c\rOpenSSL Group']{{Codec <ASN1Codec BER[1]> not found for tag <ASN1Tag UTF8_STRING[12]>}}>]>
         |   |   |###[ X509RDN ]###
         |   |   |  oid       = <ASN1_OID['.2.5.4.11']>
         |   |   |  value     = <ASN1_BADTAG[<ASN1_DECODING_ERROR['\x0c\x19FOR TESTING PURPOSES ONLY']{{Codec <ASN1Codec BER[1]> not found for tag <ASN1Tag UTF8_STRING[12]>}}>]>
         |   |   |###[ X509RDN ]###
         |   |   |  oid       = <ASN1_OID['.2.5.4.3']>
         |   |   |  value     = <ASN1_BADTAG[<ASN1_DECODING_ERROR['\x0c\x1cOpenSSL Test Intermediate CA']{{Codec <ASN1Codec BER[1]> not found for tag <ASN1Tag UTF8_STRING[12]>}}>]>
         |   |  not_before= <ASN1_UTC_TIME['111208140148Z']>
         |   |  not_after = <ASN1_UTC_TIME['211016140148Z']>
         |   |  \subject   \
         |   |   |###[ X509RDN ]###
         |   |   |  oid       = <ASN1_OID['.2.5.4.6']>
         |   |   |  value     = <ASN1_PRINTABLE_STRING['UK']>
         |   |   |###[ X509RDN ]###
         |   |   |  oid       = <ASN1_OID['.2.5.4.10']>
         |   |   |  value     = <ASN1_BADTAG[<ASN1_DECODING_ERROR['\x0c\rOpenSSL Group']{{Codec <ASN1Codec BER[1]> not found for tag <ASN1Tag UTF8_STRING[12]>}}>]>
         |   |   |###[ X509RDN ]###
         |   |   |  oid       = <ASN1_OID['.2.5.4.11']>
         |   |   |  value     = <ASN1_BADTAG[<ASN1_DECODING_ERROR['\x0c\x19FOR TESTING PURPOSES ONLY']{{Codec <ASN1Codec BER[1]> not found for tag <ASN1Tag UTF8_STRING[12]>}}>]>
         |   |   |###[ X509RDN ]###
         |   |   |  oid       = <ASN1_OID['.2.5.4.3']>
         |   |   |  value     = <ASN1_BADTAG[<ASN1_DECODING_ERROR['\x0c\x10Test Server Cert']{{Codec <ASN1Codec BER[1]> not found for tag <ASN1Tag UTF8_STRING[12]>}}>]>
         |   |  pubkey_algo= <ASN1_OID['.1.2.840.113549.1.1.1']>
         |   |  pk_value  = <ASN1_NULL[0L]>
         |   |  pubkey    = <ASN1_BIT_STRING['\x000\x82\x01\n\x02\x82\x01\x01\x00\xf3\x84\xf3\x926\xdc\xb2F\xcafz\xe5)\xc5\xf3I("\xd3\xb9\xfe\xe0\xde\xe48\xce\xee"\x1c\xe9\x91;\x94\xd0r/\x87\x85YKf\xb1\xc5\xf5z\x85]\xc2\x0f\xd3.)X6\xccHk\xa2\xa2\xb5&\xceg\xe2G\xb6\xdfI\xd2?\xfa\xa2\x10\xb7\xc2\x97D~\x874mm\xf2\x8b\xb4U+\xd6!\xdeSK\x90\xea\xfd\xea\xf985+\xf4\xe6\x9a\x0e\xf6\xbb\x12\xab\x87!\xc3/\xbc\xf4\x06\xb8\x8f\x8e\x10\x07\'\x95\xe5B\xcb\xd1\xd5\x10\x8c\x92\xac\xee\x0f\xdc#H\x89\xc9\xc6\x93\x0c"\x02\xe7t\xe7%\x00\xab\xf8\x0f\\\x10\xb5\x85;f\x94\xf0\xfbMW\x06U!"%\xdb\xf3\xaa\xa9`\xbfM\xaay\xd1\xab\x92H\xba\x19\x8e\x12\xech\xd9\xc6\xba\xdf\xecZ\x1c\xd8C\xfe\xe7R\xc9\xcf\x02\xd0\xc7\x7f\xc9~\xb0\x94\xe3SDX\x0b.\xfd)t\xb5\x06\x9b\\D\x8d\xfb2u\xa4:\xa8g{\x872\nP\x8d\xe1\xa2\x13J%\xaf\xe6\x1c\xb1%\xbf\xb4\x99\xa2S\xd3\xa2\x02\xbf\x11\x02\x03\x01\x00\x01']>
         |   |  \x509v3ext \
         |   |   |###[ X509v3Ext ]###
         |   |   |  val       = <ASN1_SEQUENCE[[<ASN1_OID['.2.5.29.19']>, <ASN1_BOOLEAN[-1L]>, <ASN1_STRING['0\x00']>]]>
         |   |   |###[ X509v3Ext ]###
         |   |   |  val       = <ASN1_SEQUENCE[[<ASN1_OID['.2.5.29.15']>, <ASN1_BOOLEAN[-1L]>, <ASN1_STRING['\x03\x02\x05\xe0']>]]>
         |   |   |###[ X509v3Ext ]###
         |   |   |  val       = <ASN1_SEQUENCE[[<ASN1_OID['.2.16.840.1.113730.1.13']>, <ASN1_STRING['\x16\x1dOpenSSL Generated Certificate']>]]>
         |   |   |###[ X509v3Ext ]###
         |   |   |  val       = <ASN1_SEQUENCE[[<ASN1_OID['.2.5.29.14']>, <ASN1_STRING["\x04\x14\x82\xbc\xcf\x00\x00\x13\xd1\xf79%\x9a'\xe7\xaf\xd2\xef \x1bn\xac"]>]]>
         |   |   |###[ X509v3Ext ]###
         |   |   |  val       = <ASN1_SEQUENCE[[<ASN1_OID['.2.5.29.35']>, <ASN1_STRING['0\x16\x80\x146\xc3l\x88\xe7\x95\xfe\xb0\xbd\xec\xce>=\x86\xab!\x81\x87\xda\xda']>]]>
         |   |  sign_algo2= <ASN1_OID['.1.2.840.113549.1.1.5']>
         |   |  sa2_value = <ASN1_NULL[0L]>
         |   |  signature = <ASN1_BIT_STRING["\x00\xa9\xbdMW@t\xfe\x96\xe9+\xd6x\xfd\xb3c\xcc\xf4\x0bM\x12\xcaZt\x8d\x9b\xf2a\xe6\xfd\x06\x11C\x84\xfc\x17\xa0\xeccc6\xb9\x9e6j\xb1\x02Zj[?j\xa1\xea\x05e\xac~@\x1aHe\x88\xd19M\xd3Kw\xe9\xc8\xbb+\x9eZ\xf4\x0849G\xb9\x02\x081\x9a\xf1\xd9\x17\xc5\xe9\xa6\xa5\x96Km@\xa9[e(\xcb\xcb\x00\x03\x82c7\xd3\xad\xb1\x96;v\xf5\x17\x16\x02{\xbdSSFr4\xd6\x08d\x9d\xbbC\xfbd\xb1I\x07w\tazB\x17\x110\x0c\xd9'\\\xf5q\xb6\xf0\x180\xf3~\xf1\x85?2~J\xaf\xb3\x10\xf7l\xc6\x85K-'\xad\n \\\xfb\x8d\x19p4\xb9u_|\x87\xd5\xc3\xec\x93\x13A\xfcs\x03\xb9\x8d\x1a\xfe\xf7&\x86I\x03\xa9\xc5\x82?\x80\r)I\xb1\x8f\xed$\x1b\xfe\xcfX\x90F\xe7\xa8\x87\xd4\x1ey\xef\x99m\x18\x9f>\x8b\x82\x07\xc1C\xc7\xe0%\xb6\xf1\xd3\x00\xd7@\xabK\x7f+z>\xa6\x99LT"]>
alexmgr commented 9 years ago

Nice addition. Allows fined grain fuzzing of client side certificate handling also I guess ;)?

tintinweb commented 9 years ago

Scapy's built-in fuzzer is pretty dumb but with some nice tweaks and as long as the asn.1 fields support it it should work :)

alexmgr commented 9 years ago

I get unittest failures on this. Looks like input DER cert might be invalid. Haven't checked yet, but might be quick fix.

======================================================================
ERROR: test_tls_certificate_x509 (tests.test_ssl_tls.TestTLSCertificate)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/Users/amoneger/projects/scapy-ssl_tls-upstream/tests/test_ssl_tls.py", line 172, in test_tls_certificate_x509
    pkt = tls.TLSRecord()/tls.TLSHandshake()/tls.TLSCertificateList(certificates=[tls.TLSCertificate(data=x509.X509Cert(self.der_cert))])
  File "/Users/amoneger/.virtualenvs/scapy-ssl_tls-upstream/lib/python2.7/site-packages/scapy/base_classes.py", line 198, in __call__
    i.__init__(*args, **kargs)
  File "/Users/amoneger/.virtualenvs/scapy-ssl_tls-upstream/lib/python2.7/site-packages/scapy/packet.py", line 84, in __init__
    self.dissect(_pkt)
  File "/Users/amoneger/.virtualenvs/scapy-ssl_tls-upstream/lib/python2.7/site-packages/scapy/packet.py", line 594, in dissect
    s = self.do_dissect(s)
  File "/Users/amoneger/.virtualenvs/scapy-ssl_tls-upstream/lib/python2.7/site-packages/scapy/asn1packet.py", line 24, in do_dissect
    return self.ASN1_root.dissect(self, x)
  File "/Users/amoneger/.virtualenvs/scapy-ssl_tls-upstream/lib/python2.7/site-packages/scapy/asn1fields.py", line 222, in dissect
    s = obj.dissect(pkt,s)
  File "/Users/amoneger/.virtualenvs/scapy-ssl_tls-upstream/lib/python2.7/site-packages/scapy/asn1fields.py", line 222, in dissect
    s = obj.dissect(pkt,s)
  File "/Users/amoneger/.virtualenvs/scapy-ssl_tls-upstream/lib/python2.7/site-packages/scapy/asn1fields.py", line 222, in dissect
    s = obj.dissect(pkt,s)
  File "/Users/amoneger/.virtualenvs/scapy-ssl_tls-upstream/lib/python2.7/site-packages/scapy/asn1fields.py", line 98, in dissect
    v,s = self.m2i(pkt, s)
  File "/Users/amoneger/.virtualenvs/scapy-ssl_tls-upstream/lib/python2.7/site-packages/scapy/asn1fields.py", line 65, in m2i
    return self.ASN1_tag.get_codec(pkt.ASN1_codec).safedec(x, context=self.context)
  File "/Users/amoneger/.virtualenvs/scapy-ssl_tls-upstream/lib/python2.7/site-packages/scapy/asn1/ber.py", line 180, in safedec
    return cls.dec(s, context, safe=True)
  File "/Users/amoneger/.virtualenvs/scapy-ssl_tls-upstream/lib/python2.7/site-packages/scapy/asn1/ber.py", line 169, in dec
    return cls.do_dec(s, context, safe)
  File "/Users/amoneger/.virtualenvs/scapy-ssl_tls-upstream/lib/python2.7/site-packages/scapy/asn1/ber.py", line 361, in do_dec
    return cls.asn1_object(".".join([str(k) for k in lst])), t
  File "/Users/amoneger/.virtualenvs/scapy-ssl_tls-upstream/lib/python2.7/site-packages/scapy/asn1/ber.py", line 121, in asn1_object
    return cls.tag.asn1_object(val)
  File "/Users/amoneger/.virtualenvs/scapy-ssl_tls-upstream/lib/python2.7/site-packages/scapy/asn1/asn1.py", line 96, in asn1_object
    return self._asn1_obj(val)
  File "/Users/amoneger/.virtualenvs/scapy-ssl_tls-upstream/lib/python2.7/site-packages/scapy/asn1/asn1.py", line 296, in __init__
    val = conf.mib._oid(val)
AttributeError: 'Conf' object has no attribute 'mib'

======================================================================
ERROR: test_tls_certificate_x509_pubkey (tests.test_ssl_tls.TestTLSCertificate)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/Users/amoneger/projects/scapy-ssl_tls-upstream/tests/test_ssl_tls.py", line 193, in test_tls_certificate_x509_pubkey
    pkt = tls.TLSRecord()/tls.TLSHandshake()/tls.TLSCertificateList(certificates=[tls.TLSCertificate(data=x509.X509Cert(self.der_cert))])
  File "/Users/amoneger/.virtualenvs/scapy-ssl_tls-upstream/lib/python2.7/site-packages/scapy/base_classes.py", line 198, in __call__
    i.__init__(*args, **kargs)
  File "/Users/amoneger/.virtualenvs/scapy-ssl_tls-upstream/lib/python2.7/site-packages/scapy/packet.py", line 84, in __init__
    self.dissect(_pkt)
  File "/Users/amoneger/.virtualenvs/scapy-ssl_tls-upstream/lib/python2.7/site-packages/scapy/packet.py", line 594, in dissect
    s = self.do_dissect(s)
  File "/Users/amoneger/.virtualenvs/scapy-ssl_tls-upstream/lib/python2.7/site-packages/scapy/asn1packet.py", line 24, in do_dissect
    return self.ASN1_root.dissect(self, x)
  File "/Users/amoneger/.virtualenvs/scapy-ssl_tls-upstream/lib/python2.7/site-packages/scapy/asn1fields.py", line 222, in dissect
    s = obj.dissect(pkt,s)
  File "/Users/amoneger/.virtualenvs/scapy-ssl_tls-upstream/lib/python2.7/site-packages/scapy/asn1fields.py", line 222, in dissect
    s = obj.dissect(pkt,s)
  File "/Users/amoneger/.virtualenvs/scapy-ssl_tls-upstream/lib/python2.7/site-packages/scapy/asn1fields.py", line 222, in dissect
    s = obj.dissect(pkt,s)
  File "/Users/amoneger/.virtualenvs/scapy-ssl_tls-upstream/lib/python2.7/site-packages/scapy/asn1fields.py", line 98, in dissect
    v,s = self.m2i(pkt, s)
  File "/Users/amoneger/.virtualenvs/scapy-ssl_tls-upstream/lib/python2.7/site-packages/scapy/asn1fields.py", line 65, in m2i
    return self.ASN1_tag.get_codec(pkt.ASN1_codec).safedec(x, context=self.context)
  File "/Users/amoneger/.virtualenvs/scapy-ssl_tls-upstream/lib/python2.7/site-packages/scapy/asn1/ber.py", line 180, in safedec
    return cls.dec(s, context, safe=True)
  File "/Users/amoneger/.virtualenvs/scapy-ssl_tls-upstream/lib/python2.7/site-packages/scapy/asn1/ber.py", line 169, in dec
    return cls.do_dec(s, context, safe)
  File "/Users/amoneger/.virtualenvs/scapy-ssl_tls-upstream/lib/python2.7/site-packages/scapy/asn1/ber.py", line 361, in do_dec
    return cls.asn1_object(".".join([str(k) for k in lst])), t
  File "/Users/amoneger/.virtualenvs/scapy-ssl_tls-upstream/lib/python2.7/site-packages/scapy/asn1/ber.py", line 121, in asn1_object
    return cls.tag.asn1_object(val)
  File "/Users/amoneger/.virtualenvs/scapy-ssl_tls-upstream/lib/python2.7/site-packages/scapy/asn1/asn1.py", line 96, in asn1_object
    return self._asn1_obj(val)
  File "/Users/amoneger/.virtualenvs/scapy-ssl_tls-upstream/lib/python2.7/site-packages/scapy/asn1/asn1.py", line 296, in __init__
    val = conf.mib._oid(val)
AttributeError: 'Conf' object has no attribute 'mib'
tintinweb commented 9 years ago

im on it :) thx

tintinweb commented 9 years ago

fixed with 8e65b57805fe68ceaf08f2fc2ef0d1e4877d88ab - unittest runs, but there are some import error warnings atm. will fix that later.

test_tls_certificate_x509 (test_ssl_tls.TestTLSCertificate) ... ok
test_tls_certificate_x509_pubkey (test_ssl_tls.TestTLSCertificate) ... ok
test_all_hooks_are_called_when_defined (test_ssl_tls.TestToRaw) ... ok
test_invalid_tls_session_context_raises_error (test_ssl_tls.TestToRaw) ... ok
test_record_payload_is_identical_to_raw_payload (test_ssl_tls.TestToRaw) ... ok
test_tls_record_header_is_updated_when_output (test_ssl_tls.TestToRaw) ... ok
test_unsupported_layer_raises_error (test_ssl_tls.TestToRaw) ... ok