From dcb42a3e699b0bcdea1b4d6cfa053297f46dde95 Mon Sep 17 00:00:00 2001 From: Craig Smith Date: Mon, 27 Feb 2017 17:29:54 -0800 Subject: [PATCH 1/5] Initial zigbee support using killerbee. Core session setup portion --- lib/msf/base/sessions/hwbridge.rb | 10 + .../post/hwbridge/extensions/zigbee/zigbee.rb | 42 ++++ .../ui/console/command_dispatcher/zigbee.rb | 86 ++++++++ modules/auxiliary/client/hwbridge/connect.rb | 3 + tools/hardware/killerbee_msfrelay | 201 ++++++++++++++++++ 5 files changed, 342 insertions(+) create mode 100644 lib/rex/post/hwbridge/extensions/zigbee/zigbee.rb create mode 100644 lib/rex/post/hwbridge/ui/console/command_dispatcher/zigbee.rb create mode 100755 tools/hardware/killerbee_msfrelay diff --git a/lib/msf/base/sessions/hwbridge.rb b/lib/msf/base/sessions/hwbridge.rb index f95af2c253..11e1d4b677 100644 --- a/lib/msf/base/sessions/hwbridge.rb +++ b/lib/msf/base/sessions/hwbridge.rb @@ -157,6 +157,16 @@ class HWBridge < Rex::Post::HWBridge::Client console.disable_output = original end + # + # Loads the zigbee extension + # + def load_zigbee + original = console.disable_output + console.disable_output = true + console.run_single('load zigbee') + console.disable_output = original + end + # # Load custom methods provided by the hardware # diff --git a/lib/rex/post/hwbridge/extensions/zigbee/zigbee.rb b/lib/rex/post/hwbridge/extensions/zigbee/zigbee.rb new file mode 100644 index 0000000000..835f9ed9fa --- /dev/null +++ b/lib/rex/post/hwbridge/extensions/zigbee/zigbee.rb @@ -0,0 +1,42 @@ +# +# -*- coding: binary -*- +require 'rex/post/hwbridge/client' + +module Rex +module Post +module HWBridge +module Extensions +module Zigbee + +### +# Zigbee extension - set of commands to be executed on zigbee compatible hw bridges +### + +class Zigbee < Extension + + def initialize(client) + super(client, 'zigbee') + + # Alias the following things on the client object so that they + # can be directly referenced + client.register_extension_aliases( + [ + { + 'name' => 'zigbee', + 'ext' => self + } + ]) + end + + # Gets supported Zigbee Devices + # @return [Array] Devices + def supported_devices + client.send_request("/zigbee/supported_devices") + end +end + +end +end +end +end +end diff --git a/lib/rex/post/hwbridge/ui/console/command_dispatcher/zigbee.rb b/lib/rex/post/hwbridge/ui/console/command_dispatcher/zigbee.rb new file mode 100644 index 0000000000..d07e567a0e --- /dev/null +++ b/lib/rex/post/hwbridge/ui/console/command_dispatcher/zigbee.rb @@ -0,0 +1,86 @@ +# -*- coding: binary -*- +require 'rex/post/hwbridge' +require 'msf/core/auxiliary/report' + +module Rex +module Post +module HWBridge +module Ui +### +# Zigbee extension - set of commands to be executed on Zigbee compatible devices +### +class Console::CommandDispatcher::Zigbee + include Console::CommandDispatcher + include Msf::Auxiliary::Report + + # + # List of supported commands. + # + def commands + all = { + 'supported_devices' => 'Get supported zigbee devices' + } + + all + end + + # + # Lists all thesupported devices + # + def cmd_supported_devices + devices = client.zigbee.supported_devices + if not devices or not devices.has_key? "devices" + print_line("error retrieving list of devices") + return + end + devices = devices["devices"] + if not devices.size > 0 + print_line("none") + return + end + self.target_device = devices[0] if devices.size == 1 + str = "Supported Devices: " + str += devices.join(', ') + str += "\nUse device name to set your desired device, default is: #{self.target_device}" + print_line(str) + end + + # + # Sets the default target device + # + def cmd_target(*args) + self.target_device = "" + device_opts = Rex::Parser::Arguments.new( + '-h' => [ false, 'Help Banner' ], + '-d' => [ true, 'Device ID' ] + ) + device_opts.parse(args) do |opt, _idx, val| + case opt + when '-h' + print_line("Usage: target -d \n") + print_line(device_opts.usage) + return + when '-d' + self.target_device = val + end + end + print_line("set target device to #{self.target_device}") + end + + # + # Name for this dispatcher + # + def name + 'Zigbee' + end + +private + attr_accessor :target_device + +end + +end +end +end +end + diff --git a/modules/auxiliary/client/hwbridge/connect.rb b/modules/auxiliary/client/hwbridge/connect.rb index be2fe30ade..331af02385 100644 --- a/modules/auxiliary/client/hwbridge/connect.rb +++ b/modules/auxiliary/client/hwbridge/connect.rb @@ -98,6 +98,9 @@ class MetasploitModule < Msf::Auxiliary if self.hw_specialty.has_key? "automotive" sess.load_automotive if self.hw_specialty["automotive"] == true end + if self.hw_specialty.has_key? "zigbee" + sess.load_zigbee if self.hw_specialty["zigbee"] == true + end end # diff --git a/tools/hardware/killerbee_msfrelay b/tools/hardware/killerbee_msfrelay new file mode 100755 index 0000000000..c600232ddb --- /dev/null +++ b/tools/hardware/killerbee_msfrelay @@ -0,0 +1,201 @@ +#!/usr/bin/python +# KillerBee Metasploit relay server + +import re +import os +import sys +import cmd +import time +import json +import base64 +import socket +import threading +import pkg_resources # Used to get killerbee version + +from BaseHTTPServer import BaseHTTPRequestHandler,HTTPServer +from urlparse import parse_qs,urlparse +from killerbee import * + +last_errors = 0 +starttime = 0 +packets_sent = 0 +last_sent = 0 +username = None +password = None + +class MSFHandler(BaseHTTPRequestHandler): + def status(self): + status = {} + hw_versions = [] + fw_version = pkg_resources.get_distribution("killerbee").version + device_names = [] + for dev in kbutils.devlist(): + hw_versions.append(dev[2]) + device_names.append(dev[1]) + if len(hw_versions) > 0: + status["operational"] = 1 + else: + status["operational"] = 0 + status["hw_specialty"] = { "zigbee": True } + # TODO: We should check firmware before reporting transmit capabilities + status["hw_capabilities"] = { "transmit": True} + status["last_10_errors"] = last_errors + status["api_version"] = "0.0.3" + status["fw_version"] = fw_version + if len(hw_versions) == 1: + status["hw_version"] = hw_versions[0] + status["device_name"] = device_names[0] + elif len(hw_versions) > 1: + status["hw_version"] = ', '.join(hw_versions) + status["device_name"] = ', '.join(device_names) + else: + status["hw_version"] = "Not Supported" + return status + + def statistics(self): + stats = {} + stats["uptime"] = int(time.time()) - starttime + stats["packet_stats"] = packets_sent + stats["last_request"] = last_sent + stats["voltage"] = "0.0v" + return stats + + def datetime(self): + return { "sytem_datetime": int(time.time()) } + + def timezone(self): + return { "system_timezone": time.strftime("%Z") } + + def supported_devices(self): + devices = [] + for dev in kbutils.devlist(): + devices.append(dev[0]) + return { "devices": devices } + + def not_supported(self): + return { "status": "not supported" } + + def send(self, data, resp=200): + self.send_response(resp) + self.send_header('Content-type','application/json') + self.end_headers() + self.wfile.write(json.dumps(data)) + return + + def do_AUTHHEAD(self): + self.send_response(401) + self.send_header('WWW-Authenticate', 'Basic realm=\"Killerbee MSF Relay\"') + self.send_header('Content-type', 'text/html') + self.end_headers() + self.wfile.write("Please Authenticate") + + def do_GET(self): + if not password == None: + if self.headers.getheader('Authorization') == None: + print("Did not authenticate") + self.do_AUTHHEAD() + return + if not self.headers.getheader('Authorization') == 'Basic '+base64.b64encode(username + ":" + password): + print("Bad Authentication") + self.do_AUTHHEAD() + return + url = urlparse(self.path) + args = parse_qs(url.query) + if self.path=="/status": + self.send(self.status()) + elif self.path=="/statistics": + self.send(self.statistics()) + elif self.path=="/settings/datetime": + self.send(self.datetime()) + elif self.path=="/settings/timezone": + self.send(self.timezone()) + elif self.path=="/zigbee/supported_devices": + self.send(self.supported_devices()) + elif self.path.startswith("/zigbee/"): + re_idx = re.compile("/zigbee/(\w+)/") + m = re_idx.match(self.path) + if m: + idx = m.group(1) + if self.path.find("/set_freq?") > -1: + self.send(self.set_freq(args)) + else: + self.send(self.not_supported(), 404) + else: + self.send(self.not_supported(), 404) + else: + self.send(self.not_supported(), 404) + return + +class Killerbee_MSFRelay(cmd.Cmd): + intro = """ + Killerbee Metasploit Relay +""" + + def __init__(self, ip='0.0.0.0', port=8080): + cmd.Cmd.__init__(self) + + self._ip = ip + self._port = port + self._sock = None + self._pause = False + + self.start() + + def start(self): + self._go = True + while self._go: + # serve the NIC port + try: + self._sock = HTTPServer((self._ip, self._port), MSFHandler) + starttime = int(time.time()) + print("Killerbee MSFRelay running.") + self._sock.serve_forever() + except KeyboardInterrupt: + self._sock.socket.close() + self._go = False + except: + sys.excepthook(*sys.exc_info()) + +if __name__ == "__main__": + import argparse + + parser = argparse.ArgumentParser() + parser.add_argument('-u', '--user', default="msf_relay", help='HTTP Username', type=str) + parser.add_argument('-p', '--password', default="rfcat_relaypass", help='HTTP Password', type=str) + parser.add_argument('-P', '--Port', default=8080, type=int) + parser.add_argument('--noauth', default=False, action="store_true", help='Do not require authentication') + parser.add_argument('--localonly', default=False, action="store_true", help='Listen on localhost only') + + ifo = parser.parse_args() + + username = ifo.user + password = ifo.password + ip = "0.0.0.0" + port = ifo.Port + if ifo.noauth: + username = None + password = None + if ifo.localonly: + host = "127.0.0.1" + + wait_msg = False + dev_found = False + while not dev_found: + try: + devs = kbutils.devlist() + if len(devs) > 0: + dev_found = True + elif not wait_msg: + print("Insert Killerbee compatible Zigbee device. (You may need to add permissions)") + wait_msg = True + except KeyboardInterrupt: + sys.exit() + except: + if not wait_msg: + print("Insert Killerbee compatible Zigbee device. (You may need to add permissions)") + wait_msg = True + + beerelay = Killerbee_MSFRelay(ip, port) + +import atexit +atexit.register(cleanupInteractiveAtExit) From 60cd04bc7b7f140c4b861709b8d165099b9a0d72 Mon Sep 17 00:00:00 2001 From: Craig Smith Date: Mon, 6 Mar 2017 16:10:14 -0800 Subject: [PATCH 2/5] Added module for zstumbler --- lib/msf/core/post/hardware.rb | 1 + lib/msf/core/post/hardware/zigbee/utils.rb | 259 ++++++++++++++++++ .../post/hwbridge/extensions/zigbee/zigbee.rb | 43 +++ .../ui/console/command_dispatcher/zigbee.rb | 48 +++- modules/auxiliary/client/hwbridge/connect.rb | 2 + modules/post/hardware/zigbee/zstumbler.rb | 102 +++++++ tools/hardware/killerbee_msfrelay | 60 +++- 7 files changed, 505 insertions(+), 10 deletions(-) create mode 100644 lib/msf/core/post/hardware/zigbee/utils.rb create mode 100644 modules/post/hardware/zigbee/zstumbler.rb diff --git a/lib/msf/core/post/hardware.rb b/lib/msf/core/post/hardware.rb index 9f6772ad85..c25549dcb4 100644 --- a/lib/msf/core/post/hardware.rb +++ b/lib/msf/core/post/hardware.rb @@ -1,4 +1,5 @@ # -*- coding: binary -*- module Msf::Post::Hardware require 'msf/core/post/hardware/automotive/uds' + require 'msf/core/post/hardware/zigbee/utils' end diff --git a/lib/msf/core/post/hardware/zigbee/utils.rb b/lib/msf/core/post/hardware/zigbee/utils.rb new file mode 100644 index 0000000000..149c992bbc --- /dev/null +++ b/lib/msf/core/post/hardware/zigbee/utils.rb @@ -0,0 +1,259 @@ +# -*- coding: binary -*- +module Msf +class Post +module Hardware +module Zigbee + +module Utils + + ## Constants for packet decoding fields + # Frame Control Field + DOT154_FCF_TYPE_MASK = 0x0007 #: Frame type mask + DOT154_FCF_SEC_EN = 0x0008 #: Set for encrypted payload + DOT154_FCF_FRAME_PND = 0x0010 #: Frame pending + DOT154_FCF_ACK_REQ = 0x0020 #: ACK request + DOT154_FCF_INTRA_PAN = 0x0040 #: Intra-PAN activity + DOT154_FCF_DADDR_MASK = 0x0C00 #: Destination addressing mode mask + DOT154_FCF_VERSION_MASK = 0x3000 #: Frame version + DOT154_FCF_SADDR_MASK = 0xC000 #: Source addressing mask mode + + # Frame Control Field Bit Shifts + DOT154_FCF_TYPE_MASK_SHIFT = 0 #: Frame type mask mode shift + DOT154_FCF_DADDR_MASK_SHIFT = 10 #: Destination addressing mode mask + DOT154_FCF_VERSION_MASK_SHIFT = 12 #: Frame versions mask mode shift + DOT154_FCF_SADDR_MASK_SHIFT = 14 #: Source addressing mask mode shift + + # Address Mode Definitions + DOT154_FCF_ADDR_NONE = 0x0000 #: Not sure when this is used + DOT154_FCF_ADDR_SHORT = 0x0002 #: 4-byte addressing + DOT154_FCF_ADDR_EXT = 0x0003 #: 8-byte addressing + + DOT154_FCF_TYPE_BEACON = 0 #: Beacon frame + DOT154_FCF_TYPE_DATA = 1 #: Data frame + DOT154_FCF_TYPE_ACK = 2 #: Acknowledgement frame + DOT154_FCF_TYPE_MACCMD = 3 #: MAC Command frame + + DOT154_CRYPT_NONE = 0x00 #: No encryption, no MIC + DOT154_CRYPT_MIC32 = 0x01 #: No encryption, 32-bit MIC + DOT154_CRYPT_MIC64 = 0x02 #: No encryption, 64-bit MIC + DOT154_CRYPT_MIC128 = 0x03 #: No encryption, 128-bit MIC + DOT154_CRYPT_ENC = 0x04 #: Encryption, no MIC + DOT154_CRYPT_ENC_MIC32 = 0x05 #: Encryption, 32-bit MIC + DOT154_CRYPT_ENC_MIC64 = 0x06 #: Encryption, 64-bit MIC + DOT154_CRYPT_ENC_MIC128 = 0x07 #: Encryption, 128-bit MIC + + # Retrieves the target Zigbee device. This is typically set by the user via the + # interactive HWBridge command line + # @return [String] Zigbee device ID + def get_target_device + if not client.zigbee + print_error("Not a Zigbee hwbridge session") + return + end + return client.zigbee.get_target_device + end + + # Sets the target default Zigbee Device. This command typically isn't called via a script + # Instead the user is expected to set this via the interactive HWBridge commandline + # @param device [String] Zigbee device ID + def set_target_device(device) + if not client.zigbee + print_error("Not a Zigbee hwbridge session") + return + end + client.zigbee.set_target_device device + end + + # Sets the Zigbee Channel + # @param device [String] Zigbee device ID + # @param channel [Integer] Channel number, typically 11-25 + def set_channel(device, channel) + if not client.zigbee + print_error("Not a Zigbee hwbridge session") + return {} + end + device = client.zigbee.target_device if not device + if not device + print_line("No target device set, use 'target' or specify bus via the options") + return {} + end + client.zigbee.set_channel(device, channel) + end + + # Inject raw packets. Need firmware on the zigbee device that supports transmission. + # @param device [String] Zigbee device ID + # @param data [String] Raw binary data sent as a string + def inject(device, data) + if not client.zigbee + print_error("Not a Zigbee hwbridge session") + return {} + end + device = client.zigbee.target_device if not device + if not device + print_line("No target device set, use 'target' or specify bus via the options") + return {} + end + client.zigbee.inject(device, data) + end + + # Recieves data from the Zigbee device + # @param device [String] Zigbee device ID + # @return [String] Binary blob of returned data + def recv(device) + if not client.zigbee + print_error("Not a Zigbee hwbridge session") + return {} + end + device = client.zigbee.target_device if not device + if not device + print_line("No target device set, use 'target' or specify bus via the options") + return {} + end + client.zigbee.recv(device) + end + + # Turn off Zigbee receiving + # @param device [String] Zigbee device ID + def sniffer_off(device) + if not client.zigbee + print_error("Not a Zigbee hwbridge session") + return {} + end + device = client.zigbee.target_device if not device + if not device + print_line("No target device set, use 'target' or specify bus via the options") + return {} + end + client.zigbee.sniffer_off(device) + end + + # Turn on Zigbee receiving + # @param device [String] Zigbee device ID + def sniffer_on(device) + if not client.zigbee + print_error("Not a Zigbee hwbridge session") + return {} + end + device = client.zigbee.target_device if not device + if not device + print_line("No target device set, use 'target' or specify bus via the options") + return {} + end + client.zigbee.sniffer_on(device) + end + + # Breaks up the packet into different sections. Also provides + # Some decoding information + # @param packet [String] Raw data from recv + # @return [Hash] { PktChop => [Array of data], .. + def dot154_packet_decode(packet) + result = {} + offset = 0 + pktchop = ['', '', '', '', '', '', [], ''] + pktchop[0] = packet[0,2] + # Sequence number + pktchop[1] = packet[2] + # Byte swap + fcf = pktchop[0].reverse.unpack("H*")[0].hex + result["FSF"] = fcf + result["SEQ"] = pktchop[1] + # Check if we are dealing with a beacon frame + if (fcf & DOT154_FCF_TYPE_MASK) == DOT154_FCF_TYPE_BEACON + beacondata = ["", "", "", "", "", "", "", "", "", ""] + # 802.15.4 fields, SPAN and SA + pktchop[4] = packet[3,2] + pktchop[5] = packet[5,2] + result["SPAN_ID"] = pktchop[4].reverse.unpack("H*")[0] + result["SOURCE"] = pktchop[5].reverse.unpack("H*")[0] + offset = 7 + + # Superframe specification + beacondata[0] = packet[offset,2] + result["SUPERFRAME"] = beacondata[0] + offset+=2 + + # GTS data + beacondata[1] = packet[offset] + result["GTS"] = beacondata[1] + offset+=1 + + # Pending address count + beacondata[2] = packet[offset] + result["PENDING_ADDRESS_COUNT"] = beacondata[2] + offset+=1 + + # Protocol ID + beacondata[3] = packet[offset] + result["PROTOCOL_ID"] = beacondata[3] + offset+=1 + + # Stack Profile version + beacondata[4] = packet[offset] + result["STACK_PROFILE"] = beacondata[4] + offset+=1 + + # Capability information + beacondata[5] = packet[offset] + result["CAPABILITY"] = beacondata[5] + offset+=1 + + # Extended PAN ID + beacondata[6] = packet[offset,8] + result["EXT_PAN_ID"] = beacondata[6].reverse.unpack("H*")[0] + offset+=8 + + # TX Offset + beacondata[7] = packet[offset,3] + result["TX_OFFSET"] = beacondata[7] + offset+=3 + + # Update ID + beacondata[8] = packet[offset] + result["UPDATE_ID"] = beacondata[8] + offset+=1 + pktchop[6] = beacondata + result["BEACONDATA"] = beacondata + else + # Not a beacon frame + + # DPAN + pktchop[2] = packet[3,2] + offset = 5 + + # Examine the destination addressing mode + daddr_mask = (fcf & DOT154_FCF_DADDR_MASK) >> 10 + if daddr_mask == DOT154_FCF_ADDR_EXT + pktchop[3] = packet[offset,8] + offset+=8 + elsif daddr_mask == DOT154_FCF_ADDR_SHORT + pktchop[3] = packet[offset,2] + offset+=2 + end + + # Examine the Intra-PAN flag + if (fcf & DOT154_FCF_INTRA_PAN) == 0 + pktchop[4] = packet[offset,2] + offset+=2 + end + + # Examine the source addressing mode + saddr_mask = (fcf & DOT154_FCF_SADDR_MASK) >> 14 + if daddr_mask == DOT154_FCF_ADDR_EXT + pktchop[5] = packet[offset,8] + offset+=8 + elsif daddr_mask == DOT154_FCF_ADDR_SHORT + pktchop[5] = packet[offset,2] + offset+=2 + end + end + # Append remaining payload + pktchop[7] = packet[offset,packet.size] if offset < packet.size + result["PktChop"] = pktchop + return result + end +end + +end +end +end +end diff --git a/lib/rex/post/hwbridge/extensions/zigbee/zigbee.rb b/lib/rex/post/hwbridge/extensions/zigbee/zigbee.rb index 835f9ed9fa..b456b33e86 100644 --- a/lib/rex/post/hwbridge/extensions/zigbee/zigbee.rb +++ b/lib/rex/post/hwbridge/extensions/zigbee/zigbee.rb @@ -28,11 +28,54 @@ class Zigbee < Extension ]) end + # Sets the default target device + # @param device [String] Target Zigbee device ID + def set_target_device(device) + self.target_device = device + end + + # Retrieves the default zigbee device ID + # @return [String] Zigbee device ID + def get_target_device + self.target_device + end + # Gets supported Zigbee Devices # @return [Array] Devices def supported_devices client.send_request("/zigbee/supported_devices") end + + # Sets the channel + # @param dev [String] Device to affect + # @param channel [Integer] Channel number + def set_channel(dev, channel) + client.send_request("/zigbee/#{dev}/set_channel?chan=#{channel}") + end + + def inject(dev, data) + data = Base64.urlsafe_encode64(data) + client.send_request("/zigbee/#{dev}/inject?data=#{data}") + end + + def recv(dev) + data = client.send_request("/zigbee/#{dev}/recv") + if data.size > 0 + data["data"] = Base64.urlsafe_decode64(data["data"]) if data.has_key? "data" + end + data + end + + def sniffer_off(dev) + client.send_request("/zigbee/#{dev}/sniffer_off") + end + + def sniffer_on(dev) + client.send_request("/zigbee/#{dev}/sniffer_on") + end + + attr_accessor :target_device + end end diff --git a/lib/rex/post/hwbridge/ui/console/command_dispatcher/zigbee.rb b/lib/rex/post/hwbridge/ui/console/command_dispatcher/zigbee.rb index d07e567a0e..c0ce03dd72 100644 --- a/lib/rex/post/hwbridge/ui/console/command_dispatcher/zigbee.rb +++ b/lib/rex/post/hwbridge/ui/console/command_dispatcher/zigbee.rb @@ -18,12 +18,21 @@ class Console::CommandDispatcher::Zigbee # def commands all = { - 'supported_devices' => 'Get supported zigbee devices' + 'supported_devices' => 'Get supported zigbee devices', + 'target' => 'Set the target device id', + 'channel' => 'Set the channel' } all end + # Sets the target device both in the UI class and in the base API + # @param device [String] Device ID + def set_target_device(device) + self.target_device = device + client.zigbee.set_target_device device + end + # # Lists all thesupported devices # @@ -38,7 +47,7 @@ class Console::CommandDispatcher::Zigbee print_line("none") return end - self.target_device = devices[0] if devices.size == 1 + set_target_device(devices[0]) if devices.size == 1 str = "Supported Devices: " str += devices.join(', ') str += "\nUse device name to set your desired device, default is: #{self.target_device}" @@ -61,12 +70,43 @@ class Console::CommandDispatcher::Zigbee print_line(device_opts.usage) return when '-d' - self.target_device = val + set_target_device val end end print_line("set target device to #{self.target_device}") end + # + # Sets the channel + # + def cmd_channel(*args) + chan = 11 + dev = self.target_device if self.target_device + xopts = Rex::Parser::Arguments.new( + '-h' => [ false, 'Help Banner' ], + '-d' => [ true, 'Zigbee Device' ], + '-c' => [ true, 'channel number' ] + ) + xopts.parse(args) do |opt, _idx, val| + case opt + when '-h' + print_line("Usage: channel -c \n") + print_line(xopts.usage) + return + when '-d' + dev = val + when '-c' + chan = val.to_i + end + end + if not dev + print_line("You must specify or set a target device") + return + end + client.zigbee.set_channel(dev, chan) + print_line("Device #{dev} channel set to #{chan}") + end + # # Name for this dispatcher # @@ -74,9 +114,7 @@ class Console::CommandDispatcher::Zigbee 'Zigbee' end -private attr_accessor :target_device - end end diff --git a/modules/auxiliary/client/hwbridge/connect.rb b/modules/auxiliary/client/hwbridge/connect.rb index 331af02385..1d2c82c39e 100644 --- a/modules/auxiliary/client/hwbridge/connect.rb +++ b/modules/auxiliary/client/hwbridge/connect.rb @@ -67,6 +67,8 @@ class MetasploitModule < Msf::Auxiliary if (res.code == 200) print_status res.body if datastore["DEBUGJSON"] == true return JSON.parse(res.body) + elsif res.code == 401 + print_error "Access Denied: #{res.body}" end return nil diff --git a/modules/post/hardware/zigbee/zstumbler.rb b/modules/post/hardware/zigbee/zstumbler.rb new file mode 100644 index 0000000000..f7482f21a5 --- /dev/null +++ b/modules/post/hardware/zigbee/zstumbler.rb @@ -0,0 +1,102 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' +require 'rex' +require 'msf/core/post/hardware/zigbee/utils' + +class MetasploitModule < Msf::Post + + include Msf::Post::Hardware::Zigbee::Utils + + def initialize(info={}) + super( update_info( info, + 'Name' => 'Sends Beacons to scan for active Zigbee networks', + 'Description' => %q{ Post Module to send beacon signals to the broadcast address while + channel hopping}, + 'License' => MSF_LICENSE, + 'Author' => ['Craig Smith'], + 'Platform' => ['hardware'], + 'SessionTypes' => ['hwbridge'] + )) + register_options([ + OptInt.new('CHANNEL', [false, "Disable channel hopping by forcing a channel", nil]), + OptInt.new('LOOP', [false, "How many times to loop over the channels. -1 is forever", 1]), + OptInt.new('DELAY', [false, "Delay in seconds to listen on each channel", 2]), + OptString.new('DEVICE', [false, "Zigbee device ID, defaults to target device", nil]) + ], self.class) + @seq = 0 + @channel = 11 + @stumbled = {} + @loop_count = 0 + end + + def display_details(routerdata) + stackprofile_map = {0 => "Network Specific", + 1 => "ZigBee Standard", + 2 => "ZigBee Enterprise"} + stackver_map = {0 => "ZigBee Prototype", + 1 => "ZigBee 2004", + 2 => "ZigBee 2006/2007"} + spanid, source, extpanid, stackprofilever, channel = routerdata + stackprofilever = stackprofilever.unpack("H*")[0].hex + stackprofile = stackprofilever & 0x0f + stackver = (stackprofilever & 0xf0) >> 4 + profile = "Unknown" + profile = stackprofile_map[stackprofile] if stackprofile_map.has_key? stackprofile + ver = "Unknown" + ver = stackver_map[stackver] if stackver_map.has_key? stackver + print_status("New Network: PANID: 0x#{spanid.upcase} SOURCE: 0x#{source.upcase}") + print_status(" Ext PANID: #{extpanid.upcase.scan(/../).join(':')} Stack Profile: #{profile}") + print_status(" Stack Version: #{ver}") + print_status(" Channel: #{@channel}") + end + + def scan + @loop_count += 1 if @channel > 26 or datastore["CHANNEL"] + @channel = 11 if @channel > 26 + @seq = 0 if @seq > 255 + print_status("Scanning Channel #{@channel}") + set_channel(datastore["DEVICE"], @channel) + beacon = "\x03\x08#{@seq.chr}\xff\xff\xff\xff\x07" + inject(datastore["DEVICE"], beacon) + delay = Time.now + datastore["DELAY"] + while delay > Time.now() + pkt = recv(datastore["DEVICE"]) + if pkt and pkt.size > 0 and pkt["valid_crc"] + pktdecode = dot154_packet_decode(pkt["data"]) + if (pktdecode["FSF"] & DOT154_FCF_TYPE_MASK) == DOT154_FCF_TYPE_BEACON + key = "#{pktdecode["SPAN_ID"]}#{pktdecode["SOURCE"]}" + value = [pktdecode["SPAN_ID"], pktdecode["SOURCE"], pktdecode["EXT_PAN_ID"], pktdecode["STACK_PROFILE"], @channel] + if not @stumbled.has_key? key + @stumbled[key] = value + display_details(value) + end + end + end + end + sniffer_off(datastore["DEVICE"]) # Needed to clear receive buffers + @seq += 1 + @channel += 1 if not datastore["CHANNEL"] + end + + def run + if not get_target_device and not datastore["DEVICE"] + print_error "No target device set. Either set one with the 'target' command or specify the DEVICE" + return + end + @channel = datastore["CHANNEL"] if datastore["CHANNEL"] + if datastore["LOOP"] == -1 + while(1) do + scan + end + else + while(@loop_count < datastore["LOOP"]) + scan + end + end + end + +end diff --git a/tools/hardware/killerbee_msfrelay b/tools/hardware/killerbee_msfrelay index c600232ddb..16e8195dda 100755 --- a/tools/hardware/killerbee_msfrelay +++ b/tools/hardware/killerbee_msfrelay @@ -22,6 +22,7 @@ packets_sent = 0 last_sent = 0 username = None password = None +kb = None class MSFHandler(BaseHTTPRequestHandler): def status(self): @@ -53,6 +54,7 @@ class MSFHandler(BaseHTTPRequestHandler): return status def statistics(self): + global packets_sent stats = {} stats["uptime"] = int(time.time()) - starttime stats["packet_stats"] = packets_sent @@ -66,6 +68,39 @@ class MSFHandler(BaseHTTPRequestHandler): def timezone(self): return { "system_timezone": time.strftime("%Z") } + def set_channel(self, args): + if not "chan" in args: + return self.not_supported() + chan = int(args["chan"][0]) + kb.set_channel(chan) + return { "success": True } + + def inject(self, args): + global packets_sent + if not "data" in args: + return self.not_supported() + try: + kb.inject(base64.urlsafe_b64decode(args["data"][0])) + packets_sent+=1 + except Exception, e: + print("ERROR: Unable to inject packet: {0}".format(e)) + return { "success": False } + return { "success": True } + + def recv(self): + pkt = kb.pnext() + if pkt != None and pkt[1]: + return {"data": base64.urlsafe_b64encode(pkt[0]), "valid_crc": pkt[1], "rssi": pkt[2] } + return {} + + def sniffer_off(self): + kb.sniffer_off() + return {"success": True } + + def sniffer_on(self): + kb.sniffer_on() + return {"success": True } + def supported_devices(self): devices = [] for dev in kbutils.devlist(): @@ -112,12 +147,20 @@ class MSFHandler(BaseHTTPRequestHandler): elif self.path=="/zigbee/supported_devices": self.send(self.supported_devices()) elif self.path.startswith("/zigbee/"): - re_idx = re.compile("/zigbee/(\w+)/") - m = re_idx.match(self.path) + re_dev = re.compile("/zigbee/([\d\w:]+)/") + m = re_dev.match(self.path) if m: - idx = m.group(1) - if self.path.find("/set_freq?") > -1: - self.send(self.set_freq(args)) + dev = m.group(1) + if self.path.find("/set_channel?") > -1: + self.send(self.set_channel(args)) + elif self.path.find("/inject?") > -1: + self.send(self.inject(args)) + elif self.path.find("/recv") > -1: + self.send(self.recv()) + elif self.path.find("/sniffer_off") > -1: + self.send(self.sniffer_off()) + elif self.path.find("/sniffer_on") > -1: + self.send(self.sniffer_on()) else: self.send(self.not_supported(), 404) else: @@ -160,6 +203,7 @@ if __name__ == "__main__": import argparse parser = argparse.ArgumentParser() + parser.add_argument('-i', '--iface', '--dev', action='store', dest='devstring') parser.add_argument('-u', '--user', default="msf_relay", help='HTTP Username', type=str) parser.add_argument('-p', '--password', default="rfcat_relaypass", help='HTTP Password', type=str) parser.add_argument('-P', '--Port', default=8080, type=int) @@ -168,6 +212,12 @@ if __name__ == "__main__": ifo = parser.parse_args() + try: + kb = KillerBee(device=ifo.devstring) + except KBInterfaceError as e: + print("Interface Error: {0}".format(e)) + sys.exit(-1) + username = ifo.user password = ifo.password ip = "0.0.0.0" From 97ad8be7fff7f7471abbc2114cc5a25357ad5a3a Mon Sep 17 00:00:00 2001 From: Craig Smith Date: Mon, 6 Mar 2017 22:42:15 -0800 Subject: [PATCH 3/5] Added some Zigbee Documentation --- .../modules/post/hardware/zigbee/zstumbler.md | 38 +++++++++++++++++++ lib/msf/core/post/hardware/zigbee/utils.rb | 18 ++++++++- .../post/hwbridge/extensions/zigbee/zigbee.rb | 10 +++++ 3 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 documentation/modules/post/hardware/zigbee/zstumbler.md diff --git a/documentation/modules/post/hardware/zigbee/zstumbler.md b/documentation/modules/post/hardware/zigbee/zstumbler.md new file mode 100644 index 0000000000..a70fb2bd13 --- /dev/null +++ b/documentation/modules/post/hardware/zigbee/zstumbler.md @@ -0,0 +1,38 @@ +Actively scans the Zigbee channels by sending a beacon broadcast packet and listening for responses. + +## Options + + **DEVICE** + + Zigbee Device ID. Defaults to the target device that is specified via the target command or if + one device is presented when runnign 'supported_devices' it will use that device. + + **CHANNEL** + + The Channel to scan. This will prevent the stumbler from changing channels. Range is 11-25 + + **LOOP** + + How many times to loop over the channels. Specifying a -1 will loop forever. The default is once. + + **DELAY** + + The delay in seconds to listen to each channel. The default is 2 + +## Scenarios + + Scanning channel 11 for other Zigbee devices in the area. + +``` +hwbridge > run post/hardware/zigbee/zstumbler channel=11 + +[*] Scanning Channel 11 +[*] New Network: PANID: 0x4724 SOURCE: 0x25D5 +[*] Ext PANID: 6E:03:C7:74:31:E2:74:AA Stack Profile: ZigBee Enterprise +[*] Stack Version: ZigBee 2006/2007 +[*] Channel: 11 +[*] New Network: PANID: 0x4724 SOURCE: 0x7DD1 +[*] Ext PANID: 6E:03:C7:74:31:E2:74:AA Stack Profile: ZigBee Enterprise +[*] Stack Version: ZigBee 2006/2007 +[*] Channel: 11 +``` diff --git a/lib/msf/core/post/hardware/zigbee/utils.rb b/lib/msf/core/post/hardware/zigbee/utils.rb index 149c992bbc..ef60bfad8f 100644 --- a/lib/msf/core/post/hardware/zigbee/utils.rb +++ b/lib/msf/core/post/hardware/zigbee/utils.rb @@ -143,7 +143,23 @@ module Utils end # Breaks up the packet into different sections. Also provides - # Some decoding information + # Some decoding information. This method relates to Killerbee's Pktchop method and + # Returns a similar array structure PktChop. If it's a beacon data you will also have + # A BEACONDATA array of raw beacon related packets. You can pull other decoded portions from + # the returned hash such as + # FSF + # SEQ + # SPAN_ID + # SOURCE + # SUPERFRAME + # GTS + # PENDING_ADDRESS_COUNT + # PROTOCOL_ID + # STACK_PROFILE + # CAPABILITY + # EXT_PAN_ID + # TX_OFFSET + # UPDATE_ID # @param packet [String] Raw data from recv # @return [Hash] { PktChop => [Array of data], .. def dot154_packet_decode(packet) diff --git a/lib/rex/post/hwbridge/extensions/zigbee/zigbee.rb b/lib/rex/post/hwbridge/extensions/zigbee/zigbee.rb index b456b33e86..363c62da88 100644 --- a/lib/rex/post/hwbridge/extensions/zigbee/zigbee.rb +++ b/lib/rex/post/hwbridge/extensions/zigbee/zigbee.rb @@ -53,11 +53,17 @@ class Zigbee < Extension client.send_request("/zigbee/#{dev}/set_channel?chan=#{channel}") end + # Injects a raw packet + # @param dev [String] Zigbee Device ID + # @param data [String] Raw hex data that will be Base64 encoded def inject(dev, data) data = Base64.urlsafe_encode64(data) client.send_request("/zigbee/#{dev}/inject?data=#{data}") end + # Receives data from transceiver + # @param dev [String] Zigbee Device ID + # @return [Hash] { data: HexString, valid_crc: X, rssi: X } def recv(dev) data = client.send_request("/zigbee/#{dev}/recv") if data.size > 0 @@ -66,10 +72,14 @@ class Zigbee < Extension data end + # Disables sniffer and puts the device in a state that can be changed (like adujsting channel) + # @param dev [String] Zigbee Device ID def sniffer_off(dev) client.send_request("/zigbee/#{dev}/sniffer_off") end + # Enables sniffer receive mode. Not necessary to call before calling recv + # @param dev [String] Zigbee Device ID def sniffer_on(dev) client.send_request("/zigbee/#{dev}/sniffer_on") end From 4e9b8946d82bec0a7c09ef950ef6a66d3e001efa Mon Sep 17 00:00:00 2001 From: Craig Smith Date: Mon, 6 Mar 2017 22:47:37 -0800 Subject: [PATCH 4/5] Fixed some small msftidy issues --- lib/msf/core/post/hardware/zigbee/utils.rb | 2 +- modules/post/hardware/zigbee/zstumbler.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/msf/core/post/hardware/zigbee/utils.rb b/lib/msf/core/post/hardware/zigbee/utils.rb index ef60bfad8f..85647c928e 100644 --- a/lib/msf/core/post/hardware/zigbee/utils.rb +++ b/lib/msf/core/post/hardware/zigbee/utils.rb @@ -161,7 +161,7 @@ module Utils # TX_OFFSET # UPDATE_ID # @param packet [String] Raw data from recv - # @return [Hash] { PktChop => [Array of data], .. + # @return [Hash] { PktChop => [Array of data], .. def dot154_packet_decode(packet) result = {} offset = 0 diff --git a/modules/post/hardware/zigbee/zstumbler.rb b/modules/post/hardware/zigbee/zstumbler.rb index f7482f21a5..484a019ad9 100644 --- a/modules/post/hardware/zigbee/zstumbler.rb +++ b/modules/post/hardware/zigbee/zstumbler.rb @@ -13,7 +13,7 @@ class MetasploitModule < Msf::Post def initialize(info={}) super( update_info( info, - 'Name' => 'Sends Beacons to scan for active Zigbee networks', + 'Name' => 'Sends Beacons to Scan for Active Zigbee Networks', 'Description' => %q{ Post Module to send beacon signals to the broadcast address while channel hopping}, 'License' => MSF_LICENSE, From 095a110e6530ba13a3e726f4e9877dc586e9d667 Mon Sep 17 00:00:00 2001 From: Pearce Barry Date: Thu, 16 Mar 2017 21:43:36 -0500 Subject: [PATCH 5/5] Code and doc tweaks (minor). Only one behavior change in the scan loop of zstumbler.rb to, when doing a scan across all the channels, keep it from retrying channel 11 again one last time just before it exits. --- .../modules/post/hardware/zigbee/zstumbler.md | 14 +-- lib/msf/core/post/hardware/zigbee/utils.rb | 86 +++++++------------ .../console/command_dispatcher/automotive.rb | 2 +- .../ui/console/command_dispatcher/zigbee.rb | 21 ++--- modules/post/hardware/zigbee/zstumbler.rb | 15 ++-- tools/hardware/killerbee_msfrelay | 10 +-- 6 files changed, 65 insertions(+), 83 deletions(-) diff --git a/documentation/modules/post/hardware/zigbee/zstumbler.md b/documentation/modules/post/hardware/zigbee/zstumbler.md index a70fb2bd13..f8095ac2ce 100644 --- a/documentation/modules/post/hardware/zigbee/zstumbler.md +++ b/documentation/modules/post/hardware/zigbee/zstumbler.md @@ -4,24 +4,24 @@ Actively scans the Zigbee channels by sending a beacon broadcast packet and list **DEVICE** - Zigbee Device ID. Defaults to the target device that is specified via the target command or if - one device is presented when runnign 'supported_devices' it will use that device. + ZigBee Device ID. Defaults to the target device that is specified via the target command or if + one device is presented when running 'supported_devices' it will use that device. **CHANNEL** - The Channel to scan. This will prevent the stumbler from changing channels. Range is 11-25 - + The channel to scan. Setting this options will prevent the stumbler from changing channels. Range is 11-26, inclusive. Default: not set +n **LOOP** - How many times to loop over the channels. Specifying a -1 will loop forever. The default is once. + How many times to loop over the channels. Specifying a -1 will loop forever. Default: 1 **DELAY** - The delay in seconds to listen to each channel. The default is 2 + The delay in seconds to listen to each channel. Default: 2 ## Scenarios - Scanning channel 11 for other Zigbee devices in the area. + Scanning channel 11 for other ZigBee devices in the area. ``` hwbridge > run post/hardware/zigbee/zstumbler channel=11 diff --git a/lib/msf/core/post/hardware/zigbee/utils.rb b/lib/msf/core/post/hardware/zigbee/utils.rb index 85647c928e..d5a59dd0cb 100644 --- a/lib/msf/core/post/hardware/zigbee/utils.rb +++ b/lib/msf/core/post/hardware/zigbee/utils.rb @@ -42,14 +42,27 @@ module Utils DOT154_CRYPT_ENC_MIC64 = 0x06 #: Encryption, 64-bit MIC DOT154_CRYPT_ENC_MIC128 = 0x07 #: Encryption, 128-bit MIC + # Infer if the current session is for a ZigBee device. + # @return [Boolean] true if session is for a ZigBee device, false otherwise + def is_zigbee_hwbridge_session? + return true if client.zigbee + print_error("Not a ZigBee hwbridge session") + false + end + + # Verify if a device has been specified. + # @return [Boolean] true if device is specified, false otherwise + def verify_device(device) + return true if device + print_line("No target device set, use 'target' or specify bus via the options.") + false + end + # Retrieves the target Zigbee device. This is typically set by the user via the # interactive HWBridge command line # @return [String] Zigbee device ID def get_target_device - if not client.zigbee - print_error("Not a Zigbee hwbridge session") - return - end + return unless is_zigbee_hwbridge_session? return client.zigbee.get_target_device end @@ -57,10 +70,7 @@ module Utils # Instead the user is expected to set this via the interactive HWBridge commandline # @param device [String] Zigbee device ID def set_target_device(device) - if not client.zigbee - print_error("Not a Zigbee hwbridge session") - return - end + return unless is_zigbee_hwbridge_session? client.zigbee.set_target_device device end @@ -68,15 +78,9 @@ module Utils # @param device [String] Zigbee device ID # @param channel [Integer] Channel number, typically 11-25 def set_channel(device, channel) - if not client.zigbee - print_error("Not a Zigbee hwbridge session") - return {} - end - device = client.zigbee.target_device if not device - if not device - print_line("No target device set, use 'target' or specify bus via the options") - return {} - end + return {} unless is_zigbee_hwbridge_session? + device = client.zigbee.target_device unless device + return {} unless verify_device(device) client.zigbee.set_channel(device, channel) end @@ -84,15 +88,9 @@ module Utils # @param device [String] Zigbee device ID # @param data [String] Raw binary data sent as a string def inject(device, data) - if not client.zigbee - print_error("Not a Zigbee hwbridge session") - return {} - end - device = client.zigbee.target_device if not device - if not device - print_line("No target device set, use 'target' or specify bus via the options") - return {} - end + return {} unless is_zigbee_hwbridge_session? + device = client.zigbee.target_device unless device + return {} unless verify_device(device) client.zigbee.inject(device, data) end @@ -100,45 +98,27 @@ module Utils # @param device [String] Zigbee device ID # @return [String] Binary blob of returned data def recv(device) - if not client.zigbee - print_error("Not a Zigbee hwbridge session") - return {} - end - device = client.zigbee.target_device if not device - if not device - print_line("No target device set, use 'target' or specify bus via the options") - return {} - end + return {} unless is_zigbee_hwbridge_session? + device = client.zigbee.target_device unless device + return {} unless verify_device(device) client.zigbee.recv(device) end # Turn off Zigbee receiving # @param device [String] Zigbee device ID def sniffer_off(device) - if not client.zigbee - print_error("Not a Zigbee hwbridge session") - return {} - end - device = client.zigbee.target_device if not device - if not device - print_line("No target device set, use 'target' or specify bus via the options") - return {} - end + return {} unless is_zigbee_hwbridge_session? + device = client.zigbee.target_device unless device + return {} unless verify_device(device) client.zigbee.sniffer_off(device) end # Turn on Zigbee receiving # @param device [String] Zigbee device ID def sniffer_on(device) - if not client.zigbee - print_error("Not a Zigbee hwbridge session") - return {} - end - device = client.zigbee.target_device if not device - if not device - print_line("No target device set, use 'target' or specify bus via the options") - return {} - end + return {} unless is_zigbee_hwbridge_session? + device = client.zigbee.target_device unless device + return {} unless verify_device(device) client.zigbee.sniffer_on(device) end diff --git a/lib/rex/post/hwbridge/ui/console/command_dispatcher/automotive.rb b/lib/rex/post/hwbridge/ui/console/command_dispatcher/automotive.rb index 059719ccff..cf55585767 100644 --- a/lib/rex/post/hwbridge/ui/console/command_dispatcher/automotive.rb +++ b/lib/rex/post/hwbridge/ui/console/command_dispatcher/automotive.rb @@ -63,7 +63,7 @@ class Console::CommandDispatcher::Automotive def cmd_busconfig(*args) bus = '' bus_config_opts = Rex::Parser::Arguments.new( - '-h' => [ false, 'Help Banner' ], + '-h' => [ false, 'Help banner' ], '-b' => [ true, 'Target bus'] ) bus_config_opts.parse(args) do |opt, _idx, val| diff --git a/lib/rex/post/hwbridge/ui/console/command_dispatcher/zigbee.rb b/lib/rex/post/hwbridge/ui/console/command_dispatcher/zigbee.rb index c0ce03dd72..45408f88dc 100644 --- a/lib/rex/post/hwbridge/ui/console/command_dispatcher/zigbee.rb +++ b/lib/rex/post/hwbridge/ui/console/command_dispatcher/zigbee.rb @@ -6,6 +6,7 @@ module Rex module Post module HWBridge module Ui + ### # Zigbee extension - set of commands to be executed on Zigbee compatible devices ### @@ -18,7 +19,7 @@ class Console::CommandDispatcher::Zigbee # def commands all = { - 'supported_devices' => 'Get supported zigbee devices', + 'supported_devices' => 'Get supported ZigBee devices', 'target' => 'Set the target device id', 'channel' => 'Set the channel' } @@ -38,19 +39,19 @@ class Console::CommandDispatcher::Zigbee # def cmd_supported_devices devices = client.zigbee.supported_devices - if not devices or not devices.has_key? "devices" + if !devices or !devices.has_key? "devices" print_line("error retrieving list of devices") return end devices = devices["devices"] - if not devices.size > 0 + unless devices.size > 0 print_line("none") return end set_target_device(devices[0]) if devices.size == 1 str = "Supported Devices: " - str += devices.join(', ') - str += "\nUse device name to set your desired device, default is: #{self.target_device}" + str << devices.join(', ') + str << "\nUse device name to set your desired device, default is: #{self.target_device}" print_line(str) end @@ -60,7 +61,7 @@ class Console::CommandDispatcher::Zigbee def cmd_target(*args) self.target_device = "" device_opts = Rex::Parser::Arguments.new( - '-h' => [ false, 'Help Banner' ], + '-h' => [ false, 'Help banner' ], '-d' => [ true, 'Device ID' ] ) device_opts.parse(args) do |opt, _idx, val| @@ -83,9 +84,9 @@ class Console::CommandDispatcher::Zigbee chan = 11 dev = self.target_device if self.target_device xopts = Rex::Parser::Arguments.new( - '-h' => [ false, 'Help Banner' ], - '-d' => [ true, 'Zigbee Device' ], - '-c' => [ true, 'channel number' ] + '-h' => [ false, 'Help banner' ], + '-d' => [ true, 'ZigBee device' ], + '-c' => [ true, 'Channel number' ] ) xopts.parse(args) do |opt, _idx, val| case opt @@ -99,7 +100,7 @@ class Console::CommandDispatcher::Zigbee chan = val.to_i end end - if not dev + if !dev print_line("You must specify or set a target device") return end diff --git a/modules/post/hardware/zigbee/zstumbler.rb b/modules/post/hardware/zigbee/zstumbler.rb index 484a019ad9..074132a139 100644 --- a/modules/post/hardware/zigbee/zstumbler.rb +++ b/modules/post/hardware/zigbee/zstumbler.rb @@ -13,7 +13,7 @@ class MetasploitModule < Msf::Post def initialize(info={}) super( update_info( info, - 'Name' => 'Sends Beacons to Scan for Active Zigbee Networks', + 'Name' => 'Sends Beacons to Scan for Active ZigBee Networks', 'Description' => %q{ Post Module to send beacon signals to the broadcast address while channel hopping}, 'License' => MSF_LICENSE, @@ -22,10 +22,10 @@ class MetasploitModule < Msf::Post 'SessionTypes' => ['hwbridge'] )) register_options([ - OptInt.new('CHANNEL', [false, "Disable channel hopping by forcing a channel", nil]), - OptInt.new('LOOP', [false, "How many times to loop over the channels. -1 is forever", 1]), + OptInt.new('CHANNEL', [false, "Disable channel hopping by forcing a channel (11-26)", nil]), + OptInt.new('LOOP', [false, "How many times to loop over the channels (-1 will run in an endless loop)", 1]), OptInt.new('DELAY', [false, "Delay in seconds to listen on each channel", 2]), - OptString.new('DEVICE', [false, "Zigbee device ID, defaults to target device", nil]) + OptString.new('DEVICE', [false, "ZigBee device ID, defaults to target device", nil]) ], self.class) @seq = 0 @channel = 11 @@ -55,8 +55,6 @@ class MetasploitModule < Msf::Post end def scan - @loop_count += 1 if @channel > 26 or datastore["CHANNEL"] - @channel = 11 if @channel > 26 @seq = 0 if @seq > 255 print_status("Scanning Channel #{@channel}") set_channel(datastore["DEVICE"], @channel) @@ -80,14 +78,17 @@ class MetasploitModule < Msf::Post sniffer_off(datastore["DEVICE"]) # Needed to clear receive buffers @seq += 1 @channel += 1 if not datastore["CHANNEL"] + @loop_count += 1 if @channel > 26 or datastore["CHANNEL"] + @channel = 11 if @channel > 26 end def run if not get_target_device and not datastore["DEVICE"] - print_error "No target device set. Either set one with the 'target' command or specify the DEVICE" + print_error "No target device set. Either set one with the 'target' command or specify the DEVICE." return end @channel = datastore["CHANNEL"] if datastore["CHANNEL"] + @channel = 11 if @channel > 26 if datastore["LOOP"] == -1 while(1) do scan diff --git a/tools/hardware/killerbee_msfrelay b/tools/hardware/killerbee_msfrelay index 16e8195dda..e3403f54cc 100755 --- a/tools/hardware/killerbee_msfrelay +++ b/tools/hardware/killerbee_msfrelay @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python # KillerBee Metasploit relay server import re @@ -171,7 +171,7 @@ class MSFHandler(BaseHTTPRequestHandler): class Killerbee_MSFRelay(cmd.Cmd): intro = """ - Killerbee Metasploit Relay + KillerBee Metasploit Relay """ def __init__(self, ip='0.0.0.0', port=8080): @@ -191,7 +191,7 @@ class Killerbee_MSFRelay(cmd.Cmd): try: self._sock = HTTPServer((self._ip, self._port), MSFHandler) starttime = int(time.time()) - print("Killerbee MSFRelay running.") + print("KillerBee MSFRelay running.") self._sock.serve_forever() except KeyboardInterrupt: self._sock.socket.close() @@ -236,13 +236,13 @@ if __name__ == "__main__": if len(devs) > 0: dev_found = True elif not wait_msg: - print("Insert Killerbee compatible Zigbee device. (You may need to add permissions)") + print("Insert KillerBee compatible ZigBee device. (You may need to add permissions)") wait_msg = True except KeyboardInterrupt: sys.exit() except: if not wait_msg: - print("Insert Killerbee compatible Zigbee device. (You may need to add permissions)") + print("Insert KillerBee compatible ZigBee device. (You may need to add permissions)") wait_msg = True beerelay = Killerbee_MSFRelay(ip, port)