##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

require 'msf/core'

class MetasploitModule < Msf::Post

  include Msf::Post::Windows::Accounts

  def initialize(info={})
    super(update_info(info,
      'Name'           => "Windows Gather Directory Permissions Enumeration",
      'Description'    => %q{
        This module enumerates directories and lists the permissions set
        on found directories. Please note: if the PATH option isn't specified,
        then the module will start enumerate whatever is in the target machine's
        %PATH% variable.
      },
      'License'        => MSF_LICENSE,
      'Platform'       => ['win'],
      'SessionTypes'   => ['meterpreter'],
      'Author'         =>
        [
          'Kx499',
          'Ben Campbell',
          'sinn3r'
        ]
    ))

    register_options(
      [
        OptString.new('PATH', [ false, 'Directory to begin search from', '']),
        OptEnum.new('FILTER', [ false, 'Filter to limit results by', 'NA', [ 'NA', 'R', 'W', 'RW' ]]),
        OptInt.new('DEPTH', [ true, 'Depth to drill down into subdirs, O = no limit',0]),
      ], self.class)
  end

  def enum_subdirs(perm_filter, dpath, maxdepth, token)
    filter = datastore['FILTER']
    filter = nil if datastore['FILTER'] == 'NA'

    begin
      dirs = session.fs.dir.foreach(dpath)
    rescue Rex::Post::Meterpreter::RequestError
      # Sometimes we cannot see the dir
      dirs = []
    end

    if maxdepth >= 1 or maxdepth < 0
      dirs.each do|d|
        next if d =~ /^(\.|\.\.)$/
        realpath = dpath + '\\' + d
        if session.fs.file.stat(realpath).directory?
          perm = check_dir_perms(realpath, token)
          if perm_filter and perm and perm.include?(perm_filter)
            print_status(perm + "\t" + realpath)
          end
          enum_subdirs(perm_filter, realpath, maxdepth - 1,token)
        end
      end
    end
  end

  def get_paths
    p = datastore['PATH']
    return [p] if not p.nil? and not p.empty?

    begin
      p = cmd_exec("cmd.exe", "/c echo %PATH%")
    rescue Rex::Post::Meterpreter::RequestError => e
      vprint_error(e.message)
      return []
    end
    print_status("Option 'PATH' isn't specified. Using system %PATH%")
    if p.include?(';')
      return p.split(';')
    else
      return [p]
    end
  end

  def get_token
    print_status("Getting impersonation token...")
    begin
      t = get_imperstoken()
    rescue ::Exception => e
      # Failure due to timeout, access denied, etc.
      t = nil
      vprint_error("Error #{e.message} while using get_imperstoken()")
      vprint_error(e.backtrace)
    end
    return t
  end

  def enum_perms(perm_filter, token, depth, paths)
    paths.each do |path|
      next if path.empty?
      path = path.strip

      print_status("Checking directory permissions from: #{path}")

      perm = check_dir_perms(path, token)
      if not perm.nil?
        # Show the permission of the parent directory
        if perm_filter and perm.include?(perm_filter)
          print_status(perm + "\t" + path)
        end

        #call recursive function to loop through and check all sub directories
        enum_subdirs(perm_filter, path, depth, token)
      end
    end
  end

  def run
    perm_filter = datastore['FILTER']
    perm_filter = nil if datastore['FILTER'] == 'NA'

    paths = get_paths
    if paths.empty?
      print_error("Unable to get the path")
      return
    end

    depth = -1
    if datastore['DEPTH'] > 0
      depth = datastore['DEPTH']
    end

    t = get_token

    unless t
      print_error("Getting impersonation token failed")
    else
      print_status("Got token: #{t.to_s}...")
      enum_perms(perm_filter, t, depth, paths)
    end
  end
end
