Land #2736, basic ADSI support via meterp extapi

This commit is contained in:
Tod Beardsley 2014-01-22 15:24:01 -06:00
commit 636c43dcdc
No known key found for this signature in database
GPG Key ID: 1EFFB682ADB9F193
5 changed files with 283 additions and 2 deletions

View File

@ -0,0 +1,71 @@
# -*- coding: binary -*-
module Rex
module Post
module Meterpreter
module Extensions
module Extapi
module Adsi
###
#
# This meterpreter extension contains extended API functions for
# querying and managing desktop windows.
#
###
class Adsi
def initialize(client)
@client = client
end
#
# Perform a generic domain query against ADSI.
#
# @param domain_name [String] The FQDN of the target domain.
# @param filter [String] The filter to apply to the query in
# LDAP format.
# @param max_results [Integer] The maximum number of results
# to return.
# @param page_size [Integer] The size of the page of results
# to return.
# @param fields [Array] Array of string fields to return for
# each result found
#
# @returns [Hash] Array of field names with associated results.
#
def domain_query(domain_name, filter, max_results, page_size, fields)
request = Packet.create_request('extapi_adsi_domain_query')
request.add_tlv(TLV_TYPE_EXT_ADSI_DOMAIN, domain_name)
request.add_tlv(TLV_TYPE_EXT_ADSI_FILTER, filter)
request.add_tlv(TLV_TYPE_EXT_ADSI_MAXRESULTS, max_results)
request.add_tlv(TLV_TYPE_EXT_ADSI_PAGESIZE, page_size)
fields.each do |f|
request.add_tlv(TLV_TYPE_EXT_ADSI_FIELD, f)
end
response = client.send_request(request)
results = []
response.each(TLV_TYPE_EXT_ADSI_RESULT) { |r|
result = []
r.each(TLV_TYPE_EXT_ADSI_VALUE) { |v|
result << v.value
}
results << result
}
return {
:fields => fields,
:results => results
}
end
attr_accessor :client
end
end; end; end; end; end; end

View File

@ -4,6 +4,7 @@ require 'rex/post/meterpreter/extensions/extapi/tlv'
require 'rex/post/meterpreter/extensions/extapi/window/window'
require 'rex/post/meterpreter/extensions/extapi/service/service'
require 'rex/post/meterpreter/extensions/extapi/clipboard/clipboard'
require 'rex/post/meterpreter/extensions/extapi/adsi/adsi'
module Rex
module Post
@ -30,7 +31,8 @@ class Extapi < Extension
{
'window' => Rex::Post::Meterpreter::Extensions::Extapi::Window::Window.new(client),
'service' => Rex::Post::Meterpreter::Extensions::Extapi::Service::Service.new(client),
'clipboard' => Rex::Post::Meterpreter::Extensions::Extapi::Clipboard::Clipboard.new(client)
'clipboard' => Rex::Post::Meterpreter::Extensions::Extapi::Clipboard::Clipboard.new(client),
'adsi' => Rex::Post::Meterpreter::Extensions::Extapi::Adsi::Adsi.new(client)
})
},
])

View File

@ -40,6 +40,14 @@ TLV_TYPE_EXT_CLIPBOARD_TYPE_IMAGE_JPG_DIMX = TLV_META_TYPE_UINT | (TLV_TYPE_E
TLV_TYPE_EXT_CLIPBOARD_TYPE_IMAGE_JPG_DIMY = TLV_META_TYPE_UINT | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 47)
TLV_TYPE_EXT_CLIPBOARD_TYPE_IMAGE_JPG_DATA = TLV_META_TYPE_RAW | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 48)
TLV_TYPE_EXT_ADSI_DOMAIN = TLV_META_TYPE_STRING | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 55)
TLV_TYPE_EXT_ADSI_FILTER = TLV_META_TYPE_STRING | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 56)
TLV_TYPE_EXT_ADSI_FIELD = TLV_META_TYPE_STRING | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 57)
TLV_TYPE_EXT_ADSI_VALUE = TLV_META_TYPE_STRING | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 58)
TLV_TYPE_EXT_ADSI_RESULT = TLV_META_TYPE_GROUP | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 59)
TLV_TYPE_EXT_ADSI_MAXRESULTS = TLV_META_TYPE_UINT | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 60)
TLV_TYPE_EXT_ADSI_PAGESIZE = TLV_META_TYPE_UINT | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 61)
end
end
end

View File

@ -16,6 +16,7 @@ class Console::CommandDispatcher::Extapi
require 'rex/post/meterpreter/ui/console/command_dispatcher/extapi/window'
require 'rex/post/meterpreter/ui/console/command_dispatcher/extapi/service'
require 'rex/post/meterpreter/ui/console/command_dispatcher/extapi/clipboard'
require 'rex/post/meterpreter/ui/console/command_dispatcher/extapi/adsi'
Klass = Console::CommandDispatcher::Extapi
@ -23,7 +24,8 @@ class Console::CommandDispatcher::Extapi
[
Klass::Window,
Klass::Service,
Klass::Clipboard
Klass::Clipboard,
Klass::Adsi
]
include Console::CommandDispatcher

View File

@ -0,0 +1,198 @@
# -*- coding: binary -*-
require 'rex/post/meterpreter'
module Rex
module Post
module Meterpreter
module Ui
###
#
# Extended API ADSI management user interface.
#
###
class Console::CommandDispatcher::Extapi::Adsi
Klass = Console::CommandDispatcher::Extapi::Adsi
include Console::CommandDispatcher
# Zero indicates "no limit"
DEFAULT_MAX_RESULTS = 0
DEFAULT_PAGE_SIZE = 0
#
# List of supported commands.
#
def commands
{
"adsi_user_enum" => "Enumerate all users on the specified domain.",
"adsi_computer_enum" => "Enumerate all computers on the specified domain.",
"adsi_domain_query" => "Enumerate all objects on the specified domain that match a filter."
}
end
#
# Name for this dispatcher
#
def name
"Extapi: ADSI Management"
end
#
# Options for the adsi_user_enum command.
#
@@adsi_user_enum_opts = Rex::Parser::Arguments.new(
"-h" => [ false, "Help banner" ],
"-m" => [ true, "Maximum results to return." ],
"-p" => [ true, "Result set page size." ]
)
def adsi_user_enum_usage
print(
"\nUsage: adsi_user_enum <domain> [-h] [-m maxresults] [-p pagesize]\n\n" +
"Enumerate the users on the target domain.\n\n" +
"Enumeration returns information such as the user name, SAM account name, locked\n" +
"status, desc, and comment.\n" +
@@adsi_user_enum_opts.usage)
end
#
# Enumerate domain users.
#
def cmd_adsi_user_enum(*args)
args.unshift("-h") if args.length == 0
if args.include?("-h")
adsi_user_enum_usage
return true
end
domain = args.shift
filter = "(objectClass=user)"
fields = [
"samaccountname",
"name",
"distinguishedname",
"description",
"comment"
]
args = [domain, filter] + fields + args
return cmd_adsi_domain_query(*args)
end
#
# Options for the adsi_computer_enum command.
#
@@adsi_computer_enum_opts = Rex::Parser::Arguments.new(
"-h" => [ false, "Help banner" ],
"-m" => [ true, "Maximum results to return." ],
"-p" => [ true, "Result set page size." ]
)
def adsi_computer_enum_usage
print(
"\nUsage: adsi_computer_enum <domain> [-h] [-m maxresults] [-p pagesize]\n\n" +
"Enumerate the computers on the target domain.\n\n" +
"Enumeration returns information such as the computer name, desc, and comment.\n" +
@@adsi_computer_enum_opts.usage)
end
#
# Enumerate domain computers.
#
def cmd_adsi_computer_enum(*args)
args.unshift("-h") if args.length == 0
if args.include?("-h")
adsi_computer_enum_usage
return true
end
domain = args.shift
filter = "(objectClass=computer)"
fields = [
"name",
"distinguishedname",
"description",
"comment"
]
args = [domain, filter] + fields + args
return cmd_adsi_domain_query(*args)
end
#
# Options for the adsi_domain_query command.
#
@@adsi_domain_query_opts = Rex::Parser::Arguments.new(
"-h" => [ false, "Help banner" ],
"-m" => [ true, "Maximum results to return." ],
"-p" => [ true, "Result set page size." ]
)
def adsi_domain_query_usage
print(
"\nUsage: adsi_domain_query <domain> <filter> <field 1> [field 2 [field ..]] [-h] [-m maxresults] [-p pagesize]\n\n" +
"Enumerate the objects on the target domain.\n\n" +
"Enumeration returns the set of fields that are specified.\n" +
@@adsi_domain_query_opts.usage)
end
#
# Enumerate domain objects.
#
def cmd_adsi_domain_query(*args)
page_size = DEFAULT_PAGE_SIZE
max_results = DEFAULT_MAX_RESULTS
args.unshift("-h") if args.length < 3
@@adsi_domain_query_opts.parse(args) { |opt, idx, val|
case opt
when "-p"
page_size = val.to_i
when "-m"
max_results = val.to_i
when "-h"
adsi_domain_query_usage
return true
end
}
# Assume that the flags are passed in at the end. Safe?
switch_index = args.index { |a| a.start_with?("-") }
if switch_index
args = args.first(switch_index)
end
domain = args.shift
filter = args.shift
objects = client.extapi.adsi.domain_query(domain, filter, max_results, page_size, args)
table = Rex::Ui::Text::Table.new(
'Header' => "#{domain} Objects",
'Indent' => 0,
'SortIndex' => 0,
'Columns' => objects[:fields]
)
objects[:results].each do |c|
table << c
end
print_line
print_line(table.to_s)
print_line("Total objects: #{objects[:results].length}")
print_line
return true
end
end
end
end
end
end