|
| 1 | +#!/usr/bin/env python |
| 2 | + |
| 3 | +# http://trac.secdev.org/scapy/ticket/353 |
| 4 | + |
| 5 | +# scapy.contrib.description = IKEv2 |
| 6 | +# scapy.contrib.status = loads |
| 7 | + |
| 8 | +from scapy.all import * |
| 9 | +import logging |
| 10 | + |
| 11 | + |
| 12 | +## Modified from the original ISAKMP code by Yaron Sheffer <[email protected]>, June 2010. |
| 13 | + |
| 14 | +import struct |
| 15 | +from scapy.packet import * |
| 16 | +from scapy.fields import * |
| 17 | +from scapy.ansmachine import * |
| 18 | +from scapy.layers.inet import IP,UDP |
| 19 | +from scapy.sendrecv import sr |
| 20 | + |
| 21 | +# see http://www.iana.org/assignments/ikev2-parameters for details |
| 22 | +IKEv2AttributeTypes= { "Encryption": (1, { "DES-IV64" : 1, |
| 23 | + "DES" : 2, |
| 24 | + "3DES" : 3, |
| 25 | + "RC5" : 4, |
| 26 | + "IDEA" : 5, |
| 27 | + "CAST" : 6, |
| 28 | + "Blowfish" : 7, |
| 29 | + "3IDEA" : 8, |
| 30 | + "DES-IV32" : 9, |
| 31 | + "AES-CBC" : 12, |
| 32 | + "AES-CTR" : 13, |
| 33 | + "AES-CCM-8" : 14, |
| 34 | + "AES-CCM-12" : 15, |
| 35 | + "AES-CCM-16" : 16, |
| 36 | + "AES-GCM-8ICV" : 18, |
| 37 | + "AES-GCM-12ICV" : 19, |
| 38 | + "AES-GCM-16ICV" : 20, |
| 39 | + "Camellia-CBC" : 23, |
| 40 | + "Camellia-CTR" : 24, |
| 41 | + "Camellia-CCM-8ICV" : 25, |
| 42 | + "Camellia-CCM-12ICV" : 26, |
| 43 | + "Camellia-CCM-16ICV" : 27, |
| 44 | + }, 0), |
| 45 | + "PRF": (2, {"PRF_HMAC_MD5":1, |
| 46 | + "PRF_HMAC_SHA1":2, |
| 47 | + "PRF_HMAC_TIGER":3, |
| 48 | + "PRF_AES128_XCBC":4, |
| 49 | + "PRF_HMAC_SHA2_256":5, |
| 50 | + "PRF_HMAC_SHA2_384":6, |
| 51 | + "PRF_HMAC_SHA2_512":7, |
| 52 | + "PRF_AES128_CMAC":8, |
| 53 | + }, 0), |
| 54 | + "Integrity": (3, { "HMAC-MD5-96": 1, |
| 55 | + "HMAC-SHA1-96": 2, |
| 56 | + "DES-MAC": 3, |
| 57 | + "KPDK-MD5": 4, |
| 58 | + "AES-XCBC-96": 5, |
| 59 | + "HMAC-MD5-128": 6, |
| 60 | + "HMAC-SHA1-160": 7, |
| 61 | + "AES-CMAC-96": 8, |
| 62 | + "AES-128-GMAC": 9, |
| 63 | + "AES-192-GMAC": 10, |
| 64 | + "AES-256-GMAC": 11, |
| 65 | + "SHA2-256-128": 12, |
| 66 | + "SHA2-384-192": 13, |
| 67 | + "SHA2-512-256": 14, |
| 68 | + }, 0), |
| 69 | + "GroupDesc": (4, { "768MODPgr" : 1, |
| 70 | + "1024MODPgr" : 2, |
| 71 | + "1536MODPgr" : 5, |
| 72 | + "2048MODPgr" : 14, |
| 73 | + "3072MODPgr" : 15, |
| 74 | + "4096MODPgr" : 16, |
| 75 | + "6144MODPgr" : 17, |
| 76 | + "8192MODPgr" : 18, |
| 77 | + "256randECPgr" : 19, |
| 78 | + "384randECPgr" : 20, |
| 79 | + "521randECPgr" : 21, |
| 80 | + "1024MODP160POSgr" : 22, |
| 81 | + "2048MODP224POSgr" : 23, |
| 82 | + "2048MODP256POSgr" : 24, |
| 83 | + "192randECPgr" : 25, |
| 84 | + "224randECPgr" : 26, |
| 85 | + }, 0), |
| 86 | + "Extended Sequence Number": (5, {"No ESN": 0, |
| 87 | + "ESN": 1, }, 0), |
| 88 | + } |
| 89 | + |
| 90 | +# the name 'IKEv2TransformTypes' is actually a misnomer (since the table |
| 91 | +# holds info for all IKEv2 Attribute types, not just transforms, but we'll |
| 92 | +# keep it for backwards compatibility... for now at least |
| 93 | +IKEv2TransformTypes = IKEv2AttributeTypes |
| 94 | + |
| 95 | +IKEv2TransformNum = {} |
| 96 | +for n in IKEv2TransformTypes: |
| 97 | + val = IKEv2TransformTypes[n] |
| 98 | + tmp = {} |
| 99 | + for e in val[1]: |
| 100 | + tmp[val[1][e]] = e |
| 101 | + IKEv2TransformNum[val[0]] = (n,tmp, val[2]) |
| 102 | + |
| 103 | +IKEv2Transforms = {} |
| 104 | +for n in IKEv2TransformTypes: |
| 105 | + IKEv2Transforms[IKEv2TransformTypes[n][0]]=n |
| 106 | + |
| 107 | +del(n) |
| 108 | +del(e) |
| 109 | +del(tmp) |
| 110 | +del(val) |
| 111 | + |
| 112 | +# Note: Transform and Proposal can only be used inside the SA payload |
| 113 | +IKEv2_payload_type = ["None", "", "Proposal", "Transform"] |
| 114 | + |
| 115 | +IKEv2_payload_type.extend([""] * 29) |
| 116 | +IKEv2_payload_type.extend(["SA","KE","IDi","IDr", "CERT","CERTREQ","AUTH","Nonce","Notify","Delete", |
| 117 | + "VendorID","TSi","TSr","Encrypted","CP","EAP"]) |
| 118 | + |
| 119 | +IKEv2_exchange_type = [""] * 34 |
| 120 | +IKEv2_exchange_type.extend(["IKE_SA_INIT","IKE_AUTH","CREATE_CHILD_SA", |
| 121 | + "INFORMATIONAL", "IKE_SESSION_RESUME"]) |
| 122 | + |
| 123 | + |
| 124 | +class IKEv2_class(Packet): |
| 125 | + def guess_payload_class(self, payload): |
| 126 | + np = self.next_payload |
| 127 | + logging.debug("For IKEv2_class np=%d" % np) |
| 128 | + if np == 0: |
| 129 | + return Raw |
| 130 | + elif np < len(IKEv2_payload_type): |
| 131 | + pt = IKEv2_payload_type[np] |
| 132 | + logging.debug(globals().get("IKEv2_payload_%s" % pt, IKEv2_payload)) |
| 133 | + return globals().get("IKEv2_payload_%s" % pt, IKEv2_payload) |
| 134 | + else: |
| 135 | + return IKEv2_payload |
| 136 | + |
| 137 | + |
| 138 | +class IKEv2(IKEv2_class): # rfc4306 |
| 139 | + name = "IKEv2" |
| 140 | + fields_desc = [ |
| 141 | + StrFixedLenField("init_SPI","",8), |
| 142 | + StrFixedLenField("resp_SPI","",8), |
| 143 | + ByteEnumField("next_payload",0,IKEv2_payload_type), |
| 144 | + XByteField("version",0x20), # IKEv2, right? |
| 145 | + ByteEnumField("exch_type",0,IKEv2_exchange_type), |
| 146 | + FlagsField("flags",0, 8, ["res0","res1","res2","Initiator","Version","Response","res6","res7"]), |
| 147 | + IntField("id",0), |
| 148 | + IntField("length",None) |
| 149 | + ] |
| 150 | + |
| 151 | + def guess_payload_class(self, payload): |
| 152 | + if self.flags & 1: |
| 153 | + return Raw |
| 154 | + return IKEv2_class.guess_payload_class(self, payload) |
| 155 | + |
| 156 | + def answers(self, other): |
| 157 | + if isinstance(other, IKEv2): |
| 158 | + if other.init_SPI == self.init_SPI: |
| 159 | + return 1 |
| 160 | + return 0 |
| 161 | + def post_build(self, p, pay): |
| 162 | + p += pay |
| 163 | + if self.length is None: |
| 164 | + p = p[:24]+struct.pack("!I",len(p))+p[28:] |
| 165 | + return p |
| 166 | + |
| 167 | + |
| 168 | +class IKEv2_Key_Length_Attribute(IntField): |
| 169 | + # We only support the fixed-length Key Length attribute (the only one currently defined) |
| 170 | + name="key length" |
| 171 | + def __init__(self, name): |
| 172 | + IntField.__init__(self, name, "0x800E0000") |
| 173 | + |
| 174 | + def i2h(self, pkt, x): |
| 175 | + return IntField.i2h(self, pkt, x & 0xFFFF) |
| 176 | + |
| 177 | + def h2i(self, pkt, x): |
| 178 | + return IntField.h2i(self, pkt, struct.pack("!I", 0x800E0000 | int(x, 0))) |
| 179 | + |
| 180 | + |
| 181 | +class IKEv2_Transform_ID(ShortField): |
| 182 | + def i2h(self, pkt, x): |
| 183 | + if pkt == None: |
| 184 | + return None |
| 185 | + else: |
| 186 | + map = IKEv2TransformNum[pkt.transform_type][1] |
| 187 | + return map[x] |
| 188 | + |
| 189 | + def h2i(self, pkt, x): |
| 190 | + if pkt == None: |
| 191 | + return None |
| 192 | + else: |
| 193 | + map = IKEv2TransformNum[pkt.transform_type][1] |
| 194 | + for k in keys(map): |
| 195 | + if map[k] == x: |
| 196 | + return k |
| 197 | + return None |
| 198 | + |
| 199 | +class IKEv2_payload_Transform(IKEv2_class): |
| 200 | + name = "IKE Transform" |
| 201 | + fields_desc = [ |
| 202 | + ByteEnumField("next_payload",None,{0:"last", 3:"Transform"}), |
| 203 | + ByteField("res",0), |
| 204 | + ShortField("length",8), |
| 205 | + ByteEnumField("transform_type",None,IKEv2Transforms), |
| 206 | + ByteField("res2",0), |
| 207 | + IKEv2_Transform_ID("transform_id", 0), |
| 208 | + ConditionalField(IKEv2_Key_Length_Attribute("key_length"), lambda pkt: pkt.length > 8), |
| 209 | + ] |
| 210 | + |
| 211 | +class IKEv2_payload_Proposal(IKEv2_class): |
| 212 | + name = "IKEv2 Proposal" |
| 213 | + fields_desc = [ |
| 214 | + ByteEnumField("next_payload",None,{0:"last", 2:"Proposal"}), |
| 215 | + ByteField("res",0), |
| 216 | + FieldLenField("length",None,"trans","H", adjust=lambda pkt,x:x+8), |
| 217 | + ByteField("proposal",1), |
| 218 | + ByteEnumField("proto",1,{1:"IKEv2"}), |
| 219 | + FieldLenField("SPIsize",None,"SPI","B"), |
| 220 | + ByteField("trans_nb",None), |
| 221 | + StrLenField("SPI","",length_from=lambda x:x.SPIsize), |
| 222 | + PacketLenField("trans",Raw(),IKEv2_payload_Transform,length_from=lambda x:x.length-8), |
| 223 | + ] |
| 224 | + |
| 225 | + |
| 226 | +class IKEv2_payload(IKEv2_class): |
| 227 | + name = "IKEv2 Payload" |
| 228 | + fields_desc = [ |
| 229 | + ByteEnumField("next_payload",None,IKEv2_payload_type), |
| 230 | + FlagsField("flags",0, 8, ["critical","res1","res2","res3","res4","res5","res6","res7"]), |
| 231 | + FieldLenField("length",None,"load","H", adjust=lambda pkt,x:x+4), |
| 232 | + StrLenField("load","",length_from=lambda x:x.length-4), |
| 233 | + ] |
| 234 | + |
| 235 | + |
| 236 | +class IKEv2_payload_VendorID(IKEv2_class): |
| 237 | + name = "IKEv2 Vendor ID" |
| 238 | + overload_fields = { IKEv2: { "next_payload":43 }} |
| 239 | + fields_desc = [ |
| 240 | + ByteEnumField("next_payload",None,IKEv2_payload_type), |
| 241 | + ByteField("res",0), |
| 242 | + FieldLenField("length",None,"vendorID","H", adjust=lambda pkt,x:x+4), |
| 243 | + StrLenField("vendorID","",length_from=lambda x:x.length-4), |
| 244 | + ] |
| 245 | + |
| 246 | +class IKEv2_payload_Delete(IKEv2_class): |
| 247 | + name = "IKEv2 Vendor ID" |
| 248 | + overload_fields = { IKEv2: { "next_payload":42 }} |
| 249 | + fields_desc = [ |
| 250 | + ByteEnumField("next_payload",None,IKEv2_payload_type), |
| 251 | + ByteField("res",0), |
| 252 | + FieldLenField("length",None,"vendorID","H", adjust=lambda pkt,x:x+4), |
| 253 | + StrLenField("vendorID","",length_from=lambda x:x.length-4), |
| 254 | + ] |
| 255 | + |
| 256 | +class IKEv2_payload_SA(IKEv2_class): |
| 257 | + name = "IKEv2 SA" |
| 258 | + overload_fields = { IKEv2: { "next_payload":33 }} |
| 259 | + fields_desc = [ |
| 260 | + ByteEnumField("next_payload",None,IKEv2_payload_type), |
| 261 | + ByteField("res",0), |
| 262 | + FieldLenField("length",None,"prop","H", adjust=lambda pkt,x:x+4), |
| 263 | + PacketLenField("prop",Raw(),IKEv2_payload_Proposal,length_from=lambda x:x.length-4), |
| 264 | + ] |
| 265 | + |
| 266 | +class IKEv2_payload_Nonce(IKEv2_class): |
| 267 | + name = "IKEv2 Nonce" |
| 268 | + overload_fields = { IKEv2: { "next_payload":40 }} |
| 269 | + fields_desc = [ |
| 270 | + ByteEnumField("next_payload",None,IKEv2_payload_type), |
| 271 | + ByteField("res",0), |
| 272 | + FieldLenField("length",None,"load","H", adjust=lambda pkt,x:x+4), |
| 273 | + StrLenField("load","",length_from=lambda x:x.length-4), |
| 274 | + ] |
| 275 | + |
| 276 | +class IKEv2_payload_Notify(IKEv2_class): |
| 277 | + name = "IKEv2 Notify" |
| 278 | + overload_fields = { IKEv2: { "next_payload":41 }} |
| 279 | + fields_desc = [ |
| 280 | + ByteEnumField("next_payload",None,IKEv2_payload_type), |
| 281 | + ByteField("res",0), |
| 282 | + FieldLenField("length",None,"load","H", adjust=lambda pkt,x:x+4), |
| 283 | + StrLenField("load","",length_from=lambda x:x.length-4), |
| 284 | + ] |
| 285 | + |
| 286 | +class IKEv2_payload_KE(IKEv2_class): |
| 287 | + name = "IKEv2 Key Exchange" |
| 288 | + overload_fields = { IKEv2: { "next_payload":34 }} |
| 289 | + fields_desc = [ |
| 290 | + ByteEnumField("next_payload",None,IKEv2_payload_type), |
| 291 | + ByteField("res",0), |
| 292 | + FieldLenField("length",None,"load","H", adjust=lambda pkt,x:x+6), |
| 293 | + ShortEnumField("group", 0, IKEv2TransformTypes['GroupDesc'][1]), |
| 294 | + StrLenField("load","",length_from=lambda x:x.length-6), |
| 295 | + ] |
| 296 | + |
| 297 | +class IKEv2_payload_IDi(IKEv2_class): |
| 298 | + name = "IKEv2 Identification - Initiator" |
| 299 | + overload_fields = { IKEv2: { "next_payload":35 }} |
| 300 | + fields_desc = [ |
| 301 | + ByteEnumField("next_payload",None,IKEv2_payload_type), |
| 302 | + ByteField("res",0), |
| 303 | + FieldLenField("length",None,"load","H",adjust=lambda pkt,x:x+8), |
| 304 | + ByteEnumField("IDtype",1,{1:"IPv4_addr", 11:"Key"}), |
| 305 | + ByteEnumField("ProtoID",0,{0:"Unused"}), |
| 306 | + ShortEnumField("Port",0,{0:"Unused"}), |
| 307 | +# IPField("IdentData","127.0.0.1"), |
| 308 | + StrLenField("load","",length_from=lambda x:x.length-8), |
| 309 | + ] |
| 310 | + |
| 311 | +class IKEv2_payload_IDr(IKEv2_class): |
| 312 | + name = "IKEv2 Identification - Responder" |
| 313 | + overload_fields = { IKEv2: { "next_payload":36 }} |
| 314 | + fields_desc = [ |
| 315 | + ByteEnumField("next_payload",None,IKEv2_payload_type), |
| 316 | + ByteField("res",0), |
| 317 | + FieldLenField("length",None,"load","H",adjust=lambda pkt,x:x+8), |
| 318 | + ByteEnumField("IDtype",1,{1:"IPv4_addr", 11:"Key"}), |
| 319 | + ByteEnumField("ProtoID",0,{0:"Unused"}), |
| 320 | + ShortEnumField("Port",0,{0:"Unused"}), |
| 321 | +# IPField("IdentData","127.0.0.1"), |
| 322 | + StrLenField("load","",length_from=lambda x:x.length-8), |
| 323 | + ] |
| 324 | + |
| 325 | + |
| 326 | + |
| 327 | +class IKEv2_payload_Encrypted(IKEv2_class): |
| 328 | + name = "IKEv2 Encrypted and Authenticated" |
| 329 | + overload_fields = { IKEv2: { "next_payload":46 }} |
| 330 | + fields_desc = [ |
| 331 | + ByteEnumField("next_payload",None,IKEv2_payload_type), |
| 332 | + ByteField("res",0), |
| 333 | + FieldLenField("length",None,"load","H",adjust=lambda pkt,x:x+4), |
| 334 | + StrLenField("load","",length_from=lambda x:x.length-4), |
| 335 | + ] |
| 336 | + |
| 337 | + |
| 338 | + |
| 339 | +IKEv2_payload_type_overload = {} |
| 340 | +for i in range(len(IKEv2_payload_type)): |
| 341 | + name = "IKEv2_payload_%s" % IKEv2_payload_type[i] |
| 342 | + if name in globals(): |
| 343 | + IKEv2_payload_type_overload[globals()[name]] = {"next_payload":i} |
| 344 | + |
| 345 | +del(i) |
| 346 | +del(name) |
| 347 | +IKEv2_class.overload_fields = IKEv2_payload_type_overload.copy() |
| 348 | + |
| 349 | +split_layers(UDP, ISAKMP, sport=500) |
| 350 | +split_layers(UDP, ISAKMP, dport=500) |
| 351 | + |
| 352 | +bind_layers( UDP, IKEv2, dport=500, sport=500) # TODO: distinguish IKEv1/IKEv2 |
| 353 | +bind_layers( UDP, IKEv2, dport=4500, sport=4500) |
| 354 | + |
| 355 | +def ikev2scan(ip): |
| 356 | + return sr(IP(dst=ip)/UDP()/IKEv2(init_SPI=RandString(8), |
| 357 | + exch_type=34)/IKEv2_payload_SA(prop=IKEv2_payload_Proposal())) |
| 358 | + |
| 359 | +# conf.debug_dissector = 1 |
| 360 | + |
| 361 | +interact(mydict=globals(), mybanner="IKEv2 alpha-level protocol implementation") |
0 commit comments