Package killerbee :: Module scapy_extensions
[hide private]
[frames] | no frames]

Source Code for Module killerbee.scapy_extensions

  1  DEFAULT_KB_CHANNEL = 11 
  2  DEFAULT_KB_DEVICE = None 
  3   
  4  from scapy.config import conf 
  5  setattr(conf, 'killerbee_channel', DEFAULT_KB_CHANNEL) 
  6  setattr(conf, 'killerbee_device', DEFAULT_KB_DEVICE) 
  7  setattr(conf, 'killerbee_nkey', None) 
  8  from scapy.base_classes import SetGen 
  9  from scapy.packet import Gen,Raw 
 10   
 11  import os 
 12  import time 
 13  from kbutils import randmac 
 14  import logging 
 15  log_killerbee = logging.getLogger('scapy.killerbee') 
16 17 -def __kb_send(kb, x, channel = None, inter = 0, loop = 0, count = None, verbose = None, realtime = None, *args, **kargs):
18 if type(x) is str: 19 x = Raw(load=x) 20 if not isinstance(x, Gen): 21 x = SetGen(x) 22 if verbose is None: 23 verbose = conf.verb 24 25 n = 0 26 if count is not None: 27 loop = -count 28 elif not loop: 29 loop=-1 30 dt0 = None 31 try: 32 while loop: 33 for p in x: 34 if realtime: 35 ct = time.time() 36 if dt0: 37 st = dt0+p.time-ct 38 if st > 0: 39 time.sleep(st) 40 else: 41 dt0 = ct-p.time 42 kb.inject(p.do_build()[:-2], channel = None, count = 1, delay = 0) # [:-2] because the firmware adds the FCS 43 n += 1 44 if verbose: 45 os.write(1,".") 46 time.sleep(inter) 47 if loop < 0: 48 loop += 1 49 except KeyboardInterrupt: 50 pass 51 return n
52
53 -def __kb_recv(kb, count = 0, store = 1, prn = None, lfilter = None, stop_filter = None, verbose = None, timeout = None):
54 kb.sniffer_on() 55 if timeout is not None: 56 stoptime = time.time()+timeout 57 if verbose is None: 58 verbose = conf.verb 59 60 lst = [] 61 packetcount = 0 62 remain = None 63 while 1: 64 try: 65 if timeout is not None: 66 remain = stoptime-time.time() 67 if remain <= 0: 68 break 69 70 packet = kb.pnext() # int(remain * 1000) to convert to seconds 71 if packet == None: continue 72 if verbose > 1: 73 os.write(1, "*") 74 packet = Dot15d4(packet[0]) 75 if lfilter and not lfilter(packet): 76 continue 77 packetcount += 1 78 if store: 79 lst.append(packet) 80 if prn: 81 r = prn(packet) 82 if r is not None: 83 print r 84 if stop_filter and stop_filter(packet): 85 break 86 if count > 0 and packetcount >= count: 87 break 88 except KeyboardInterrupt: 89 break 90 91 kb.sniffer_off() 92 kb.close() 93 return lst
94
95 @conf.commands.register 96 -def kbdev():
97 """List KillerBee recognized devices""" 98 show_dev()
99
100 @conf.commands.register 101 -def kbsendp(pkt, channel = None, inter = 0, loop = 0, iface = None, verbose = None, realtime=None):
102 """ 103 Send a packet with KillerBee 104 @param channel: 802.15.4 channel to transmit/receive on 105 @param inter: time to wait between tranmissions 106 @param loop: number of times to process the packet list 107 @param iface: KillerBee interface to use, or KillerBee() class instance 108 @param verbose: set verbosity level 109 @param realtime: use packet's timestamp, bending time with realtime value 110 """ 111 if channel == None: 112 channel = conf.killerbee_channel 113 if not isinstance(iface, KillerBee): 114 if iface is not None: 115 kb = KillerBee(device = iface) 116 else: 117 kb = KillerBee(device = conf.killerbee_device) 118 kb.set_channel(channel) 119 else: 120 kb = iface 121 122 pkts_out = __kb_send(kb, pkt, inter = inter, loop = loop, count = None, verbose = verbose, realtime = realtime) 123 print "\nSent %i packets." % pkts_out
124
125 @conf.commands.register 126 -def kbsrp(pkt, channel = None, inter = 0, count = 0, iface = None, store = 1, prn = None, lfilter = None, timeout = None, verbose = None, realtime = None):
127 """ 128 Send and receive packets with KillerBee 129 @param channel: 802.15.4 channel to transmit/receive on 130 @param inter: time to wait between tranmissions 131 @param count: number of packets to capture. 0 means infinity 132 @param iface: KillerBee interface to use, or KillerBee() class instance 133 @param store: wether to store sniffed packets or discard them 134 @param prn: function to apply to each packet. If something is returned, 135 it is displayed. Ex: 136 ex: prn = lambda x: x.summary() 137 @param lfilter: python function applied to each packet to determine 138 if further action may be done 139 ex: lfilter = lambda x: x.haslayer(Padding) 140 @param timeout: stop sniffing after a given time (default: None) 141 @param verbose: set verbosity level 142 @param realtime: use packet's timestamp, bending time with realtime value 143 """ 144 if verbose is None: 145 verbose = conf.verb 146 if channel == None: 147 channel = conf.killerbee_channel 148 if not isinstance(iface, KillerBee): 149 if iface is not None: 150 kb = KillerBee(device = iface) 151 else: 152 kb = KillerBee(device = conf.killerbee_device) 153 kb.set_channel(channel) 154 else: 155 kb = iface 156 157 pkts_out = __kb_send(kb, pkt, inter = inter, loop = 0, count = None, verbose = verbose, realtime = realtime) 158 if verbose: 159 print "\nSent %i packets." % pkts_out 160 161 pkts_in = __kb_recv(kb, count = count, store = store, prn = prn, lfilter = lfilter, verbose = verbose, timeout = timeout) 162 if verbose: 163 print "\nReceived %i packets." % len(pkts_in) 164 return plist.PacketList(pkts_in, 'Results')
165
166 @conf.commands.register 167 -def kbsrp1(pkt, channel = None, inter = 0, iface = None, store = 1, prn = None, lfilter = None, timeout = None, verbose = None, realtime = None):
168 """Send and receive packets with KillerBee and return only the first answer""" 169 return kbsrp(pkt, channel = channel, inter = inter, count = 1, iface = iface, store = store, prn = prn, lfilter = lfilter, timeout = timeout, verbose = verbose, realtime = realtime)
170
171 @conf.commands.register 172 -def kbsniff(channel = None, count = 0, iface = None, store = 1, prn = None, lfilter = None, stop_filter = None, verbose = None, timeout = None):
173 """ 174 Sniff packets with KillerBee. 175 @param channel: 802.15.4 channel to transmit/receive on 176 @param count: number of packets to capture. 0 means infinity 177 @param iface: KillerBee interface to use, or KillerBee() class instance 178 @param store: wether to store sniffed packets or discard them 179 @param prn: function to apply to each packet. If something is returned, 180 it is displayed. Ex: 181 ex: prn = lambda x: x.summary() 182 @param lfilter: python function applied to each packet to determine 183 if further action may be done 184 ex: lfilter = lambda x: x.haslayer(Padding) 185 @param timeout: stop sniffing after a given time (default: None) 186 """ 187 if channel == None: 188 channel = conf.killerbee_channel 189 if not isinstance(iface, KillerBee): 190 if iface is not None: 191 kb = KillerBee(device = iface) 192 else: 193 kb = KillerBee(device = conf.killerbee_device) 194 kb.set_channel(channel) 195 else: 196 kb = iface 197 return plist.PacketList(__kb_recv(kb, count = count, store = store, prn = prn, lfilter = lfilter, stop_filter = stop_filter, verbose = verbose, timeout = timeout), 'Sniffed')
198
199 @conf.commands.register 200 -def kbrdpcap(filename, count = -1, skip = 0, nofcs=False):
201 """ 202 Read a pcap file with the KillerBee library. 203 Wraps the PcapReader to return scapy packet object from pcap files. 204 This uses the killerbee internal methods instead of the scapy native methods. 205 This is not necessarily better, and suggestions are welcome. 206 Specify nofcs parameter as True if for some reason the packets in the PCAP 207 don't have FCS (checksums) at the end. 208 @return: Scapy packetlist of Dot15d4 packets parsed from the given PCAP file. 209 """ 210 cap = PcapReader(filename) 211 lst = [] 212 packetcount = 0 213 if count > 0: 214 count += skip 215 216 while 1: 217 packet = cap.pnext() 218 packetcount += 1 219 if packet[1] == None: 220 break 221 if skip > 0 and packetcount <= skip: 222 continue 223 if nofcs: packet = Dot15d4(packet[1]) 224 else: packet = Dot15d4FCS(packet[1]) 225 lst.append(packet) 226 if count > 0 and packetcount >= count: 227 break 228 return plist.PacketList(lst, os.path.basename(filename))
229
230 @conf.commands.register 231 -def kbwrpcap(save_file, pkts):
232 """ 233 Write a pcap using the KillerBee library. 234 """ 235 pd = PcapDumper(DLT_IEEE802_15_4, save_file, ppi=False) 236 for packet in pkts: 237 pd.pcap_dump(str(packet)) 238 pd.close()
239
240 @conf.commands.register 241 -def kbrddain(filename, count = -1, skip = 0):
242 """ 243 Read a dain tree file with the KillerBee library 244 Wraps the DainTreeReader to return scapy packet object from daintree files. 245 """ 246 cap = DainTreeReader(filename) 247 lst = [] 248 packetcount = 0 249 250 while 1: 251 packet = cap.pnext() 252 packetcount += 1 253 if packet[1] == None: 254 break 255 if skip > 0 and packetcount <= skip: 256 continue 257 packet = Dot15d4(packet[1]) 258 lst.append(packet) 259 if count > 0 and packetcount >= count: 260 break 261 return plist.PacketList(lst, os.path.basename(filename))
262
263 @conf.commands.register 264 -def kbwrdain(save_file, pkts):
265 """ 266 Write a daintree file using the KillerBee library. 267 """ 268 dt = DainTreeDumper(save_file) 269 for packet in pkts: 270 dt.pwrite(str(packet)) 271 dt.close()
272
273 @conf.commands.register 274 -def kbkeysearch(packet, searchdata, ispath = True, skipfcs = True, raw = False):
275 """ 276 Search a binary file for the encryption key to an encrypted packet. 277 """ 278 if 'fcf_security' in packet.fields and packet.fcf_security == 0: 279 raise Exception('Packet Not Encrypted (fcf_security Not Set)') 280 if ispath: 281 searchdata = open(searchdata, 'r').read() 282 packet = packet.do_build() 283 if skipfcs: 284 packet = packet[:-2] 285 offset = 0 286 keybytes = [] 287 d = Dot154PacketParser() 288 searchdatalen = len(searchdata) 289 while (offset < (searchdatalen - 16)): 290 if d.decrypt(packet, searchdata[offset:offset+16]) != '': 291 if raw: 292 return ''.join(searchdata[offset + i] for i in xrange(0, 16)) 293 else: 294 return ':'.join("%02x" % ord(searchdata[offset + i]) for i in xrange(0, 16)) 295 else: 296 offset+=1 297 return None
298
299 @conf.commands.register 300 -def kbgetnetworkkey(pkts):
301 """ 302 Search packets for a plaintext key exchange returns the first one found. 303 """ 304 if not isinstance(pkts, Gen): 305 pkts = SetGen(pkts) 306 for packet in pkts: 307 packet = str(packet) 308 zmac = Dot154PacketParser() 309 znwk = ZigBeeNWKPacketParser() 310 zaps = ZigBeeAPSPacketParser() 311 try: 312 # Process MAC layer details 313 zmacpayload = zmac.pktchop(packet)[-1] 314 if zmacpayload == None: 315 continue 316 317 # Process NWK layer details 318 znwkpayload = znwk.pktchop(zmacpayload)[-1] 319 if znwkpayload == None: 320 continue 321 322 # Process the APS layer details 323 zapschop = zaps.pktchop(znwkpayload) 324 if zapschop == None: 325 continue 326 327 # See if this is an APS Command frame 328 apsfc = ord(zapschop[0]) 329 if (apsfc & ZBEE_APS_FCF_FRAME_TYPE) != ZBEE_APS_FCF_CMD: 330 continue 331 332 # Delivery mode is Normal Delivery (0) 333 apsdeliverymode = (apsfc & ZBEE_APS_FCF_DELIVERY_MODE) >> 2 334 if apsdeliverymode != 0: 335 continue 336 337 # Ensure Security is Disabled 338 if (apsfc & ZBEE_APS_FCF_SECURITY) == 1: 339 continue 340 341 zapspayload = zapschop[-1] 342 343 # Check payload length, must be at least 35 bytes 344 # APS cmd | key type | key | sequence number | dest addr | src addr 345 if len(zapspayload) < 35: 346 continue 347 348 # Check for APS command identifier Transport Key (0x05) 349 if ord(zapspayload[0]) != 5: 350 continue 351 352 # Transport Key Frame, get the key type. Network Key is 0x01, no 353 # other keys should be sent in plaintext 354 if ord(zapspayload[1]) != 1: 355 continue 356 357 # Reverse these fields 358 networkkey = zapspayload[2:18][::-1] 359 destaddr = zapspayload[19:27][::-1] 360 srcaddr = zapspayload[27:35][::-1] 361 362 key_bytes = [] 363 dst_mac_bytes = [] 364 src_mac_bytes = [] 365 key = {} 366 key['key'] = ':'.join("%02x" % ord(networkkey[x]) for x in xrange(16)) 367 key['dst'] = ':'.join("%02x" % ord(destaddr[x]) for x in xrange(8)) 368 key['src'] = ':'.join("%02x" % ord(srcaddr[x]) for x in xrange(8)) 369 return key 370 except: 371 continue 372 return { }
373
374 @conf.commands.register 375 -def kbtshark(store = 0, *args,**kargs):
376 """Sniff packets using KillerBee and print them calling pkt.show()""" 377 return kbsniff(prn=lambda x: x.display(), store = store, *args, **kargs)
378
379 @conf.commands.register 380 -def kbrandmac(length = 8):
381 """Returns a random MAC address using a list valid OUI's from ZigBee device manufacturers.""" 382 return randmac(length)
383
384 @conf.commands.register 385 -def kbdecrypt(pkt, key = None, verbose = None):
386 """Decrypt Zigbee frames using AES CCM* with 32-bit MIC""" 387 if verbose is None: 388 verbose = conf.verb 389 if key == None: 390 if conf.killerbee_nkey == None: 391 log_killerbee.error("Cannot find decryption key. (Set conf.killerbee_nkey)") 392 return None 393 key = conf.killerbee_nkey 394 if len(key) != 16: 395 log_killerbee.error("Invalid decryption key, must be a 16 byte string.") 396 return None 397 elif not pkt.haslayer(ZigbeeSecurityHeader) or not pkt.haslayer(ZigbeeNWK): 398 log_killerbee.error("Cannot decrypt frame without a ZigbeeSecurityHeader.") 399 return None 400 try: 401 import zigbee_crypt 402 except ImportError: 403 log_killerbee.error("Could not import zigbee_crypt extension, cryptographic functionality is not available.") 404 return None 405 406 from struct import pack 407 f = pkt.getlayer(ZigbeeSecurityHeader).fields 408 409 #TODO fix mic! 410 mic = '\x00\x00\x00\x00' #pack(">I", f['mic']) 411 encrypted = f['data'] 412 413 nonce = "" # build the nonce 414 nonce += pack(">Q", f['source']) 415 nonce += pack(">I", f['fc']) 416 #fc = (f['reserved1'] << 6) | (f['extended_nonce'] << 5) | (f['key_type'] << 3) | f['reserved2'] 417 fc = (f['reserved1'] << 6) | (f['extended_nonce'] << 5) | (f['key_type'] << 3) | f['nwk_seclevel'] 418 nonce += chr(fc | 0x05) 419 420 if verbose > 2: 421 print "Decrypt Details:" 422 print "\tKey: " + key.encode('hex') 423 print "\tNonce: " + nonce.encode('hex') 424 print "\tMic: " + mic.encode('hex') 425 print "\tEncrypted Data: " + encrypted.encode('hex') 426 427 crop_size = 4 + 2 + len(pkt.getlayer(ZigbeeSecurityHeader).fields['data']) # the size of all the zigbee crap, minus the length of the encrypted data, mic and FCS 428 429 # the Security Control Field flags have to be adjusted before this is calculated, so we store their original values so we can reset them later 430 reserved2 = pkt.getlayer(ZigbeeSecurityHeader).fields['nwk_seclevel'] 431 #reserved2 = pkt.getlayer(ZigbeeSecurityHeader).fields['reserved2'] 432 pkt.getlayer(ZigbeeSecurityHeader).fields['nwk_seclevel'] = (pkt.getlayer(ZigbeeSecurityHeader).fields['nwk_seclevel'] | 0x05) 433 #pkt.getlayer(ZigbeeSecurityHeader).fields['reserved2'] = (pkt.getlayer(ZigbeeSecurityHeader).fields['reserved2'] | 0x05) 434 zigbeeData = pkt.getlayer(ZigbeeNWK).do_build() 435 zigbeeData = zigbeeData[:-crop_size] 436 pkt.getlayer(ZigbeeSecurityHeader).fields['nwk_seclevel'] = reserved2 437 #pkt.getlayer(ZigbeeSecurityHeader).fields['reserved2'] = reserved2 438 439 (payload, micCheck) = zigbee_crypt.decrypt_ccm(key, nonce, mic, encrypted, zigbeeData) 440 441 if verbose > 2: 442 print "\tDecrypted Data: " + payload.encode('hex') 443 444 frametype = pkt.getlayer(ZigbeeNWK).fields['frametype'] 445 if frametype == 0: 446 payload = ZigbeeAppDataPayload(payload) 447 elif frametype == 1: 448 payload = ZigbeeNWKCommandPayload(payload) 449 else: 450 payload = Raw(payload) 451 if micCheck == 1: 452 return (payload, True) 453 else: 454 return (payload, False)
455
456 @conf.commands.register 457 -def kbencrypt(pkt, data, key = None, verbose = None):
458 """Encrypt Zigbee frames using AES CCM* with 32-bit MIC""" 459 if verbose is None: 460 verbose = conf.verb 461 if key == None: 462 if conf.killerbee_nkey == None: 463 log_killerbee.error("Cannot find decryption key. (Set conf.killerbee_nkey)") 464 return None 465 key = conf.killerbee_nkey 466 if len(key) != 16: 467 log_killerbee.error("Invalid encryption key, must be a 16 byte string.") 468 return None 469 elif not pkt.haslayer(ZigbeeSecurityHeader) or not pkt.haslayer(ZigbeeNWK): 470 log_killerbee.error("Cannot encrypt frame without a ZigbeeSecurityHeader.") 471 return None 472 try: 473 import zigbee_crypt 474 except ImportError: 475 log_killerbee.error("Could not import zigbee_crypt extension, cryptographic functionality is not available.") 476 return None 477 478 from struct import unpack 479 f = pkt.getlayer(ZigbeeSecurityHeader).fields 480 481 f['data'] = '' # explicitly clear it out, this should go without say 482 483 if isinstance(data, Packet): 484 decrypted = data.do_build() 485 else: 486 decrypted = data 487 488 nonce = "" # build the nonce 489 nonce += pack(">Q", f['source']) 490 nonce += pack(">I", f['fc']) 491 fc = (f['reserved1'] << 6) | (f['extended_nonce'] << 5) | (f['key_type'] << 3) | f['reserved2'] 492 nonce += chr(fc | 0x05) 493 494 if verbose > 2: 495 print "Encrypt Details:" 496 print "\tKey: " + key.encode('hex') 497 print "\tNonce: " + nonce.encode('hex') 498 print "\tDecrypted Data: " + decrypted.encode('hex') 499 500 crop_size = 4 + 2 # the size of all the zigbee crap, minus the length of the mic and FCS 501 502 # the Security Control Field flags have to be adjusted before this is calculated, so we store their original values so we can reset them later 503 reserved2 = pkt.getlayer(ZigbeeSecurityHeader).fields['reserved2'] 504 pkt.getlayer(ZigbeeSecurityHeader).fields['reserved2'] = (pkt.getlayer(ZigbeeSecurityHeader).fields['reserved2'] | 0x05) 505 zigbeeData = pkt.getlayer(ZigbeeNWK).do_build() 506 zigbeeData = zigbeeData[:-crop_size] 507 pkt.getlayer(ZigbeeSecurityHeader).fields['reserved2'] = reserved2 508 509 (payload, mic) = zigbee_crypt.encrypt_ccm(key, nonce, 4, decrypted, zigbeeData) 510 511 if verbose > 2: 512 print "\tEncrypted Data: " + payload.encode('hex') 513 print "\tMic: " + mic.encode('hex') 514 515 # Set pkt's values to reflect the encrypted ones to it's ready to be sent 516 f['data'] = payload 517 f['mic'] = unpack(">I", mic)[0] 518 return pkt
519