From 39b0c5a2b2bf8d3ef0cb656540c578a1ecf3536f Mon Sep 17 00:00:00 2001 From: gpotter2 <gabriel@potter.fr> Date: Wed, 12 Jul 2017 10:45:03 +0100 Subject: [PATCH] [Contrib] Add Authentication payload type to IKEv2 (#661) * ikev2 contrib fixup * allow unknown transform_type and transform_id * Simplify Authentication payload type * CERT payload: add x509Cert and x509CRL attributes * First re-indent * Fixed ID not being a number * Add some small IKEv2 tests * Replaced tabs by spaces * Separate classes by type * New tests- small fixes --- scapy/contrib/ikev2.py | 499 +++++++++++++++++++++++++++++++--------- scapy/contrib/ikev2.uts | 81 +++++++ 2 files changed, 477 insertions(+), 103 deletions(-) create mode 100644 scapy/contrib/ikev2.uts diff --git a/scapy/contrib/ikev2.py b/scapy/contrib/ikev2.py index c28b1aee..c511f333 100644 --- a/scapy/contrib/ikev2.py +++ b/scapy/contrib/ikev2.py @@ -25,6 +25,8 @@ import struct from scapy.packet import * from scapy.fields import * +from scapy.layers.inet6 import * +from scapy.layers.x509 import X509_Cert, X509_CRL from scapy.ansmachine import * from scapy.layers.inet import IP,UDP from scapy.layers.isakmp import ISAKMP @@ -99,98 +101,263 @@ IKEv2AttributeTypes= { "Encryption": (1, { "DES-IV64" : 1, "ESN": 1, }, 0), } +IKEv2AuthenticationTypes = { + 0 : "Reserved", + 1 : "RSA Digital Signature", + 2 : "Shared Key Message Integrity Code", + 3 : "DSS Digital Signature", + 9 : "ECDSA with SHA-256 on the P-256 curve", + 10 : "ECDSA with SHA-384 on the P-384 curve", + 11 : "ECDSA with SHA-512 on the P-521 curve", + 12 : "Generic Secure Password Authentication Method", + 13 : "NULL Authentication", + 14 : "Digital Signature" +} + IKEv2NotifyMessageTypes = { - 1 : "UNSUPPORTED_CRITICAL_PAYLOAD", - 4 : "INVALID_IKE_SPI", - 5 : "INVALID_MAJOR_VERSION", - 7 : "INVALID_SYNTAX", - 9 : "INVALID_MESSAGE_ID", - 11 : "INVALID_SPI", - 14 : "NO_PROPOSAL_CHOSEN", - 17 : "INVALID_KE_PAYLOAD", - 24 : "AUTHENTICATION_FAILED", - 34 : "SINGLE_PAIR_REQUIRED", - 35 : "NO_ADDITIONAL_SAS", - 36 : "INTERNAL_ADDRESS_FAILURE", - 37 : "FAILED_CP_REQUIRED", - 38 : "TS_UNACCEPTABLE", - 39 : "INVALID_SELECTORS", - 40 : "UNACCEPTABLE_ADDRESSES", - 41 : "UNEXPECTED_NAT_DETECTED", - 42 : "USE_ASSIGNED_HoA", - 43 : "TEMPORARY_FAILURE", - 44 : "CHILD_SA_NOT_FOUND", - 45 : "INVALID_GROUP_ID", - 46 : "AUTHORIZATION_FAILED", - 16384 : "INITIAL_CONTACT", - 16385 : "SET_WINDOW_SIZE", - 16386 : "ADDITIONAL_TS_POSSIBLE", - 16387 : "IPCOMP_SUPPORTED", - 16388 : "NAT_DETECTION_SOURCE_IP", - 16389 : "NAT_DETECTION_DESTINATION_IP", - 16390 : "COOKIE", - 16391 : "USE_TRANSPORT_MODE", - 16392 : "HTTP_CERT_LOOKUP_SUPPORTED", - 16393 : "REKEY_SA", - 16394 : "ESP_TFC_PADDING_NOT_SUPPORTED", - 16395 : "NON_FIRST_FRAGMENTS_ALSO", - 16396 : "MOBIKE_SUPPORTED", - 16397 : "ADDITIONAL_IP4_ADDRESS", - 16398 : "ADDITIONAL_IP6_ADDRESS", - 16399 : "NO_ADDITIONAL_ADDRESSES", - 16400 : "UPDATE_SA_ADDRESSES", - 16401 : "COOKIE2", - 16402 : "NO_NATS_ALLOWED", - 16403 : "AUTH_LIFETIME", - 16404 : "MULTIPLE_AUTH_SUPPORTED", - 16405 : "ANOTHER_AUTH_FOLLOWS", - 16406 : "REDIRECT_SUPPORTED", - 16407 : "REDIRECT", - 16408 : "REDIRECTED_FROM", - 16409 : "TICKET_LT_OPAQUE", - 16410 : "TICKET_REQUEST", - 16411 : "TICKET_ACK", - 16412 : "TICKET_NACK", - 16413 : "TICKET_OPAQUE", - 16414 : "LINK_ID", - 16415 : "USE_WESP_MODE", - 16416 : "ROHC_SUPPORTED", - 16417 : "EAP_ONLY_AUTHENTICATION", - 16418 : "CHILDLESS_IKEV2_SUPPORTED", - 16419 : "QUICK_CRASH_DETECTION", - 16420 : "IKEV2_MESSAGE_ID_SYNC_SUPPORTED", - 16421 : "IPSEC_REPLAY_COUNTER_SYNC_SUPPORTED", - 16422 : "IKEV2_MESSAGE_ID_SYNC", - 16423 : "IPSEC_REPLAY_COUNTER_SYNC", - 16424 : "SECURE_PASSWORD_METHODS", - 16425 : "PSK_PERSIST", - 16426 : "PSK_CONFIRM", - 16427 : "ERX_SUPPORTED", - 16428 : "IFOM_CAPABILITY", - 16429 : "SENDER_REQUEST_ID", - 16430 : "IKEV2_FRAGMENTATION_SUPPORTED", - 16431 : "SIGNATURE_HASH_ALGORITHMS", - 16432 : "CLONE_IKE_SA_SUPPORTED", - 16433 : "CLONE_IKE_SA" + 1 : "UNSUPPORTED_CRITICAL_PAYLOAD", + 4 : "INVALID_IKE_SPI", + 5 : "INVALID_MAJOR_VERSION", + 7 : "INVALID_SYNTAX", + 9 : "INVALID_MESSAGE_ID", + 11 : "INVALID_SPI", + 14 : "NO_PROPOSAL_CHOSEN", + 17 : "INVALID_KE_PAYLOAD", + 24 : "AUTHENTICATION_FAILED", + 34 : "SINGLE_PAIR_REQUIRED", + 35 : "NO_ADDITIONAL_SAS", + 36 : "INTERNAL_ADDRESS_FAILURE", + 37 : "FAILED_CP_REQUIRED", + 38 : "TS_UNACCEPTABLE", + 39 : "INVALID_SELECTORS", + 40 : "UNACCEPTABLE_ADDRESSES", + 41 : "UNEXPECTED_NAT_DETECTED", + 42 : "USE_ASSIGNED_HoA", + 43 : "TEMPORARY_FAILURE", + 44 : "CHILD_SA_NOT_FOUND", + 45 : "INVALID_GROUP_ID", + 46 : "AUTHORIZATION_FAILED", + 16384 : "INITIAL_CONTACT", + 16385 : "SET_WINDOW_SIZE", + 16386 : "ADDITIONAL_TS_POSSIBLE", + 16387 : "IPCOMP_SUPPORTED", + 16388 : "NAT_DETECTION_SOURCE_IP", + 16389 : "NAT_DETECTION_DESTINATION_IP", + 16390 : "COOKIE", + 16391 : "USE_TRANSPORT_MODE", + 16392 : "HTTP_CERT_LOOKUP_SUPPORTED", + 16393 : "REKEY_SA", + 16394 : "ESP_TFC_PADDING_NOT_SUPPORTED", + 16395 : "NON_FIRST_FRAGMENTS_ALSO", + 16396 : "MOBIKE_SUPPORTED", + 16397 : "ADDITIONAL_IP4_ADDRESS", + 16398 : "ADDITIONAL_IP6_ADDRESS", + 16399 : "NO_ADDITIONAL_ADDRESSES", + 16400 : "UPDATE_SA_ADDRESSES", + 16401 : "COOKIE2", + 16402 : "NO_NATS_ALLOWED", + 16403 : "AUTH_LIFETIME", + 16404 : "MULTIPLE_AUTH_SUPPORTED", + 16405 : "ANOTHER_AUTH_FOLLOWS", + 16406 : "REDIRECT_SUPPORTED", + 16407 : "REDIRECT", + 16408 : "REDIRECTED_FROM", + 16409 : "TICKET_LT_OPAQUE", + 16410 : "TICKET_REQUEST", + 16411 : "TICKET_ACK", + 16412 : "TICKET_NACK", + 16413 : "TICKET_OPAQUE", + 16414 : "LINK_ID", + 16415 : "USE_WESP_MODE", + 16416 : "ROHC_SUPPORTED", + 16417 : "EAP_ONLY_AUTHENTICATION", + 16418 : "CHILDLESS_IKEV2_SUPPORTED", + 16419 : "QUICK_CRASH_DETECTION", + 16420 : "IKEV2_MESSAGE_ID_SYNC_SUPPORTED", + 16421 : "IPSEC_REPLAY_COUNTER_SYNC_SUPPORTED", + 16422 : "IKEV2_MESSAGE_ID_SYNC", + 16423 : "IPSEC_REPLAY_COUNTER_SYNC", + 16424 : "SECURE_PASSWORD_METHODS", + 16425 : "PSK_PERSIST", + 16426 : "PSK_CONFIRM", + 16427 : "ERX_SUPPORTED", + 16428 : "IFOM_CAPABILITY", + 16429 : "SENDER_REQUEST_ID", + 16430 : "IKEV2_FRAGMENTATION_SUPPORTED", + 16431 : "SIGNATURE_HASH_ALGORITHMS", + 16432 : "CLONE_IKE_SA_SUPPORTED", + 16433 : "CLONE_IKE_SA" } IKEv2CertificateEncodings = { - 1 : "PKCS #7 wrapped X.509 certificate", - 2 : "PGP Certificate", - 3 : "DNS Signed Key", - 4 : "X.509 Certificate - Signature", - 6 : "Kerberos Token", - 7 : "Certificate Revocation List (CRL)", - 8 : "Authority Revocation List (ARL)", - 9 : "SPKI Certificate", - 10 : "X.509 Certificate - Attribute", - 11 : "Raw RSA Key", - 12 : "Hash and URL of X.509 certificate", - 13 : "Hash and URL of X.509 bundle" + 1 : "PKCS #7 wrapped X.509 certificate", + 2 : "PGP Certificate", + 3 : "DNS Signed Key", + 4 : "X.509 Certificate - Signature", + 6 : "Kerberos Token", + 7 : "Certificate Revocation List (CRL)", + 8 : "Authority Revocation List (ARL)", + 9 : "SPKI Certificate", + 10 : "X.509 Certificate - Attribute", + 11 : "Raw RSA Key", + 12 : "Hash and URL of X.509 certificate", + 13 : "Hash and URL of X.509 bundle" +} + +IKEv2TrafficSelectorTypes = { + 7 : "TS_IPV4_ADDR_RANGE", + 8 : "TS_IPV6_ADDR_RANGE", + 9 : "TS_FC_ADDR_RANGE" } -# the name 'IKEv2TransformTypes' is actually a misnomer (since the table -# holds info for all IKEv2 Attribute types, not just transforms, but we'll +IPProtocolIDs = { + 0 : "All protocols", + 1 : "Internet Control Message Protocol", + 2 : "Internet Group Management Protocol", + 3 : "Gateway-to-Gateway Protocol", + 4 : "IP in IP (encapsulation)", + 5 : "Internet Stream Protocol", + 6 : "Transmission Control Protocol", + 7 : "Core-based trees", + 8 : "Exterior Gateway Protocol", + 9 : "Interior Gateway Protocol (any private interior gateway (used by Cisco for their IGRP))", + 10 : "BBN RCC Monitoring", + 11 : "Network Voice Protocol", + 12 : "Xerox PUP", + 13 : "ARGUS", + 14 : "EMCON", + 15 : "Cross Net Debugger", + 16 : "Chaos", + 17 : "User Datagram Protocol", + 18 : "Multiplexing", + 19 : "DCN Measurement Subsystems", + 20 : "Host Monitoring Protocol", + 21 : "Packet Radio Measurement", + 22 : "XEROX NS IDP", + 23 : "Trunk-1", + 24 : "Trunk-2", + 25 : "Leaf-1", + 26 : "Leaf-2", + 27 : "Reliable Datagram Protocol", + 28 : "Internet Reliable Transaction Protocol", + 29 : "ISO Transport Protocol Class 4", + 30 : "Bulk Data Transfer Protocol", + 31 : "MFE Network Services Protocol", + 32 : "MERIT Internodal Protocol", + 33 : "Datagram Congestion Control Protocol", + 34 : "Third Party Connect Protocol", + 35 : "Inter-Domain Policy Routing Protocol", + 36 : "Xpress Transport Protocol", + 37 : "Datagram Delivery Protocol", + 38 : "IDPR Control Message Transport Protocol", + 39 : "TP++ Transport Protocol", + 40 : "IL Transport Protocol", + 41 : "IPv6 Encapsulation", + 42 : "Source Demand Routing Protocol", + 43 : "Routing Header for IPv6", + 44 : "Fragment Header for IPv6", + 45 : "Inter-Domain Routing Protocol", + 46 : "Resource Reservation Protocol", + 47 : "Generic Routing Encapsulation", + 48 : "Mobile Host Routing Protocol", + 49 : "BNA", + 50 : "Encapsulating Security Payload", + 51 : "Authentication Header", + 52 : "Integrated Net Layer Security Protocol", + 53 : "SwIPe", + 54 : "NBMA Address Resolution Protocol", + 55 : "IP Mobility (Min Encap)", + 56 : "Transport Layer Security Protocol (using Kryptonet key management)", + 57 : "Simple Key-Management for Internet Protocol", + 58 : "ICMP for IPv6", + 59 : "No Next Header for IPv6", + 60 : "Destination Options for IPv6", + 61 : "Any host internal protocol", + 62 : "CFTP", + 63 : "Any local network", + 64 : "SATNET and Backroom EXPAK", + 65 : "Kryptolan", + 66 : "MIT Remote Virtual Disk Protocol", + 67 : "Internet Pluribus Packet Core", + 68 : "Any distributed file system", + 69 : "SATNET Monitoring", + 70 : "VISA Protocol", + 71 : "Internet Packet Core Utility", + 72 : "Computer Protocol Network Executive", + 73 : "Computer Protocol Heart Beat", + 74 : "Wang Span Network", + 75 : "Packet Video Protocol", + 76 : "Backroom SATNET Monitoring", + 77 : "SUN ND PROTOCOL-Temporary", + 78 : "WIDEBAND Monitoring", + 79 : "WIDEBAND EXPAK", + 80 : "International Organization for Standardization Internet Protocol", + 81 : "Versatile Message Transaction Protocol", + 82 : "Secure Versatile Message Transaction Protocol", + 83 : "VINES", + 84 : "Internet Protocol Traffic Manager", + 85 : "NSFNET-IGP", + 86 : "Dissimilar Gateway Protocol", + 87 : "TCF", + 88 : "EIGRP", + 89 : "Open Shortest Path First", + 90 : "Sprite RPC Protocol", + 91 : "Locus Address Resolution Protocol", + 92 : "Multicast Transport Protocol", + 93 : "AX.25", + 94 : "IP-within-IP Encapsulation Protocol", + 95 : "Mobile Internetworking Control Protocol", + 96 : "Semaphore Communications Sec. Pro", + 97 : "Ethernet-within-IP Encapsulation", + 98 : "Encapsulation Header", + 99 : "Any private encryption scheme", + 100 : "GMTP", + 101 : "Ipsilon Flow Management Protocol", + 102 : "PNNI over IP", + 103 : "Protocol Independent Multicast", + 104 : "IBM's ARIS (Aggregate Route IP Switching) Protocol", + 105 : "SCPS (Space Communications Protocol Standards)", + 106 : "QNX", + 107 : "Active Networks", + 108 : "IP Payload Compression Protocol", + 109 : "Sitara Networks Protocol", + 110 : "Compaq Peer Protocol", + 111 : "IPX in IP", + 112 : "Virtual Router Redundancy Protocol, Common Address Redundancy Protocol (not IANA assigned)", + 113 : "PGM Reliable Transport Protocol", + 114 : "Any 0-hop protocol", + 115 : "Layer Two Tunneling Protocol Version 3", + 116 : "D-II Data Exchange (DDX)", + 117 : "Interactive Agent Transfer Protocol", + 118 : "Schedule Transfer Protocol", + 119 : "SpectraLink Radio Protocol", + 120 : "Universal Transport Interface Protocol", + 121 : "Simple Message Protocol", + 122 : "Simple Multicast Protocol", + 123 : "Performance Transparency Protocol", + 124 : "Intermediate System to Intermediate System (IS-IS) Protocol over IPv4", + 125 : "Flexible Intra-AS Routing Environment", + 126 : "Combat Radio Transport Protocol", + 127 : "Combat Radio User Datagram", + 128 : "Service-Specific Connection-Oriented Protocol in a Multilink and Connectionless Environment", + 129 : "IPLT", + 130 : "Secure Packet Shield", + 131 : "Private IP Encapsulation within IP", + 132 : "Stream Control Transmission Protocol", + 133 : "Fibre Channel", + 134 : "Reservation Protocol (RSVP) End-to-End Ignore", + 135 : "Mobility Extension Header for IPv6", + 136 : "Lightweight User Datagram Protocol", + 137 : "Multiprotocol Label Switching Encapsulated in IP", + 138 : "MANET Protocols", + 139 : "Host Identity Protocol", + 140 : "Site Multihoming by IPv6 Intermediation", + 141 : "Wrapped Encapsulating Security Payload", + 142 : "Robust Header Compression", +} + +# the name 'IKEv2TransformTypes' is actually a misnomer (since the table +# holds info for all IKEv2 Attribute types, not just transforms, but we'll # keep it for backwards compatibility... for now at least IKEv2TransformTypes = IKEv2AttributeTypes @@ -243,11 +410,11 @@ class IKEv2(IKEv2_class): # rfc4306 StrFixedLenField("init_SPI","",8), StrFixedLenField("resp_SPI","",8), ByteEnumField("next_payload",0,IKEv2_payload_type), - XByteField("version",0x20), # IKEv2, right? + XByteField("version", 0x20), ByteEnumField("exch_type",0,IKEv2_exchange_type), FlagsField("flags",0, 8, ["res0","res1","res2","Initiator","Version","Response","res6","res7"]), IntField("id",0), - IntField("length",None) + IntField("length",None) # Length of total message: packets + all payloads ] def guess_payload_class(self, payload): @@ -295,13 +462,13 @@ class IKEv2_payload_Proposal(IKEv2_class): fields_desc = [ ByteEnumField("next_payload",None,{0:"last", 2:"Proposal"}), ByteField("res",0), - FieldLenField("length",None,"trans","H", adjust=lambda pkt,x:x+8), + FieldLenField("length",None,"trans","H", adjust=lambda pkt,x:x+8+(pkt.SPIsize if pkt.SPIsize else 0)), ByteField("proposal",1), - ByteEnumField("proto",1,{1:"IKEv2"}), + ByteEnumField("proto",1,{1:"IKEv2", 2:"AH", 3:"ESP"}), FieldLenField("SPIsize",None,"SPI","B"), ByteField("trans_nb",None), - StrLenField("SPI","",length_from=lambda x:x.SPIsize), - PacketLenField("trans",conf.raw_layer(),IKEv2_payload_Transform,length_from=lambda x:x.length-8), + StrLenField("SPI","",length_from=lambda pkt:pkt.SPIsize), + PacketLenField("trans",conf.raw_layer(),IKEv2_payload_Transform,length_from=lambda pkt:pkt.length-8-pkt.SPIsize), ] @@ -315,6 +482,18 @@ class IKEv2_payload(IKEv2_class): ] +class IKEv2_payload_AUTH(IKEv2_class): + name = "IKEv2 Authentication" + overload_fields = { IKEv2: { "next_payload":39 }} + fields_desc = [ + ByteEnumField("next_payload",None,IKEv2_payload_type), + ByteField("res",0), + FieldLenField("length",None,"load","H", adjust=lambda pkt,x:x+8), + ByteEnumField("auth_type",None,IKEv2AuthenticationTypes), + X3BytesField("res2",0), + StrLenField("load","",length_from=lambda x:x.length-8), + ] + class IKEv2_payload_VendorID(IKEv2_class): name = "IKEv2 Vendor ID" overload_fields = { IKEv2: { "next_payload":43 }} @@ -325,6 +504,94 @@ class IKEv2_payload_VendorID(IKEv2_class): StrLenField("vendorID","",length_from=lambda x:x.length-4), ] +class TrafficSelector(Packet): + @classmethod + def dispatch_hook(cls, _pkt=None, *args, **kargs): + if _pkt and len(_pkt) >= 16: + ts_type = struct.unpack("!B", _pkt[0:1])[0] + if ts_type == 7: + return IPv4TrafficSelector + elif ts_type == 8: + return IPv6TrafficSelector + elif ts_type == 9: + return EncryptedTrafficSelector + else: + return RawTrafficSelector + return IPv4TrafficSelector + +class IPv4TrafficSelector(TrafficSelector): + name = "IKEv2 IPv4 Traffic Selector" + fields_desc = [ + ByteEnumField("TS_type",7,IKEv2TrafficSelectorTypes), + ByteEnumField("IP_protocol_ID",None,IPProtocolIDs), + ShortField("length",16), + ShortField("start_port",0), + ShortField("end_port",65535), + IPField("starting_address_v4","192.168.0.1"), + IPField("ending_address_v4","192.168.0.255"), + ] + +class IPv6TrafficSelector(TrafficSelector): + name = "IKEv2 IPv6 Traffic Selector" + fields_desc = [ + ByteEnumField("TS_type",8,IKEv2TrafficSelectorTypes), + ByteEnumField("IP_protocol_ID",None,IPProtocolIDs), + ShortField("length",20), + ShortField("start_port",0), + ShortField("end_port",65535), + IP6Field("starting_address_v6","2001::"), + IP6Field("ending_address_v6","2001::"), + ] + +class EncryptedTrafficSelector(TrafficSelector): + name = "IKEv2 Encrypted Traffic Selector" + fields_desc = [ + ByteEnumField("TS_type",9,IKEv2TrafficSelectorTypes), + ByteEnumField("IP_protocol_ID",None,IPProtocolIDs), + ShortField("length",16), + ByteField("res",0), + X3BytesField("starting_address_FC",0), + ByteField("res2",0), + X3BytesField("ending_address_FC",0), + ByteField("starting_R_CTL",0), + ByteField("ending_R_CTL",0), + ByteField("starting_type",0), + ByteField("ending_type",0), + ] + +class RawTrafficSelector(TrafficSelector): + name = "IKEv2 Encrypted Traffic Selector" + fields_desc = [ + ByteEnumField("TS_type",None,IKEv2TrafficSelectorTypes), + ByteEnumField("IP_protocol_ID",None,IPProtocolIDs), + FieldLenField("length",None,"load","H", adjust=lambda pkt,x:x+4), + PacketField("load", "", Raw) + ] + +class IKEv2_payload_TSi(IKEv2_class): + name = "IKEv2 Traffic Selector - Initiator" + overload_fields = { IKEv2: { "next_payload":44 }} + fields_desc = [ + ByteEnumField("next_payload",None,IKEv2_payload_type), + ByteField("res",0), + FieldLenField("length",None,"traffic_selector","H", adjust=lambda pkt,x:x+8), + ByteField("number_of_TSs",0), + X3BytesField("res2",0), + PacketListField("traffic_selector",None,TrafficSelector,length_from=lambda x:x.length-8,count_from=lambda x:x.number_of_TSs), + ] + +class IKEv2_payload_TSr(IKEv2_class): + name = "IKEv2 Traffic Selector - Responder" + overload_fields = { IKEv2: { "next_payload":45 }} + fields_desc = [ + ByteEnumField("next_payload",None,IKEv2_payload_type), + ByteField("res",0), + FieldLenField("length",None,"traffic_selector","H", adjust=lambda pkt,x:x+8), + ByteField("number_of_TSs",0), + X3BytesField("res2",0), + PacketListField("traffic_selector",None,TrafficSelector,length_from=lambda x:x.length-8,count_from=lambda x:x.number_of_TSs), + ] + class IKEv2_payload_Delete(IKEv2_class): name = "IKEv2 Vendor ID" overload_fields = { IKEv2: { "next_payload":42 }} @@ -409,8 +676,6 @@ class IKEv2_payload_IDr(IKEv2_class): StrLenField("load","",length_from=lambda x:x.length-8), ] - - class IKEv2_payload_Encrypted(IKEv2_class): name = "IKEv2 Encrypted and Authenticated" overload_fields = { IKEv2: { "next_payload":46 }} @@ -432,11 +697,44 @@ class IKEv2_payload_CERTREQ(IKEv2_class): ] class IKEv2_payload_CERT(IKEv2_class): + @classmethod + def dispatch_hook(cls, _pkt=None, *args, **kargs): + if _pkt and len(_pkt) >= 16: + ts_type = struct.unpack("!B", _pkt[4:5])[0] + if ts_type == 4: + return IKEv2_payload_CERT_CRT + elif ts_type == 7: + return IKEv2_payload_CERT_CRL + else: + return IKEv2_payload_CERT_STR + return IKEv2_payload_CERT_STR + +class IKEv2_payload_CERT_CRT(IKEv2_payload_CERT): name = "IKEv2 Certificate" fields_desc = [ ByteEnumField("next_payload",None,IKEv2_payload_type), ByteField("res",0), - FieldLenField("length",None,"cert_data","H",adjust=lambda pkt,x:x+5), + FieldLenField("length",None,"x509Cert","H",adjust=lambda pkt,x: x+len(pkt.x509Cert)+5), + ByteEnumField("cert_type",4,IKEv2CertificateEncodings), + PacketLenField("x509Cert", X509_Cert(''), X509_Cert, length_from=lambda x:x.length-5), + ] + +class IKEv2_payload_CERT_CRL(IKEv2_payload_CERT): + name = "IKEv2 Certificate" + fields_desc = [ + ByteEnumField("next_payload",None,IKEv2_payload_type), + ByteField("res",0), + FieldLenField("length",None,"x509CRL","H",adjust=lambda pkt,x: x+len(pkt.x509CRL)+5), + ByteEnumField("cert_type",7,IKEv2CertificateEncodings), + PacketLenField("x509CRL", X509_CRL(''), X509_CRL, length_from=lambda x:x.length-5), + ] + +class IKEv2_payload_CERT_STR(IKEv2_payload_CERT): + name = "IKEv2 Certificate" + fields_desc = [ + ByteEnumField("next_payload",None,IKEv2_payload_type), + ByteField("res",0), + FieldLenField("length",None,"cert_data","H",adjust=lambda pkt,x: x+5), ByteEnumField("cert_type",0,IKEv2CertificateEncodings), StrLenField("cert_data","",length_from=lambda x:x.length-5), ] @@ -456,12 +754,7 @@ split_layers(UDP, ISAKMP, dport=500) bind_layers( UDP, IKEv2, dport=500, sport=500) # TODO: distinguish IKEv1/IKEv2 bind_layers( UDP, IKEv2, dport=4500, sport=4500) -def ikev2scan(ip): +def ikev2scan(ip, **kwargs): + """Send a IKEv2 SA to an IP and wait for answers.""" return sr(IP(dst=ip)/UDP()/IKEv2(init_SPI=RandString(8), - exch_type=34)/IKEv2_payload_SA(prop=IKEv2_payload_Proposal())) - -# conf.debug_dissector = 1 - -if __name__ == "__main__": - from scapy.main import interact - interact(mydict=globals(), mybanner="IKEv2 alpha-level protocol implementation") + exch_type=34)/IKEv2_payload_SA(prop=IKEv2_payload_Proposal()), **kwargs) diff --git a/scapy/contrib/ikev2.uts b/scapy/contrib/ikev2.uts new file mode 100644 index 00000000..dd6ebd6a --- /dev/null +++ b/scapy/contrib/ikev2.uts @@ -0,0 +1,81 @@ +% Ikev2 Tests +* Tests for the Ikev2 layer + ++ Basic Layer Tests + += Ikev2 build + +a = IKEv2() +assert str(a) == '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\x1c' + += Ikev2 dissection + +a = IKEv2("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00! \x00\x00\x00\x00\x00\x00\x00\x00\x000\x00\x00\x00\x14\x00\x00\x00\x10\x01\x01\x00\x00\x00\x00\x00\x08\x02\x00\x00\x03") +assert a[IKEv2_payload_Transform].transform_type == 2 +assert a[IKEv2_payload_Transform].transform_id == 3 +assert a.next_payload == 33 +assert a[IKEv2_payload_SA].next_payload == 0 +assert a[IKEv2_payload_Proposal].next_payload == 0 +assert a[IKEv2_payload_Proposal].proposal == 1 +assert a[IKEv2_payload_Transform].next_payload == 0 +a[IKEv2_payload_Transform].show() + + += Build Ikev2 SA request packet + +a = IKEv2(init_SPI="MySPI",exch_type=34)/IKEv2_payload_SA(prop=IKEv2_payload_Proposal()) +assert str(a) == b'MySPI\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00! "\x00\x00\x00\x00\x00\x00\x00\x00(\x00\x00\x00\x0c\x00\x00\x00\x08\x01\x01\x00\x00' + + +## packets taken from +## https://github.com/wireshark/wireshark/blob/master/test/captures/ikev2-decrypt-aes128ccm12.pcap + += Dissect Initiator Request + +a = Ether(b'\x00!k\x91#H\xb8\'\xeb\xa6XI\x08\x00E\x00\x01\x14u\xc2@\x00@\x11@\xb6\xc0\xa8\x01\x02\xc0\xa8\x01\x0e\x01\xf4\x01\xf4\x01\x00=8\xeahM!Yz\xfd6\x00\x00\x00\x00\x00\x00\x00\x00! "\x08\x00\x00\x00\x00\x00\x00\x00\xf8"\x00\x00(\x00\x00\x00$\x01\x01\x00\x03\x03\x00\x00\x0c\x01\x00\x00\x0f\x80\x0e\x00\x80\x03\x00\x00\x08\x02\x00\x00\x05\x00\x00\x00\x08\x04\x00\x00\x13(\x00\x00H\x00\x13\x00\x002\xc6\xdf\xfe\\C\xb0\xd5\x81\x1f~\xaa\xa8L\x9fx\xbf\x99\xb9\x06\x9c+\x07.\x0b\x82\xf4k\xf6\xf6m\xd4_\x97\xef\x89\xee(_\xd5\xdfRzDwkR\x9f\xc9\xd8\xa9\t\xd8B\xa6\xfbY\xb9j\tS\x95ar)\x00\x00$\xb6UF-oKf\xf8r\xcc\xd7\xf0\xf4\xb4\x85w2\x92\x139\xcb\xaaR7\xed\xba$O&+h#)\x00\x00\x1c\x00\x00@\x04\x94\x9c\x9d\xb5s\x9du\xa9t\xa4\x9c\x18F\x186\x9b4\xb7\xf9B)\x00\x00\x1c\x00\x00@\x05>r\x1bF\xbe\x07\xd51\x11B]\x7f\x80\xd2\xc6\xe2 \xc6\x07.\x00\x00\x00\x10\x00\x00@/\x00\x01\x00\x02\x00\x03\x00\x04') +assert a[IKEv2_payload_SA].prop.trans.transform_id == 15 +assert a[IKEv2_payload_Notify].next_payload == 41 +assert IP(a[IKEv2_payload_Notify].load).src == "70.24.54.155" +assert IP(a[IKEv2_payload_Notify].payload.load).dst == "32.198.7.46" + += Dissect Responder Response + +b = Ether(b'\xb8\'\xeb\xa6XI\x00!k\x91#H\x08\x00E\x00\x01\x0c\xd2R@\x00@\x11\xe4-\xc0\xa8\x01\x0e\xc0\xa8\x01\x02\x01\xf4\x01\xf4\x00\xf8\x07\xdd\xeahM!Yz\xfd6\xd9\xfe*\xb2-\xac#\xac! " \x00\x00\x00\x00\x00\x00\x00\xf0"\x00\x00(\x00\x00\x00$\x01\x01\x00\x03\x03\x00\x00\x0c\x01\x00\x00\x0f\x80\x0e\x00\x80\x03\x00\x00\x08\x02\x00\x00\x05\x00\x00\x00\x08\x04\x00\x00\x13(\x00\x00H\x00\x13\x00\x00,f\xbe\xad\xb6\xce\x855\xd6!\x8c\xb4\x01\xaaZ\x1e\xb4\x03[\x97\xca\xdd\xaf67J\x97\x9c\x04F\xb8\x80\x05\x06\xbf\x9do\x95\tR2k\xf3\x01\x19\x13\xda\x93\xbb\x8e@\xf8\x157k\xe1\xa0h\x01\xc0\xa6>;T)\x00\x00$\x9e]&sy\xe6\x81\xe7\xd3\x8d\x81\xc7\x10\xd3\x83@\x1d\xe7\xe3`{\x92m\x90\xa9\x95\x8a\xdc\xb5(1\xaa)\x00\x00\x1c\x00\x00@\x04z\x07\x85\'=Y 8)\xa6\x97U\x0f1\xcb\xb9N\xb7+C)\x00\x00\x1c\x00\x00@\x05\xc3\xe5\x8a\x8c\xc9\x93<\xe0\xb7\x8f*P\xe8\xde\x80\x13N\x12\xce1\x00\x00\x00\x08\x00\x00@\x14') +assert b[UDP].dport == 500 +assert b[IKEv2_payload_KE].load == b',f\xbe\xad\xb6\xce\x855\xd6!\x8c\xb4\x01\xaaZ\x1e\xb4\x03[\x97\xca\xdd\xaf67J\x97\x9c\x04F\xb8\x80\x05\x06\xbf\x9do\x95\tR2k\xf3\x01\x19\x13\xda\x93\xbb\x8e@\xf8\x157k\xe1\xa0h\x01\xc0\xa6>;T' +assert b[IKEv2_payload_Nonce].payload.type == 16388 +assert b[IKEv2_payload_Nonce].payload.payload.payload.next_payload == 0 + += Dissect Encrypted Inititor Request + +a = Ether(b"\x00!k\x91#H\xb8'\xeb\xa6XI\x08\x00E\x00\x00Yu\xe2@\x00@\x11AQ\xc0\xa8\x01\x02\xc0\xa8\x01\x0e\x01\xf4\x01\xf4\x00E}\xe0\xeahM!Yz\xfd6\xd9\xfe*\xb2-\xac#\xac. %\x08\x00\x00\x00\x02\x00\x00\x00=*\x00\x00!\xcc\xa0\xb3]\xe5\xab\xc5\x1c\x99\x87\xcb\xf1\xf5\xec\xff!\x0e\xb7g\xcd\xb8Qy8;\x96Mx\xe2") +assert a[IKEv2_payload_Encrypted].next_payload == 42 +assert a[IKEv2_payload_Encrypted].load == b'\xcc\xa0\xb3]\xe5\xab\xc5\x1c\x99\x87\xcb\xf1\xf5\xec\xff!\x0e\xb7g\xcd\xb8Qy8;\x96Mx\xe2' + += Dissect Encrypted Responder Response + +b = Ether(b"\xb8'\xeb\xa6XI\x00!k\x91#H\x08\x00E\x00\x00Q\xd5y@\x00@\x11\xe1\xc1\xc0\xa8\x01\x0e\xc0\xa8\x01\x02\x01\xf4\x01\xf4\x00=\xf9F\xeahM!Yz\xfd6\xd9\xfe*\xb2-\xac#\xac. % \x00\x00\x00\x02\x00\x00\x005\x00\x00\x00\x19\xa8\x0c\x95{\xac\x15\xc3\xf8\xaf\xdf1Z\x81\xccK|@\xe8f\rD") +assert b[IKEv2].init_SPI == b'\xeahM!Yz\xfd6' +assert b[IKEv2].resp_SPI == b'\xd9\xfe*\xb2-\xac#\xac' +assert b[IKEv2].next_payload == 46 +assert b[IKEv2_payload_Encrypted].load == b'\xa8\x0c\x95{\xac\x15\xc3\xf8\xaf\xdf1Z\x81\xccK|@\xe8f\rD' + += Test Certs detection + +a = IKEv2_payload_CERT(str(IKEv2_payload_CERT_CRL())) +b = IKEv2_payload_CERT(str(IKEv2_payload_CERT_STR())) +c = IKEv2_payload_CERT(str(IKEv2_payload_CERT_CRT())) + +assert isinstance(a, IKEv2_payload_CERT_CRL) +assert isinstance(b, IKEv2_payload_CERT_STR) +assert isinstance(c, IKEv2_payload_CERT_CRT) + += Test TrafficSelector detection + +a = TrafficSelector(str(IPv4TrafficSelector())) +b = TrafficSelector(str(IPv6TrafficSelector())) +c = TrafficSelector(str(EncryptedTrafficSelector())) + +assert isinstance(a, IPv4TrafficSelector) +assert isinstance(b, IPv6TrafficSelector) +assert isinstance(c, EncryptedTrafficSelector) \ No newline at end of file -- GitLab