lib/msf/core/post/hardware/zigbee/utils.rb
# -*- 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
# 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
return unless is_zigbee_hwbridge_session?
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)
return unless is_zigbee_hwbridge_session?
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)
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
# 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)
return {} unless is_zigbee_hwbridge_session?
device = client.zigbee.target_device unless device
return {} unless verify_device(device)
client.zigbee.inject(device, data)
end
# Receives data from the Zigbee device
# @param device [String] Zigbee device ID
# @return [String] Binary blob of returned data
def recv(device)
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)
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)
return {} unless is_zigbee_hwbridge_session?
device = client.zigbee.target_device unless device
return {} unless verify_device(device)
client.zigbee.sniffer_on(device)
end
# Breaks up the packet into different sections. Also provides
# 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)
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