2011-10-17 04:20:53 +00:00
|
|
|
##
|
|
|
|
# $Id$
|
|
|
|
##
|
|
|
|
|
|
|
|
##
|
|
|
|
# This file is part of the Metasploit Framework and may be subject to
|
|
|
|
# redistribution and commercial restrictions. Please see the Metasploit
|
2012-02-21 01:40:50 +00:00
|
|
|
# web site for more information on licensing and terms of use.
|
|
|
|
# http://metasploit.com/
|
2011-10-17 04:20:53 +00:00
|
|
|
##
|
|
|
|
|
|
|
|
require 'msf/core'
|
|
|
|
require 'rex'
|
|
|
|
|
|
|
|
class Metasploit3 < Msf::Post
|
|
|
|
|
|
|
|
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.
|
|
|
|
},
|
|
|
|
'License' => MSF_LICENSE,
|
|
|
|
'Version' => '$Revision$',
|
|
|
|
'Platform' => ['windows'],
|
|
|
|
'SessionTypes' => ['meterpreter'],
|
|
|
|
'Author' => ['Kx499']
|
|
|
|
))
|
|
|
|
|
|
|
|
register_options(
|
|
|
|
[
|
2011-10-17 16:35:53 +00:00
|
|
|
OptString.new('PATH', [ true, 'Directory to begin search from', '']),
|
2011-10-17 04:20:53 +00:00
|
|
|
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 get_imperstoken
|
|
|
|
adv = session.railgun.advapi32
|
|
|
|
tok_all = "TOKEN_ASSIGN_PRIMARY |TOKEN_DUPLICATE | TOKEN_IMPERSONATE | TOKEN_QUERY | "
|
|
|
|
tok_all << "TOKEN_QUERY_SOURCE | TOKEN_ADJUST_PRIVILEGES | TOKEN_ADJUST_GROUPS"
|
|
|
|
tok_all << " | TOKEN_ADJUST_DEFAULT"
|
|
|
|
|
|
|
|
#get impersonation token handle it["DuplicateTokenhandle"] carries this value
|
|
|
|
#p = kern.GetCurrentProcess() #get handle to current process
|
|
|
|
pid = session.sys.process.open.pid
|
|
|
|
pr = session.sys.process.open(pid, PROCESS_ALL_ACCESS)
|
2011-10-23 11:56:13 +00:00
|
|
|
pt = adv.OpenProcessToken(pr.handle, tok_all, 4) #get handle to primary token
|
2011-10-17 04:20:53 +00:00
|
|
|
it = adv.DuplicateToken(pt["TokenHandle"],2, 4) # get an impersonation token
|
|
|
|
if it["return"] #if it fails return 0 for error handling
|
|
|
|
return it["DuplicateTokenHandle"]
|
2011-10-23 11:56:13 +00:00
|
|
|
else
|
2011-10-17 04:20:53 +00:00
|
|
|
return 0
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def check_dir(dir, token)
|
2011-12-24 20:57:46 +00:00
|
|
|
# If path doesn't exist, do not continue
|
|
|
|
begin
|
|
|
|
session.fs.dir.entries(dir)
|
|
|
|
rescue
|
|
|
|
print_error("Path seems invalid: #{dir}")
|
|
|
|
return nil
|
|
|
|
end
|
|
|
|
|
2011-10-17 04:20:53 +00:00
|
|
|
adv = session.railgun.advapi32
|
|
|
|
si = "OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION"
|
|
|
|
result = ""
|
|
|
|
|
|
|
|
#define generic mapping structure
|
|
|
|
gen_map = [0,0,0,0]
|
|
|
|
gen_map = gen_map.pack("L")
|
|
|
|
|
2011-10-23 11:56:13 +00:00
|
|
|
#get Security Descriptor for the directory
|
2011-10-17 04:20:53 +00:00
|
|
|
f = adv.GetFileSecurityA(dir, si, 20, 20, 4)
|
|
|
|
f = adv.GetFileSecurityA(dir, si, f["lpnLengthNeeded"], f["lpnLengthNeeded"], 4)
|
|
|
|
sd = f["pSecurityDescriptor"]
|
|
|
|
|
|
|
|
#check for write access, called once to get buffer size
|
|
|
|
a = adv.AccessCheck(sd, token, "ACCESS_READ | ACCESS_WRITE", gen_map, 0, 0, 4, 8)
|
|
|
|
len = a["PrivilegeSetLength"]
|
|
|
|
|
|
|
|
r = adv.AccessCheck(sd, token, "ACCESS_READ", gen_map, len, len, 4, 8)
|
2011-12-24 20:57:46 +00:00
|
|
|
if !r["return"] then return nil end
|
2011-10-17 04:20:53 +00:00
|
|
|
if r["GrantedAccess"] > 0 then result << "R" end
|
|
|
|
|
|
|
|
w = adv.AccessCheck(sd, token, "ACCESS_WRITE", gen_map, len, len, 4, 8)
|
2011-12-24 20:57:46 +00:00
|
|
|
if !w["return"] then return nil end
|
2011-10-17 04:20:53 +00:00
|
|
|
if w["GrantedAccess"] > 0 then result << "W" end
|
|
|
|
end
|
|
|
|
|
|
|
|
def enum_subdirs(dpath, maxdepth, token)
|
|
|
|
filter = datastore['FILTER']
|
|
|
|
filter = nil if datastore['FILTER'] == 'NA'
|
|
|
|
dirs = session.fs.dir.foreach(dpath)
|
|
|
|
if maxdepth >= 1 or maxdepth < 0
|
|
|
|
dirs.each do|d|
|
|
|
|
next if d =~ /^(\.|\.\.)$/
|
|
|
|
realpath = dpath + '\\' + d
|
|
|
|
if session.fs.file.stat(realpath).directory?
|
2011-10-23 11:56:13 +00:00
|
|
|
perm = check_dir(realpath, token)
|
2011-10-17 04:20:53 +00:00
|
|
|
if !filter or perm.include? filter
|
|
|
|
print_status(perm + "\t" + realpath)
|
|
|
|
end
|
|
|
|
enum_subdirs(realpath, maxdepth - 1,token)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def run
|
|
|
|
t = 0 #holds impers token
|
|
|
|
|
|
|
|
#check and set vars
|
|
|
|
if not datastore['PATH'].empty?
|
|
|
|
path = datastore['PATH']
|
|
|
|
end
|
|
|
|
|
|
|
|
depth = -1
|
|
|
|
|
|
|
|
if datastore['DEPTH'] > 0
|
|
|
|
depth = datastore['DEPTH']
|
|
|
|
end
|
|
|
|
|
|
|
|
#get impersonation token
|
|
|
|
print_status("Getting impersonation token...")
|
2012-05-04 16:44:05 +00:00
|
|
|
begin
|
|
|
|
t = get_imperstoken()
|
|
|
|
rescue ::Exception => e
|
|
|
|
# Failure due to timeout, access denied, etc.
|
|
|
|
t = 0
|
|
|
|
vprint_error("Error #{e.message} while using get_imperstoken()")
|
|
|
|
vprint_error(e.backtrace)
|
|
|
|
end
|
2011-10-23 11:56:13 +00:00
|
|
|
|
2011-10-17 04:20:53 +00:00
|
|
|
#loop through sub dirs if we have an impers token..else error
|
|
|
|
if t == 0
|
|
|
|
print_error("Getting impersonation token failed")
|
|
|
|
else
|
|
|
|
print_status("Got token...")
|
|
|
|
print_status("Checking directory permissions from: " + path)
|
|
|
|
|
2011-12-24 20:57:46 +00:00
|
|
|
is_path_valid = check_dir(path, t)
|
|
|
|
if not is_path_valid.nil?
|
|
|
|
#call recursive function to loop through and check all sub directories
|
|
|
|
enum_subdirs(path, depth, t)
|
|
|
|
end
|
2011-10-17 04:20:53 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|