class MKADistributedCAKParamSet(MKAParamSet): """ Distributed CAK Parameter Set (802.1X-2010, section 11.11). """ ######################################################################### # # IEEE 802.1X-2010 standard # Section 11.11 ######################################################################### # name = "Distributed CAK parameter set" fields_desc = [ PadField( ByteEnumField( "param_set_type", 5, _parameter_set_types ), 2, padwith=b"\x00" ), ShortField("param_set_body_len", 0), XStrFixedLenField( "cak_aes_key_wrap", "", length=MKAParamSet.EAPOL_MKA_DEFAULT_KEY_WRAP_LEN ), XStrField("cak_key_name", "") ]
class ESP(Packet): """ Encapsulated Security Payload See https://tools.ietf.org/rfc/rfc4303.txt """ name = 'ESP' fields_desc = [ XIntField('spi', 0x0), IntField('seq', 0), XStrField('data', None), ] overload_fields = { IP: { 'proto': socket.IPPROTO_ESP }, IPv6: { 'nh': socket.IPPROTO_ESP }, IPv6ExtHdrHopByHop: { 'nh': socket.IPPROTO_ESP }, IPv6ExtHdrDestOpt: { 'nh': socket.IPPROTO_ESP }, IPv6ExtHdrRouting: { 'nh': socket.IPPROTO_ESP }, }
class TLSInnerPlaintext(_GenericTLSSessionInheritance): name = "TLS Inner Plaintext" fields_desc = [ _TLSMsgListField("msg", []), ByteEnumField("type", None, _tls_type), XStrField("pad", "") ] def pre_dissect(self, s): """ We need to parse the padding and type as soon as possible, else we won't be able to parse the message list... """ if len(s) < 1: raise Exception("Invalid InnerPlaintext (too short).") tmp_len = len(s) - 1 if s[-1] != b"\x00": msg_len = tmp_len else: n = 1 while s[-n] != b"\x00" and n < tmp_len: n += 1 msg_len = tmp_len - n self.fields_desc[0].length_from = lambda pkt: msg_len self.type = struct.unpack("B", s[msg_len:msg_len + 1])[0] return s
class WireguardTransport(Packet): name = "Wireguard Transport" fields_desc = [ XLEIntField("receiver_index", 0), XLELongField("counter", 0), XStrField("encrypted_encapsulated_packet", None) ]
class ZigbeeSecurityHeader(Packet): name = "Zigbee Security Header" fields_desc = [ # Security control (1 octet) FlagsField("reserved1", 0, 2, ['reserved1', 'reserved2']), BitField("extended_nonce", 1, 1), # set to 1 if the sender address field is present (source) # noqa: E501 # Key identifier BitEnumField("key_type", 1, 2, { 0: 'data_key', 1: 'network_key', 2: 'key_transport_key', 3: 'key_load_key' }), # Security level (3 bits) BitEnumField("nwk_seclevel", 0, 3, { 0: "None", 1: "MIC-32", 2: "MIC-64", 3: "MIC-128", 4: "ENC", 5: "ENC-MIC-32", 6: "ENC-MIC-64", 7: "ENC-MIC-128" }), # Frame counter (4 octets) XLEIntField("fc", 0), # provide frame freshness and prevent duplicate frames # noqa: E501 # Source address (0/8 octets) ConditionalField(dot15d4AddressField("source", 0, adjust=lambda pkt, x: 8), lambda pkt: pkt.extended_nonce), # noqa: E501 # Key sequence number (0/1 octet): only present when key identifier is 1 (network key) # noqa: E501 ConditionalField(ByteField("key_seqnum", 0), lambda pkt: pkt.getfieldval("key_type") == 1), # noqa: E501 # Payload # the length of the encrypted data is the payload length minus the MIC XStrField("data", ""), # noqa: E501 # Message Integrity Code (0/variable in size), length depends on nwk_seclevel # noqa: E501 XStrField("mic", ""), ] def post_dissect(self, s): # Get the mic dissected correctly mic_length = util_mic_len(self) if mic_length > 0: # Slice "data" into "data + mic" _data, _mic = self.data[:-mic_length], self.data[-mic_length:] self.data, self.mic = _data, _mic return s
class SSLv2RequestCertificate(_SSLv2Handshake): """ In order to parse a RequestCertificate, the exact message string should be fed to the class. This is how SSLv2 defines the challenge length... """ name = "SSLv2 Handshake - Request Certificate" fields_desc = [ByteEnumField("msgtype", 7, _sslv2_handshake_type), ByteEnumField("authtype", 1, {1: "md5_with_rsa"}), XStrField("challenge", "")] def tls_session_update(self, msg_str): super(SSLv2RequestCertificate, self).tls_session_update(msg_str) self.tls_session.sslv2_challenge_clientcert = self.challenge
class SSLv2ServerFinished(_SSLv2Handshake): """ In order to parse a ServerFinished, the exact message string should be fed to the class. SSLv2 does not offer any other way to know the sid length. """ name = "SSLv2 Handshake - Server Finished" fields_desc = [ByteEnumField("msgtype", 6, _sslv2_handshake_type), XStrField("sid", "")] def build(self, *args, **kargs): fval = self.getfieldval("sid") if fval == b"": self.sid = self.tls_session.sid return super(SSLv2ServerFinished, self).build(*args, **kargs) def post_dissection_tls_session_update(self, msg_str): self.tls_session_update(msg_str) self.tls_session.sid = self.sid
class SSLv2ClientFinished(_SSLv2Handshake): """ In order to parse a ClientFinished, the exact message string should be fed to the class. SSLv2 does not offer any other way to know the c_id length. """ name = "SSLv2 Handshake - Client Finished" fields_desc = [ByteEnumField("msgtype", 3, _sslv2_handshake_type), XStrField("connection_id", "")] def build(self, *args, **kargs): fval = self.getfieldval("connection_id") if fval == b"": self.connection_id = self.tls_session.sslv2_connection_id return super(SSLv2ClientFinished, self).build(*args, **kargs) def post_dissection(self, pkt): s = self.tls_session if s.sslv2_connection_id is not None: if self.connection_id != s.sslv2_connection_id: pkt_info = pkt.firstlayer().summary() log_runtime.info("TLS: invalid client Finished received [%s]", pkt_info)
class SSLv2ServerVerify(_SSLv2Handshake): """ In order to parse a ServerVerify, the exact message string should be fed to the class. This is how SSLv2 defines the challenge length... """ name = "SSLv2 Handshake - Server Verify" fields_desc = [ByteEnumField("msgtype", 5, _sslv2_handshake_type), XStrField("challenge", "")] def build(self, *args, **kargs): fval = self.getfieldval("challenge") if fval is None: self.challenge = self.tls_session.sslv2_challenge return super(SSLv2ServerVerify, self).build(*args, **kargs) def post_dissection(self, pkt): s = self.tls_session if s.sslv2_challenge is not None: if self.challenge != s.sslv2_challenge: pkt_info = pkt.firstlayer().summary() log_runtime.info("TLS: invalid ServerVerify received [%s]", pkt_info)
class DoIP(Packet): """ Implementation of the DoIP (ISO 13400) protocol. DoIP packets can be sent via UDP and TCP. Depending on the payload type, the correct connection need to be chosen: +--------------+--------------------------------------------------------------+-----------------+ | Payload Type | Payload Type Name | Connection Kind | +--------------+--------------------------------------------------------------+-----------------+ | 0x0000 | Generic DoIP header negative acknowledge | UDP / TCP | +--------------+--------------------------------------------------------------+-----------------+ | 0x0001 | Vehicle Identification request message | UDP | +--------------+--------------------------------------------------------------+-----------------+ | 0x0002 | Vehicle identification request message with EID | UDP | +--------------+--------------------------------------------------------------+-----------------+ | 0x0003 | Vehicle identification request message with VIN | UDP | +--------------+--------------------------------------------------------------+-----------------+ | 0x0004 | Vehicle announcement message/vehicle identification response | UDP | +--------------+--------------------------------------------------------------+-----------------+ | 0x0005 | Routing activation request | TCP | +--------------+--------------------------------------------------------------+-----------------+ | 0x0006 | Routing activation response | TCP | +--------------+--------------------------------------------------------------+-----------------+ | 0x0007 | Alive Check request | TCP | +--------------+--------------------------------------------------------------+-----------------+ | 0x0008 | Alive Check response | TCP | +--------------+--------------------------------------------------------------+-----------------+ | 0x4001 | IP entity status request | UDP | +--------------+--------------------------------------------------------------+-----------------+ | 0x4002 | DoIP entity status response | UDP | +--------------+--------------------------------------------------------------+-----------------+ | 0x4003 | Diagnostic power mode information request | UDP | +--------------+--------------------------------------------------------------+-----------------+ | 0x4004 | Diagnostic power mode information response | UDP | +--------------+--------------------------------------------------------------+-----------------+ | 0x8001 | Diagnostic message | TCP | +--------------+--------------------------------------------------------------+-----------------+ | 0x8002 | Diagnostic message positive acknowledgement | TCP | +--------------+--------------------------------------------------------------+-----------------+ | 0x8003 | Diagnostic message negative acknowledgement | TCP | +--------------+--------------------------------------------------------------+-----------------+ Example with UDP: >>> socket = L3RawSocket(iface="eth0") >>> resp = socket.sr1(IP(dst="169.254.117.238")/UDP(dport=13400)/DoIP(payload_type=1)) Example with TCP: >>> socket = DoIPSocket("169.254.117.238") >>> pkt = DoIP(payload_type=0x8001, source_address=0xe80, target_address=0x1000) / UDS() / UDS_RDBI(identifiers=[0x1000]) >>> resp = socket.sr1(pkt, timeout=1) Example with UDS: >>> socket = UDS_DoIPSocket("169.254.117.238") >>> pkt = UDS() / UDS_RDBI(identifiers=[0x1000]) >>> resp = socket.sr1(pkt, timeout=1) """ # noqa: E501 payload_types = { 0x0000: "Generic DoIP header NACK", 0x0001: "Vehicle identification request", 0x0002: "Vehicle identification request with EID", 0x0003: "Vehicle identification request with VIN", 0x0004: "Vehicle announcement message/vehicle identification response message", # noqa: E501 0x0005: "Routing activation request", 0x0006: "Routing activation response", 0x0007: "Alive check request", 0x0008: "Alive check response", 0x4001: "DoIP entity status request", 0x4002: "DoIP entity status response", 0x4003: "Diagnostic power mode information request", 0x4004: "Diagnostic power mode information response", 0x8001: "Diagnostic message", 0x8002: "Diagnostic message ACK", 0x8003: "Diagnostic message NACK" } name = 'DoIP' fields_desc = [ XByteField("protocol_version", 0x02), XByteField("inverse_version", 0xFD), XShortEnumField("payload_type", 0, payload_types), IntField("payload_length", None), ConditionalField( ByteEnumField( "nack", 0, { 0: "Incorrect pattern format", 1: "Unknown payload type", 2: "Message too large", 3: "Out of memory", 4: "Invalid payload length" }), lambda p: p.payload_type in [0x0]), ConditionalField(StrFixedLenField("vin", b"", 17), lambda p: p.payload_type in [3, 4]), ConditionalField(XShortField("logical_address", 0), lambda p: p.payload_type in [4]), ConditionalField(StrFixedLenField("eid", b"", 6), lambda p: p.payload_type in [2, 4]), ConditionalField(StrFixedLenField("gid", b"", 6), lambda p: p.payload_type in [4]), ConditionalField( XByteEnumField( "further_action", 0, { 0x00: "No further action required", 0x01: "Reserved by ISO 13400", 0x02: "Reserved by ISO 13400", 0x03: "Reserved by ISO 13400", 0x04: "Reserved by ISO 13400", 0x05: "Reserved by ISO 13400", 0x06: "Reserved by ISO 13400", 0x07: "Reserved by ISO 13400", 0x08: "Reserved by ISO 13400", 0x09: "Reserved by ISO 13400", 0x0a: "Reserved by ISO 13400", 0x0b: "Reserved by ISO 13400", 0x0c: "Reserved by ISO 13400", 0x0d: "Reserved by ISO 13400", 0x0e: "Reserved by ISO 13400", 0x0f: "Reserved by ISO 13400", 0x10: "Routing activation required to initiate central security", }), lambda p: p.payload_type in [4]), ConditionalField( XByteEnumField( "vin_gid_status", 0, { 0x00: "VIN and/or GID are synchronized", 0x01: "Reserved by ISO 13400", 0x02: "Reserved by ISO 13400", 0x03: "Reserved by ISO 13400", 0x04: "Reserved by ISO 13400", 0x05: "Reserved by ISO 13400", 0x06: "Reserved by ISO 13400", 0x07: "Reserved by ISO 13400", 0x08: "Reserved by ISO 13400", 0x09: "Reserved by ISO 13400", 0x0a: "Reserved by ISO 13400", 0x0b: "Reserved by ISO 13400", 0x0c: "Reserved by ISO 13400", 0x0d: "Reserved by ISO 13400", 0x0e: "Reserved by ISO 13400", 0x0f: "Reserved by ISO 13400", 0x10: "Incomplete: VIN and GID are NOT synchronized" }), lambda p: p.payload_type in [4]), ConditionalField( XShortField("source_address", 0), lambda p: p.payload_type in [5, 8, 0x8001, 0x8002, 0x8003]), # noqa: E501 ConditionalField( XByteEnumField( "activation_type", 0, { 0: "Default", 1: "WWH-OBD", 0xe0: "Central security", 0x16: "Default", 0x116: "Diagnostic", 0xe016: "Central security" }), lambda p: p.payload_type in [5]), ConditionalField(XShortField("logical_address_tester", 0), lambda p: p.payload_type in [6]), ConditionalField(XShortField("logical_address_doip_entity", 0), lambda p: p.payload_type in [6]), ConditionalField( XByteEnumField( "routing_activation_response", 0, { 0x00: "Routing activation denied due to unknown source address.", 0x01: "Routing activation denied because all concurrently supported TCP_DATA sockets are registered and active.", # noqa: E501 0x02: "Routing activation denied because an SA different from the table connection entry was received on the already activated TCP_DATA socket.", # noqa: E501 0x03: "Routing activation denied because the SA is already registered and active on a different TCP_DATA socket.", # noqa: E501 0x04: "Routing activation denied due to missing authentication.", 0x05: "Routing activation denied due to rejected confirmation.", 0x06: "Routing activation denied due to unsupported routing activation type.", # noqa: E501 0x07: "Routing activation denied because the specified activation type requires a secure TLS TCP_DATA socket.", # noqa: E501 0x08: "Reserved by ISO 13400.", 0x09: "Reserved by ISO 13400.", 0x0a: "Reserved by ISO 13400.", 0x0b: "Reserved by ISO 13400.", 0x0c: "Reserved by ISO 13400.", 0x0d: "Reserved by ISO 13400.", 0x0e: "Reserved by ISO 13400.", 0x0f: "Reserved by ISO 13400.", 0x10: "Routing successfully activated.", 0x11: "Routing will be activated; confirmation required." }), lambda p: p.payload_type in [6]), ConditionalField(XIntField("reserved_iso", 0), lambda p: p.payload_type in [5, 6]), ConditionalField(XStrField("reserved_oem", b""), lambda p: p.payload_type in [5, 6]), ConditionalField( XByteEnumField("diagnostic_power_mode", 0, { 0: "not ready", 1: "ready", 2: "not supported" }), lambda p: p.payload_type in [0x4004]), ConditionalField( ByteEnumField("node_type", 0, { 0: "DoIP gateway", 1: "DoIP node" }), lambda p: p.payload_type in [0x4002]), ConditionalField(XByteField("max_open_sockets", 1), lambda p: p.payload_type in [0x4002]), ConditionalField(XByteField("cur_open_sockets", 0), lambda p: p.payload_type in [0x4002]), ConditionalField(IntField("max_data_size", 0), lambda p: p.payload_type in [0x4002]), ConditionalField(XShortField("target_address", 0), lambda p: p.payload_type in [0x8001, 0x8002, 0x8003 ]), # noqa: E501 ConditionalField(XByteEnumField("ack_code", 0, {0: "ACK"}), lambda p: p.payload_type in [0x8002]), ConditionalField( ByteEnumField( "nack_code", 0, { 0x00: "Reserved by ISO 13400", 0x01: "Reserved by ISO 13400", 0x02: "Invalid source address", 0x03: "Unknown target address", 0x04: "Diagnostic message too large", 0x05: "Out of memory", 0x06: "Target unreachable", 0x07: "Unknown network", 0x08: "Transport protocol error" }), lambda p: p.payload_type in [0x8003]), ConditionalField(XStrField("previous_msg", b""), lambda p: p.payload_type in [0x8002, 0x8003]) ] def answers(self, other): # type: (Packet) -> int """DEV: true if self is an answer from other""" if isinstance(other, type(self)): if self.payload_type == 0: return 1 matches = [(4, 1), (4, 2), (4, 3), (6, 5), (8, 7), (0x4002, 0x4001), (0x4004, 0x4003), (0x8001, 0x8001), (0x8003, 0x8001)] if (self.payload_type, other.payload_type) in matches: if self.payload_type == 0x8001: return self.payload.answers(other.payload) return 1 return 0 def hashret(self): # type: () -> bytes if self.payload_type in [0x8001, 0x8002, 0x8003]: return bytes(self)[:2] + struct.pack( "H", self.target_address ^ self.source_address) return bytes(self)[:2] def post_build(self, pkt, pay): # type: (bytes, bytes) -> bytes """ This will set the Field 'payload_length' to the correct value. """ if self.payload_length is None: pkt = pkt[:4] + struct.pack("!I", len(pay) + len(pkt) - 8) + \ pkt[8:] return pkt + pay def extract_padding(self, s): # type: (bytes) -> Tuple[bytes, Optional[bytes]] if self.payload_type == 0x8001: return s[:self.payload_length - 4], None else: return b"", None
class NTLM_AUTHENTICATE(Packet): name = "NTLM Authenticate" messageType = 3 OFFSET = 88 NTLM_VERSION = 1 fields_desc = [ NTLM_Header, # LmChallengeResponseFields LEShortField('LmChallengeResponseLen', None), LEShortField('LmChallengeResponseMaxLen', None), LEIntField('LmChallengeResponseBufferOffset', None), # NtChallengeResponseFields LEShortField('NtChallengeResponseLen', None), LEShortField('NtChallengeResponseMaxLen', None), LEIntField('NtChallengeResponseBufferOffset', None), # DomainNameFields LEShortField('DomainNameLen', None), LEShortField('DomainNameMaxLen', None), LEIntField('DomainNameBufferOffset', None), # UserNameFields LEShortField('UserNameLen', None), LEShortField('UserNameMaxLen', None), LEIntField('UserNameBufferOffset', None), # WorkstationFields LEShortField('WorkstationLen', None), LEShortField('WorkstationMaxLen', None), LEIntField('WorkstationBufferOffset', None), # EncryptedRandomSessionKeyFields LEShortField('EncryptedRandomSessionKeyLen', None), LEShortField('EncryptedRandomSessionKeyMaxLen', None), LEIntField('EncryptedRandomSessionKeyBufferOffset', None), # NegotiateFlags FlagsField('NegotiateFlags', 0, -32, _negotiateFlags), # VERSION _NTLM_Version, # MIC XStrFixedLenField('MIC', b"", length=16), # Payload _NTLMPayloadField('Payload', OFFSET, [ MultipleTypeField([ (PacketField('LmChallengeResponse', LMv2_RESPONSE(), LMv2_RESPONSE), lambda pkt: pkt.NTLM_VERSION == 2) ], PacketField('LmChallengeResponse', LM_RESPONSE(), LM_RESPONSE)), MultipleTypeField([(PacketField( 'NtChallengeResponse', NTLMv2_RESPONSE(), NTLMv2_RESPONSE), lambda pkt: pkt.NTLM_VERSION == 2)], PacketField('NtChallengeResponse', NTLM_RESPONSE(), NTLM_RESPONSE)), _NTLMStrField('DomainName', b''), _NTLMStrField('UserName', b''), _NTLMStrField('Workstation', b''), XStrField('EncryptedRandomSessionKey', b''), ]) ] def post_build(self, pkt, pay): # type: (bytes, bytes) -> bytes return _NTML_post_build( self, pkt, self.OFFSET, { "LmChallengeResponse": 12, "NtChallengeResponse": 20, "DomainName": 28, "UserName": 36, "Workstation": 44, "EncryptedRandomSessionKey": 52 }) + pay