Land #2736, basic ADSI support via meterp extapi
commit
636c43dcdc
|
@ -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
|
||||||
|
|
|
@ -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/window/window'
|
||||||
require 'rex/post/meterpreter/extensions/extapi/service/service'
|
require 'rex/post/meterpreter/extensions/extapi/service/service'
|
||||||
require 'rex/post/meterpreter/extensions/extapi/clipboard/clipboard'
|
require 'rex/post/meterpreter/extensions/extapi/clipboard/clipboard'
|
||||||
|
require 'rex/post/meterpreter/extensions/extapi/adsi/adsi'
|
||||||
|
|
||||||
module Rex
|
module Rex
|
||||||
module Post
|
module Post
|
||||||
|
@ -30,7 +31,8 @@ class Extapi < Extension
|
||||||
{
|
{
|
||||||
'window' => Rex::Post::Meterpreter::Extensions::Extapi::Window::Window.new(client),
|
'window' => Rex::Post::Meterpreter::Extensions::Extapi::Window::Window.new(client),
|
||||||
'service' => Rex::Post::Meterpreter::Extensions::Extapi::Service::Service.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)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
|
|
|
@ -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_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_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
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -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/window'
|
||||||
require 'rex/post/meterpreter/ui/console/command_dispatcher/extapi/service'
|
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/clipboard'
|
||||||
|
require 'rex/post/meterpreter/ui/console/command_dispatcher/extapi/adsi'
|
||||||
|
|
||||||
Klass = Console::CommandDispatcher::Extapi
|
Klass = Console::CommandDispatcher::Extapi
|
||||||
|
|
||||||
|
@ -23,7 +24,8 @@ class Console::CommandDispatcher::Extapi
|
||||||
[
|
[
|
||||||
Klass::Window,
|
Klass::Window,
|
||||||
Klass::Service,
|
Klass::Service,
|
||||||
Klass::Clipboard
|
Klass::Clipboard,
|
||||||
|
Klass::Adsi
|
||||||
]
|
]
|
||||||
|
|
||||||
include Console::CommandDispatcher
|
include Console::CommandDispatcher
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue