## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ ISAKMP (Internet Security Association and Key Management Protocol). """ import struct from scapy.packet import * from scapy.fields import * from scapy.ansmachine import * from scapy.layers.inet import IP,UDP from scapy.sendrecv import sr # see http://www.iana.org/assignments/ipsec-registry for details ISAKMPAttributeTypes= { "Encryption": (1, { "DES-CBC" : 1, "IDEA-CBC" : 2, "Blowfish-CBC" : 3, "RC5-R16-B64-CBC" : 4, "3DES-CBC" : 5, "CAST-CBC" : 6, "AES-CBC" : 7, "CAMELLIA-CBC" : 8, }, 0), "Hash": (2, { "MD5": 1, "SHA": 2, "Tiger": 3, "SHA2-256": 4, "SHA2-384": 5, "SHA2-512": 6,}, 0), "Authentication":(3, { "PSK": 1, "DSS": 2, "RSA Sig": 3, "RSA Encryption": 4, "RSA Encryption Revised": 5, "ElGamal Encryption": 6, "ElGamal Encryption Revised": 7, "ECDSA Sig": 8, "HybridInitRSA": 64221, "HybridRespRSA": 64222, "HybridInitDSS": 64223, "HybridRespDSS": 64224, "XAUTHInitPreShared": 65001, "XAUTHRespPreShared": 65002, "XAUTHInitDSS": 65003, "XAUTHRespDSS": 65004, "XAUTHInitRSA": 65005, "XAUTHRespRSA": 65006, "XAUTHInitRSAEncryption": 65007, "XAUTHRespRSAEncryption": 65008, "XAUTHInitRSARevisedEncryption": 65009, "XAUTHRespRSARevisedEncryptio": 65010, }, 0), "GroupDesc": (4, { "768MODPgr" : 1, "1024MODPgr" : 2, "EC2Ngr155" : 3, "EC2Ngr185" : 4, "1536MODPgr" : 5, "2048MODPgr" : 14, "3072MODPgr" : 15, "4096MODPgr" : 16, "6144MODPgr" : 17, "8192MODPgr" : 18, }, 0), "GroupType": (5, {"MODP": 1, "ECP": 2, "EC2N": 3}, 0), "GroupPrime": (6, {}, 1), "GroupGenerator1":(7, {}, 1), "GroupGenerator2":(8, {}, 1), "GroupCurveA": (9, {}, 1), "GroupCurveB": (10, {}, 1), "LifeType": (11, {"Seconds": 1, "Kilobytes": 2, }, 0), "LifeDuration": (12, {}, 1), "PRF": (13, {}, 0), "KeyLength": (14, {}, 0), "FieldSize": (15, {}, 0), "GroupOrder": (16, {}, 1), } # the name 'ISAKMPTransformTypes' is actually a misnomer (since the table # holds info for all ISAKMP Attribute types, not just transforms, but we'll # keep it for backwards compatibility... for now at least ISAKMPTransformTypes = ISAKMPAttributeTypes ISAKMPTransformNum = {} for n in ISAKMPTransformTypes: val = ISAKMPTransformTypes[n] tmp = {} for e in val[1]: tmp[val[1][e]] = e ISAKMPTransformNum[val[0]] = (n,tmp, val[2]) del(n) del(e) del(tmp) del(val) class ISAKMPTransformSetField(StrLenField): islist=1 #def type2num(self, (typ,val)): def type2num(self, typval): typ = typval[0] val = typval[1] type_val,enc_dict,tlv = ISAKMPTransformTypes.get(typval[0], (typval[0],{},0)) val = enc_dict.get(val, val) s = b"" if (val & ~0xffff): if not tlv: warning("%r should not be TLV but is too big => using TLV encoding" % typval[0]) n = 0 while val: s = bytes([(val&0xff)])+s val >>= 8 n += 1 val = n else: type_val |= 0x8000 return struct.pack("!HH",type_val, val)+s def num2type(self, typ, enc): val = ISAKMPTransformNum.get(typ,(typ,{})) enc = val[1].get(enc,enc) return (val[0],enc) def i2m(self, pkt, i): if i is None: return b"" i = map(self.type2num, i) return b"".join(i) def m2i(self, pkt, m): # I try to ensure that we don't read off the end of our packet based # on bad length fields we're provided in the packet. There are still # conditions where struct.unpack() may not get enough packet data, but # worst case that should result in broken attributes (which would # be expected). (wam) lst = [] while len(m) >= 4: trans_type, = struct.unpack("!H", m[:2]) is_tlv = not (trans_type & 0x8000) if is_tlv: # We should probably check to make sure the attribute type we # are looking at is allowed to have a TLV format and issue a # warning if we're given an TLV on a basic attribute. value_len, = struct.unpack("!H", m[2:4]) if value_len+4 > len(m): warning("Bad length for ISAKMP tranform type=%#6x" % trans_type) value = m[4:4+value_len] r = 0 for i in struct.unpack("!%s" % ("B"*len(value),), value): r = (r << 8) | i value = r #value = reduce(lambda x,y: (x<<8)|y, struct.unpack("!%s" % ("B"*len(value),), value),0) else: trans_type &= 0x7fff value_len=0 value, = struct.unpack("!H", m[2:4]) m=m[4+value_len:] lst.append(self.num2type(trans_type, value)) if len(m) > 0: warning("Extra bytes after ISAKMP transform dissection [%r]" % m) return lst ISAKMP_payload_type = ["None","SA","Proposal","Transform","KE","ID","CERT","CR","Hash", "SIG","Nonce","Notification","Delete","VendorID"] ISAKMP_exchange_type = ["None","base","identity prot.", "auth only", "aggressive", "info"] class ISAKMP_class(Packet): def guess_payload_class(self, payload): np = self.next_payload if np == 0: return conf.raw_layer elif np < len(ISAKMP_payload_type): pt = ISAKMP_payload_type[np] return globals().get("ISAKMP_payload_%s" % pt, ISAKMP_payload) else: return ISAKMP_payload class ISAKMP(ISAKMP_class): # rfc2408 name = "ISAKMP" fields_desc = [ StrFixedLenField("init_cookie","",8), StrFixedLenField("resp_cookie","",8), ByteEnumField("next_payload",0,ISAKMP_payload_type), XByteField("version",0x10), ByteEnumField("exch_type",0,ISAKMP_exchange_type), FlagsField("flags",0, 8, ["encryption","commit","auth_only","res3","res4","res5","res6","res7"]), # XXX use a Flag field IntField("id",0), IntField("length",None) ] def guess_payload_class(self, payload): if self.flags & 1: return conf.raw_layer return ISAKMP_class.guess_payload_class(self, payload) def answers(self, other): if isinstance(other, ISAKMP): if other.init_cookie == self.init_cookie: return 1 return 0 def post_build(self, p, pay): p += pay if self.length is None: p = p[:24]+struct.pack("!I",len(p))+p[28:] return p class ISAKMP_payload_Transform(ISAKMP_class): name = "IKE Transform" fields_desc = [ ByteEnumField("next_payload",None,ISAKMP_payload_type), ByteField("res",0), # ShortField("len",None), ShortField("length",None), ByteField("num",None), ByteEnumField("id",1,{1:"KEY_IKE"}), ShortField("res2",0), ISAKMPTransformSetField("transforms",None,length_from=lambda x:x.length-8) # XIntField("enc",0x80010005L), # XIntField("hash",0x80020002L), # XIntField("auth",0x80030001L), # XIntField("group",0x80040002L), # XIntField("life_type",0x800b0001L), # XIntField("durationh",0x000c0004L), # XIntField("durationl",0x00007080L), ] def post_build(self, p, pay): if self.length is None: l = len(p) p = p[:2]+bytes([((l>>8)&0xff),(l&0xff)])+p[4:] p += pay return p class ISAKMP_payload_Proposal(ISAKMP_class): name = "IKE proposal" # ISAKMP_payload_type = 0 fields_desc = [ ByteEnumField("next_payload",None,ISAKMP_payload_type), ByteField("res",0), FieldLenField("length",None,"trans","H", adjust=lambda pkt,x:x+8), ByteField("proposal",1), ByteEnumField("proto",1,{1:"ISAKMP"}), FieldLenField("SPIsize",None,"SPI","B"), ByteField("trans_nb",None), StrLenField("SPI","",length_from=lambda x:x.SPIsize), PacketLenField("trans",conf.raw_layer(),ISAKMP_payload_Transform,length_from=lambda x:x.length-8), ] class ISAKMP_payload(ISAKMP_class): name = "ISAKMP payload" fields_desc = [ ByteEnumField("next_payload",None,ISAKMP_payload_type), ByteField("res",0), FieldLenField("length",None,"load","H", adjust=lambda pkt,x:x+4), StrLenField("load","",length_from=lambda x:x.length-4), ] class ISAKMP_payload_VendorID(ISAKMP_class): name = "ISAKMP Vendor ID" overload_fields = { ISAKMP: { "next_payload":13 }} fields_desc = [ ByteEnumField("next_payload",None,ISAKMP_payload_type), ByteField("res",0), FieldLenField("length",None,"vendorID","H", adjust=lambda pkt,x:x+4), StrLenField("vendorID","",length_from=lambda x:x.length-4), ] class ISAKMP_payload_SA(ISAKMP_class): name = "ISAKMP SA" overload_fields = { ISAKMP: { "next_payload":1 }} fields_desc = [ ByteEnumField("next_payload",None,ISAKMP_payload_type), ByteField("res",0), FieldLenField("length",None,"prop","H", adjust=lambda pkt,x:x+12), IntEnumField("DOI",1,{1:"IPSEC"}), IntEnumField("situation",1,{1:"identity"}), PacketLenField("prop",conf.raw_layer(),ISAKMP_payload_Proposal,length_from=lambda x:x.length-12), ] class ISAKMP_payload_Nonce(ISAKMP_class): name = "ISAKMP Nonce" overload_fields = { ISAKMP: { "next_payload":10 }} fields_desc = [ ByteEnumField("next_payload",None,ISAKMP_payload_type), ByteField("res",0), FieldLenField("length",None,"load","H", adjust=lambda pkt,x:x+4), StrLenField("load","",length_from=lambda x:x.length-4), ] class ISAKMP_payload_KE(ISAKMP_class): name = "ISAKMP Key Exchange" overload_fields = { ISAKMP: { "next_payload":4 }} fields_desc = [ ByteEnumField("next_payload",None,ISAKMP_payload_type), ByteField("res",0), FieldLenField("length",None,"load","H", adjust=lambda pkt,x:x+4), StrLenField("load","",length_from=lambda x:x.length-4), ] class ISAKMP_payload_ID(ISAKMP_class): name = "ISAKMP Identification" overload_fields = { ISAKMP: { "next_payload":5 }} fields_desc = [ ByteEnumField("next_payload",None,ISAKMP_payload_type), ByteField("res",0), FieldLenField("length",None,"load","H",adjust=lambda pkt,x:x+8), ByteEnumField("IDtype",1,{1:"IPv4_addr", 11:"Key"}), ByteEnumField("ProtoID",0,{0:"Unused"}), ShortEnumField("Port",0,{0:"Unused"}), # IPField("IdentData","127.0.0.1"), StrLenField("load","",length_from=lambda x:x.length-8), ] class ISAKMP_payload_Hash(ISAKMP_class): name = "ISAKMP Hash" overload_fields = { ISAKMP: { "next_payload":8 }} fields_desc = [ ByteEnumField("next_payload",None,ISAKMP_payload_type), ByteField("res",0), FieldLenField("length",None,"load","H",adjust=lambda pkt,x:x+4), StrLenField("load","",length_from=lambda x:x.length-4), ] ISAKMP_payload_type_overload = {} for i in range(len(ISAKMP_payload_type)): name = "ISAKMP_payload_%s" % ISAKMP_payload_type[i] if name in globals(): ISAKMP_payload_type_overload[globals()[name]] = {"next_payload":i} del(i) del(name) ISAKMP_class.overload_fields = ISAKMP_payload_type_overload.copy() bind_layers( UDP, ISAKMP, dport=500, sport=500) def ikescan(ip): return sr(IP(dst=ip)/UDP()/ISAKMP(init_cookie=RandString(8), exch_type=2)/ISAKMP_payload_SA(prop=ISAKMP_payload_Proposal()))