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)
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()
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):
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
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
313 zmacpayload = zmac.pktchop(packet)[-1]
314 if zmacpayload == None:
315 continue
316
317
318 znwkpayload = znwk.pktchop(zmacpayload)[-1]
319 if znwkpayload == None:
320 continue
321
322
323 zapschop = zaps.pktchop(znwkpayload)
324 if zapschop == None:
325 continue
326
327
328 apsfc = ord(zapschop[0])
329 if (apsfc & ZBEE_APS_FCF_FRAME_TYPE) != ZBEE_APS_FCF_CMD:
330 continue
331
332
333 apsdeliverymode = (apsfc & ZBEE_APS_FCF_DELIVERY_MODE) >> 2
334 if apsdeliverymode != 0:
335 continue
336
337
338 if (apsfc & ZBEE_APS_FCF_SECURITY) == 1:
339 continue
340
341 zapspayload = zapschop[-1]
342
343
344
345 if len(zapspayload) < 35:
346 continue
347
348
349 if ord(zapspayload[0]) != 5:
350 continue
351
352
353
354 if ord(zapspayload[1]) != 1:
355 continue
356
357
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
410 mic = '\x00\x00\x00\x00'
411 encrypted = f['data']
412
413 nonce = ""
414 nonce += pack(">Q", f['source'])
415 nonce += pack(">I", f['fc'])
416
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'])
428
429
430 reserved2 = pkt.getlayer(ZigbeeSecurityHeader).fields['nwk_seclevel']
431
432 pkt.getlayer(ZigbeeSecurityHeader).fields['nwk_seclevel'] = (pkt.getlayer(ZigbeeSecurityHeader).fields['nwk_seclevel'] | 0x05)
433
434 zigbeeData = pkt.getlayer(ZigbeeNWK).do_build()
435 zigbeeData = zigbeeData[:-crop_size]
436 pkt.getlayer(ZigbeeSecurityHeader).fields['nwk_seclevel'] = reserved2
437
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'] = ''
482
483 if isinstance(data, Packet):
484 decrypted = data.do_build()
485 else:
486 decrypted = data
487
488 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
501
502
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
516 f['data'] = payload
517 f['mic'] = unpack(">I", mic)[0]
518 return pkt
519