1## This file is part of Scapy 2## Copyright (C) 2017 Maxence Tury 3## This program is published under a GPLv2 license 4 5""" 6SSLv2 handshake fields & logic. 7""" 8 9import math 10 11from scapy.error import log_runtime, warning 12from scapy.fields import * 13from scapy.packet import Packet, Raw, Padding 14from scapy.layers.tls.cert import Cert, PrivKey, PubKey 15from scapy.layers.tls.basefields import _tls_version, _TLSVersionField 16from scapy.layers.tls.handshake import _CipherSuitesField 17from scapy.layers.tls.keyexchange import _TLSSignatureField, _TLSSignature 18from scapy.layers.tls.session import (_GenericTLSSessionInheritance, 19 readConnState, writeConnState) 20from scapy.layers.tls.crypto.suites import (_tls_cipher_suites, 21 _tls_cipher_suites_cls, 22 _GenericCipherSuite, 23 _GenericCipherSuiteMetaclass, 24 get_usable_ciphersuites, 25 SSL_CK_DES_192_EDE3_CBC_WITH_MD5) 26 27 28############################################################################### 29### Generic SSLv2 Handshake message ### 30############################################################################### 31 32_sslv2_handshake_type = { 0: "error", 1: "client_hello", 33 2: "client_master_key", 3: "client_finished", 34 4: "server_hello", 5: "server_verify", 35 6: "server_finished", 7: "request_certificate", 36 8: "client_certificate" } 37 38 39class _SSLv2Handshake(_GenericTLSSessionInheritance): 40 """ 41 Inherited by other Handshake classes to get post_build(). 42 Also used as a fallback for unknown TLS Handshake packets. 43 """ 44 name = "SSLv2 Handshake Generic message" 45 fields_desc = [ ByteEnumField("msgtype", None, _sslv2_handshake_type) ] 46 47 def guess_payload_class(self, p): 48 return Padding 49 50 def tls_session_update(self, msg_str): 51 """ 52 Covers both post_build- and post_dissection- context updates. 53 """ 54 self.tls_session.handshake_messages.append(msg_str) 55 self.tls_session.handshake_messages_parsed.append(self) 56 57 58############################################################################### 59### Error ### 60############################################################################### 61 62_tls_error_code = { 1: "no_cipher", 2: "no_certificate", 63 4: "bad_certificate", 6: "unsupported_certificate_type" } 64 65class SSLv2Error(_SSLv2Handshake): 66 """ 67 SSLv2 Error. 68 """ 69 name = "SSLv2 Handshake - Error" 70 fields_desc = [ ByteEnumField("msgtype", 0, _sslv2_handshake_type), 71 ShortEnumField("code", None, _tls_error_code) ] 72 73 74############################################################################### 75### ClientHello ### 76############################################################################### 77 78class _SSLv2CipherSuitesField(_CipherSuitesField): 79 def __init__(self, name, default, dico, length_from=None): 80 _CipherSuitesField.__init__(self, name, default, dico, 81 length_from=length_from) 82 self.itemfmt = b"" 83 self.itemsize = 3 84 85 def i2m(self, pkt, val): 86 if val is None: 87 val2 = [] 88 val2 = [(x >> 16, x & 0x00ffff) for x in val] 89 return b"".join([struct.pack(">BH", x[0], x[1]) for x in val2]) 90 91 def m2i(self, pkt, m): 92 res = [] 93 while m: 94 res.append(struct.unpack("!I", b"\x00" + m[:3])[0]) 95 m = m[3:] 96 return res 97 98 99class SSLv2ClientHello(_SSLv2Handshake): 100 """ 101 SSLv2 ClientHello. 102 """ 103 name = "SSLv2 Handshake - Client Hello" 104 fields_desc = [ ByteEnumField("msgtype", 1, _sslv2_handshake_type), 105 _TLSVersionField("version", 0x0002, _tls_version), 106 107 FieldLenField("cipherslen", None, fmt="!H", 108 length_of="ciphers"), 109 FieldLenField("sidlen", None, fmt="!H", 110 length_of="sid"), 111 FieldLenField("challengelen", None, fmt="!H", 112 length_of="challenge"), 113 114 XStrLenField("sid", b"", 115 length_from=lambda pkt:pkt.sidlen), 116 _SSLv2CipherSuitesField("ciphers", 117 [SSL_CK_DES_192_EDE3_CBC_WITH_MD5], 118 _tls_cipher_suites, 119 length_from=lambda pkt: pkt.cipherslen), 120 XStrLenField("challenge", b"", 121 length_from=lambda pkt:pkt.challengelen) ] 122 123 def tls_session_update(self, msg_str): 124 super(SSLv2ClientHello, self).tls_session_update(msg_str) 125 self.tls_session.advertised_tls_version = self.version 126 self.tls_session.sslv2_common_cs = self.ciphers 127 self.tls_session.sslv2_challenge = self.challenge 128 129 130############################################################################### 131### ServerHello ### 132############################################################################### 133 134class _SSLv2CertDataField(StrLenField): 135 def getfield(self, pkt, s): 136 l = 0 137 if self.length_from is not None: 138 l = self.length_from(pkt) 139 try: 140 certdata = Cert(s[:l]) 141 except: 142 certdata = s[:l] 143 return s[l:], certdata 144 145 def i2len(self, pkt, i): 146 if isinstance(i, Cert): 147 return len(i.der) 148 return len(i) 149 150 def i2m(self, pkt, i): 151 if isinstance(i, Cert): 152 return i.der 153 return i 154 155 156class SSLv2ServerHello(_SSLv2Handshake): 157 """ 158 SSLv2 ServerHello. 159 """ 160 name = "SSLv2 Handshake - Server Hello" 161 fields_desc = [ ByteEnumField("msgtype", 4, _sslv2_handshake_type), 162 163 ByteField("sid_hit", 0), 164 ByteEnumField("certtype", 1, {1: "x509_cert"}), 165 _TLSVersionField("version", 0x0002, _tls_version), 166 167 FieldLenField("certlen", None, fmt="!H", 168 length_of="cert"), 169 FieldLenField("cipherslen", None, fmt="!H", 170 length_of="ciphers"), 171 FieldLenField("connection_idlen", None, fmt="!H", 172 length_of="connection_id"), 173 174 _SSLv2CertDataField("cert", b"", 175 length_from=lambda pkt: pkt.certlen), 176 _SSLv2CipherSuitesField("ciphers", [], _tls_cipher_suites, 177 length_from=lambda pkt: pkt.cipherslen), 178 XStrLenField("connection_id", b"", 179 length_from=lambda pkt: pkt.connection_idlen) ] 180 181 def tls_session_update(self, msg_str): 182 """ 183 XXX Something should be done about the session ID here. 184 """ 185 super(SSLv2ServerHello, self).tls_session_update(msg_str) 186 187 s = self.tls_session 188 client_cs = s.sslv2_common_cs 189 css = [cs for cs in client_cs if cs in self.ciphers] 190 s.sslv2_common_cs = css 191 s.sslv2_connection_id = self.connection_id 192 s.tls_version = self.version 193 if self.cert is not None: 194 s.server_certs = [self.cert] 195 196 197############################################################################### 198### ClientMasterKey ### 199############################################################################### 200 201class _SSLv2CipherSuiteField(EnumField): 202 def __init__(self, name, default, dico): 203 EnumField.__init__(self, name, default, dico) 204 205 def i2m(self, pkt, val): 206 if val is None: 207 return b"" 208 val2 = (val >> 16, val & 0x00ffff) 209 return struct.pack(">BH", val2[0], val2[1]) 210 211 def addfield(self, pkt, s, val): 212 return s + self.i2m(pkt, val) 213 214 def m2i(self, pkt, m): 215 return struct.unpack("!I", b"\x00" + m[:3])[0] 216 217 def getfield(self, pkt, s): 218 return s[3:], self.m2i(pkt, s) 219 220class _SSLv2EncryptedKeyField(XStrLenField): 221 def i2repr(self, pkt, x): 222 s = super(_SSLv2EncryptedKeyField, self).i2repr(pkt, x) 223 if pkt.decryptedkey is not None: 224 dx = pkt.decryptedkey 225 ds = super(_SSLv2EncryptedKeyField, self).i2repr(pkt, dx) 226 s += " [decryptedkey= %s]" % ds 227 return s 228 229class SSLv2ClientMasterKey(_SSLv2Handshake): 230 """ 231 SSLv2 ClientMasterKey. 232 """ 233 __slots__ = ["decryptedkey"] 234 name = "SSLv2 Handshake - Client Master Key" 235 fields_desc = [ ByteEnumField("msgtype", 2, _sslv2_handshake_type), 236 _SSLv2CipherSuiteField("cipher", None, _tls_cipher_suites), 237 238 FieldLenField("clearkeylen", None, fmt="!H", 239 length_of="clearkey"), 240 FieldLenField("encryptedkeylen", None, fmt="!H", 241 length_of="encryptedkey"), 242 FieldLenField("keyarglen", None, fmt="!H", 243 length_of="keyarg"), 244 245 XStrLenField("clearkey", "", 246 length_from=lambda pkt: pkt.clearkeylen), 247 _SSLv2EncryptedKeyField("encryptedkey", "", 248 length_from=lambda pkt: pkt.encryptedkeylen), 249 XStrLenField("keyarg", "", 250 length_from=lambda pkt: pkt.keyarglen) ] 251 252 def __init__(self, *args, **kargs): 253 """ 254 When post_building, the packets fields are updated (this is somewhat 255 non-standard). We might need these fields later, but calling __str__ 256 on a new packet (i.e. not dissected from a raw string) applies 257 post_build to an object different from the original one... unless 258 we hackishly always set self.explicit to 1. 259 """ 260 if "decryptedkey" in kargs: 261 self.decryptedkey = kargs["decryptedkey"] 262 del kargs["decryptedkey"] 263 else: 264 self.decryptedkey = b"" 265 super(SSLv2ClientMasterKey, self).__init__(*args, **kargs) 266 self.explicit = 1 267 268 def pre_dissect(self, s): 269 clearkeylen = struct.unpack("!H", s[4:6])[0] 270 encryptedkeylen = struct.unpack("!H", s[6:8])[0] 271 encryptedkeystart = 10 + clearkeylen 272 encryptedkey = s[encryptedkeystart:encryptedkeystart+encryptedkeylen] 273 if self.tls_session.server_rsa_key: 274 self.decryptedkey = \ 275 self.tls_session.server_rsa_key.decrypt(encryptedkey) 276 else: 277 self.decryptedkey = None 278 return s 279 280 def post_build(self, pkt, pay): 281 cs_val = None 282 if self.cipher is None: 283 common_cs = self.tls_session.sslv2_common_cs 284 cs_vals = get_usable_ciphersuites(common_cs, "SSLv2") 285 if len(cs_vals) == 0: 286 warning("No known common cipher suite between SSLv2 Hellos.") 287 cs_val = 0x0700c0 288 cipher = b"\x07\x00\xc0" 289 else: 290 cs_val = cs_vals[0] #XXX choose the best one 291 cipher = struct.pack(">BH", cs_val >> 16, cs_val & 0x00ffff) 292 cs_cls = _tls_cipher_suites_cls[cs_val] 293 self.cipher = cs_val 294 else: 295 cipher = pkt[1:4] 296 cs_val = struct.unpack("!I", b"\x00" + cipher)[0] 297 if cs_val not in _tls_cipher_suites_cls: 298 warning("Unknown ciphersuite %d from ClientMasterKey" % cs_val) 299 cs_cls = None 300 else: 301 cs_cls = _tls_cipher_suites_cls[cs_val] 302 303 if cs_cls: 304 if (self.encryptedkey == b"" and 305 len(self.tls_session.server_certs) > 0): 306 # else, the user is responsible for export slicing & encryption 307 key = randstring(cs_cls.cipher_alg.key_len) 308 309 if self.clearkey == b"" and cs_cls.kx_alg.export: 310 self.clearkey = key[:-5] 311 312 if self.decryptedkey == b"": 313 if cs_cls.kx_alg.export: 314 self.decryptedkey = key[-5:] 315 else: 316 self.decryptedkey = key 317 318 pubkey = self.tls_session.server_certs[0].pubKey 319 self.encryptedkey = pubkey.encrypt(self.decryptedkey) 320 321 if self.keyarg == b"" and cs_cls.cipher_alg.type == "block": 322 self.keyarg = randstring(cs_cls.cipher_alg.block_size) 323 324 clearkey = self.clearkey or b"" 325 if self.clearkeylen is None: 326 self.clearkeylen = len(clearkey) 327 clearkeylen = struct.pack("!H", self.clearkeylen) 328 329 encryptedkey = self.encryptedkey or b"" 330 if self.encryptedkeylen is None: 331 self.encryptedkeylen = len(encryptedkey) 332 encryptedkeylen = struct.pack("!H", self.encryptedkeylen) 333 334 keyarg = self.keyarg or b"" 335 if self.keyarglen is None: 336 self.keyarglen = len(keyarg) 337 keyarglen = struct.pack("!H", self.keyarglen) 338 339 s = (chb(pkt[0]) + cipher 340 + clearkeylen + encryptedkeylen + keyarglen 341 + clearkey + encryptedkey + keyarg) 342 return s + pay 343 344 def tls_session_update(self, msg_str): 345 super(SSLv2ClientMasterKey, self).tls_session_update(msg_str) 346 347 s = self.tls_session 348 cs_val = self.cipher 349 if cs_val not in _tls_cipher_suites_cls: 350 warning("Unknown cipher suite %d from ClientMasterKey" % cs_val) 351 cs_cls = None 352 else: 353 cs_cls = _tls_cipher_suites_cls[cs_val] 354 355 tls_version = s.tls_version or 0x0002 356 connection_end = s.connection_end 357 wcs_seq_num = s.wcs.seq_num 358 s.pwcs = writeConnState(ciphersuite=cs_cls, 359 connection_end=connection_end, 360 seq_num=wcs_seq_num, 361 tls_version=tls_version) 362 rcs_seq_num = s.rcs.seq_num 363 s.prcs = readConnState(ciphersuite=cs_cls, 364 connection_end=connection_end, 365 seq_num=rcs_seq_num, 366 tls_version=tls_version) 367 368 if self.decryptedkey is not None: 369 s.master_secret = self.clearkey + self.decryptedkey 370 s.compute_sslv2_km_and_derive_keys() 371 372 if s.pwcs.cipher.type == "block": 373 s.pwcs.cipher.iv = self.keyarg 374 if s.prcs.cipher.type == "block": 375 s.prcs.cipher.iv = self.keyarg 376 377 s.triggered_prcs_commit = True 378 s.triggered_pwcs_commit = True 379 380 381############################################################################### 382### ServerVerify ### 383############################################################################### 384 385class SSLv2ServerVerify(_SSLv2Handshake): 386 """ 387 In order to parse a ServerVerify, the exact message string should be 388 fed to the class. This is how SSLv2 defines the challenge length... 389 """ 390 name = "SSLv2 Handshake - Server Verify" 391 fields_desc = [ ByteEnumField("msgtype", 5, _sslv2_handshake_type), 392 XStrField("challenge", "") ] 393 394 def build(self, *args, **kargs): 395 fval = self.getfieldval("challenge") 396 if fval is None: 397 self.challenge = self.tls_session.sslv2_challenge 398 return super(SSLv2ServerVerify, self).build(*args, **kargs) 399 400 def post_dissection(self, pkt): 401 s = self.tls_session 402 if s.sslv2_challenge is not None: 403 if self.challenge != s.sslv2_challenge: 404 pkt_info = pkt.firstlayer().summary() 405 log_runtime.info("TLS: invalid ServerVerify received [%s]", pkt_info) 406 407 408############################################################################### 409### RequestCertificate ### 410############################################################################### 411 412class SSLv2RequestCertificate(_SSLv2Handshake): 413 """ 414 In order to parse a RequestCertificate, the exact message string should be 415 fed to the class. This is how SSLv2 defines the challenge length... 416 """ 417 name = "SSLv2 Handshake - Request Certificate" 418 fields_desc = [ ByteEnumField("msgtype", 7, _sslv2_handshake_type), 419 ByteEnumField("authtype", 1, {1: "md5_with_rsa"}), 420 XStrField("challenge", "") ] 421 422 def tls_session_update(self, msg_str): 423 super(SSLv2RequestCertificate, self).tls_session_update(msg_str) 424 self.tls_session.sslv2_challenge_clientcert = self.challenge 425 426 427############################################################################### 428### ClientCertificate ### 429############################################################################### 430 431class SSLv2ClientCertificate(_SSLv2Handshake): 432 """ 433 SSLv2 ClientCertificate. 434 """ 435 name = "SSLv2 Handshake - Client Certificate" 436 fields_desc = [ ByteEnumField("msgtype", 8, _sslv2_handshake_type), 437 438 ByteEnumField("certtype", 1, {1: "x509_cert"}), 439 FieldLenField("certlen", None, fmt="!H", 440 length_of="certdata"), 441 FieldLenField("responselen", None, fmt="!H", 442 length_of="responsedata"), 443 444 _SSLv2CertDataField("certdata", b"", 445 length_from=lambda pkt: pkt.certlen), 446 _TLSSignatureField("responsedata", None, 447 length_from=lambda pkt: pkt.responselen) ] 448 449 def build(self, *args, **kargs): 450 s = self.tls_session 451 sig = self.getfieldval("responsedata") 452 test = (sig is None and 453 s.sslv2_key_material is not None and 454 s.sslv2_challenge_clientcert is not None and 455 len(s.server_certs) > 0) 456 if test: 457 s = self.tls_session 458 m = (s.sslv2_key_material + 459 s.sslv2_challenge_clientcert + 460 s.server_certs[0].der) 461 self.responsedata = _TLSSignature(tls_session=s) 462 self.responsedata._update_sig(m, s.client_key) 463 else: 464 self.responsedata = b"" 465 return super(SSLv2ClientCertificate, self).build(*args, **kargs) 466 467 def post_dissection_tls_session_update(self, msg_str): 468 self.tls_session_update(msg_str) 469 470 s = self.tls_session 471 test = (len(s.client_certs) > 0 and 472 s.sslv2_key_material is not None and 473 s.sslv2_challenge_clientcert is not None and 474 len(s.server_certs) > 0) 475 if test: 476 m = (s.sslv2_key_material + 477 s.sslv2_challenge_clientcert + 478 s.server_certs[0].der) 479 sig_test = self.responsedata._verify_sig(m, s.client_certs[0]) 480 if not sig_test: 481 pkt_info = self.firstlayer().summary() 482 log_runtime.info("TLS: invalid client CertificateVerify signature [%s]", pkt_info) 483 484 def tls_session_update(self, msg_str): 485 super(SSLv2ClientCertificate, self).tls_session_update(msg_str) 486 if self.certdata: 487 self.tls_session.client_certs = [self.certdata] 488 489 490############################################################################### 491### Finished ### 492############################################################################### 493 494class SSLv2ClientFinished(_SSLv2Handshake): 495 """ 496 In order to parse a ClientFinished, the exact message string should be fed 497 to the class. SSLv2 does not offer any other way to know the c_id length. 498 """ 499 name = "SSLv2 Handshake - Client Finished" 500 fields_desc = [ ByteEnumField("msgtype", 3, _sslv2_handshake_type), 501 XStrField("connection_id", "") ] 502 503 def build(self, *args, **kargs): 504 fval = self.getfieldval("connection_id") 505 if fval == b"": 506 self.connection_id = self.tls_session.sslv2_connection_id 507 return super(SSLv2ClientFinished, self).build(*args, **kargs) 508 509 def post_dissection(self, pkt): 510 s = self.tls_session 511 if s.sslv2_connection_id is not None: 512 if self.connection_id != s.sslv2_connection_id: 513 pkt_info = pkt.firstlayer().summary() 514 log_runtime.info("TLS: invalid client Finished received [%s]", pkt_info) 515 516 517class SSLv2ServerFinished(_SSLv2Handshake): 518 """ 519 In order to parse a ServerFinished, the exact message string should be fed 520 to the class. SSLv2 does not offer any other way to know the sid length. 521 """ 522 name = "SSLv2 Handshake - Server Finished" 523 fields_desc = [ ByteEnumField("msgtype", 6, _sslv2_handshake_type), 524 XStrField("sid", "") ] 525 526 def build(self, *args, **kargs): 527 fval = self.getfieldval("sid") 528 if fval == b"": 529 self.sid = self.tls_session.sid 530 return super(SSLv2ServerFinished, self).build(*args, **kargs) 531 532 def post_dissection_tls_session_update(self, msg_str): 533 self.tls_session_update(msg_str) 534 self.tls_session.sid = self.sid 535 536 537############################################################################### 538### All handshake messages defined in this module ### 539############################################################################### 540 541_sslv2_handshake_cls = { 0: SSLv2Error, 1: SSLv2ClientHello, 542 2: SSLv2ClientMasterKey, 3: SSLv2ClientFinished, 543 4: SSLv2ServerHello, 5: SSLv2ServerVerify, 544 6: SSLv2ServerFinished, 7: SSLv2RequestCertificate, 545 8: SSLv2ClientCertificate } 546 547