metasploit-aggregator 0.1.1 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/bin/{msfaggregator → metasploit-aggregator} +11 -0
- data/lib/metasploit/aggregator.rb +15 -4
- data/lib/metasploit/aggregator/connection_manager.rb +18 -0
- data/lib/metasploit/aggregator/forwarder.rb +10 -4
- data/lib/metasploit/aggregator/http/responder.rb +4 -0
- data/lib/metasploit/aggregator/http_forwarder.rb +0 -3
- data/lib/metasploit/aggregator/router.rb +3 -3
- data/lib/metasploit/aggregator/session_detail_service.rb +71 -0
- data/lib/metasploit/aggregator/tlv/packet.rb +740 -0
- data/lib/metasploit/aggregator/tlv/packet_parser.rb +103 -0
- data/lib/metasploit/aggregator/tlv/uuid.rb +275 -0
- data/lib/metasploit/aggregator/version.rb +1 -1
- data/metasploit-aggregator.gemspec +2 -1
- metadata +22 -4
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: df43d9c519841eb237a29d07f02423decce8ae9c
|
4
|
+
data.tar.gz: 3a9f144ffceefc2caf76a0b99aaacfd8d933cab5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 77a8d3d7de9523f6680346211b722b901eaac594ab51124c3f84053dfbf4dda8391524651d0bbbaeb1ab6b7592be3a4cd7b2a1aff227196b04c80932093e7f53
|
7
|
+
data.tar.gz: da9034ab7c33353c9eb890ff67885f025957f754be923264dbb0a5ad14545a818003c41131ea0a7bc77f81242ca9e0ae12ab279b68664a2a3cf1947c0cec9567
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data.tar.gz.sig
CHANGED
Binary file
|
@@ -33,5 +33,16 @@ loop do
|
|
33
33
|
server.stop
|
34
34
|
elsif command.chomp == 'park'
|
35
35
|
client.release_session($stdin.gets.chomp)
|
36
|
+
elsif command.chomp == 'details'
|
37
|
+
client = Metasploit::Aggregator::ServerProxy.new(admin_host, admin_port)
|
38
|
+
client.sessions.each_pair do |payload, console|
|
39
|
+
details = client.session_details(payload)
|
40
|
+
$stdout.puts payload
|
41
|
+
details.each_pair do |key, attr|
|
42
|
+
$stdout.print "\t"
|
43
|
+
$stdout.print "#{key}:"
|
44
|
+
$stdout.puts "#{attr}"
|
45
|
+
end
|
46
|
+
end
|
36
47
|
end
|
37
48
|
end
|
@@ -42,6 +42,11 @@ module Metasploit
|
|
42
42
|
# index for impl
|
43
43
|
end
|
44
44
|
|
45
|
+
# return any extended details for the payload requested
|
46
|
+
def session_details(payload)
|
47
|
+
|
48
|
+
end
|
49
|
+
|
45
50
|
# start a listening port maintained on the service
|
46
51
|
# connections are forwarded to any registered default
|
47
52
|
# TODO: may want to require a type here for future proof of api
|
@@ -116,6 +121,12 @@ module Metasploit
|
|
116
121
|
Logger.log(e.to_s)
|
117
122
|
end
|
118
123
|
|
124
|
+
def session_details(payload)
|
125
|
+
@client.call(:session_details, payload)
|
126
|
+
rescue MessagePack::RPC::TimeoutError => e
|
127
|
+
Logger.log(e.to_s)
|
128
|
+
end
|
129
|
+
|
119
130
|
def add_cable(type, host, port, certificate = nil)
|
120
131
|
@client.call(:add_cable, type, host, port, certificate)
|
121
132
|
rescue MessagePack::RPC::TimeoutError => e
|
@@ -222,6 +233,10 @@ module Metasploit
|
|
222
233
|
true # return always return success for now
|
223
234
|
end
|
224
235
|
|
236
|
+
def session_details(payload)
|
237
|
+
@manager.connection_details(payload)
|
238
|
+
end
|
239
|
+
|
225
240
|
def add_cable(type, host, port, certificate = nil)
|
226
241
|
unless @manager.nil?
|
227
242
|
case type
|
@@ -272,10 +287,6 @@ module Metasploit
|
|
272
287
|
true
|
273
288
|
end
|
274
289
|
|
275
|
-
def release_session(host)
|
276
|
-
@manager.park(host)
|
277
|
-
end
|
278
|
-
|
279
290
|
def request(uuid)
|
280
291
|
# return requests here
|
281
292
|
result = nil
|
@@ -15,6 +15,7 @@ module Metasploit
|
|
15
15
|
@cables = []
|
16
16
|
@manager_mutex = Mutex.new
|
17
17
|
@router = Router.instance
|
18
|
+
@details_cache = SessionDetailService.instance
|
18
19
|
end
|
19
20
|
|
20
21
|
def self.ssl_generate_certificate
|
@@ -106,6 +107,23 @@ module Metasploit
|
|
106
107
|
connections
|
107
108
|
end
|
108
109
|
|
110
|
+
def connection_details(payload)
|
111
|
+
detail_map = {}
|
112
|
+
details = @details_cache.session_details(payload)
|
113
|
+
unless details.nil?
|
114
|
+
details.each_pair do |key, value|
|
115
|
+
detail_map[key] = value.to_s
|
116
|
+
end
|
117
|
+
end
|
118
|
+
@cables.each do |cable|
|
119
|
+
next unless cable.forwarder.connections.include?(payload)
|
120
|
+
# TODO: improve how time is exposed for live connections
|
121
|
+
time = cable.forwarder.connection_info(payload)['TIME']
|
122
|
+
detail_map['LAST_SEEN'] = Time.now - time unless time.nil?
|
123
|
+
end
|
124
|
+
detail_map
|
125
|
+
end
|
126
|
+
|
109
127
|
def cables
|
110
128
|
local_cables = []
|
111
129
|
@cables.each do |cable|
|
@@ -33,16 +33,22 @@ module Metasploit
|
|
33
33
|
connections
|
34
34
|
end
|
35
35
|
|
36
|
+
def connection_info(connection)
|
37
|
+
info = {}
|
38
|
+
info['TIME'] = @response_queues[connection].time unless @response_queues[connection].nil?
|
39
|
+
info
|
40
|
+
end
|
41
|
+
|
36
42
|
def flush_stale_sessions
|
37
43
|
@forwarder_mutex.synchronize do
|
38
44
|
stale_sessions = []
|
39
|
-
@response_queues.each_pair do |
|
45
|
+
@response_queues.each_pair do |payload, queue|
|
40
46
|
unless (queue.time + CONNECTION_TIMEOUT) > Time.now
|
41
|
-
stale_sessions <<
|
47
|
+
stale_sessions << payload
|
42
48
|
end
|
43
49
|
end
|
44
|
-
stale_sessions.each do |
|
45
|
-
stale_queue = @response_queues.delete(
|
50
|
+
stale_sessions.each do |payload|
|
51
|
+
stale_queue = @response_queues.delete(payload)
|
46
52
|
stale_queue.stop_processing unless stale_queue.nil?
|
47
53
|
end
|
48
54
|
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
require "metasploit/aggregator/session_detail_service"
|
1
2
|
require "metasploit/aggregator/http/request"
|
2
3
|
|
3
4
|
module Metasploit
|
@@ -19,6 +20,7 @@ module Metasploit
|
|
19
20
|
@thread = Thread.new { process_requests }
|
20
21
|
@time = Time.now
|
21
22
|
@router = Router.instance
|
23
|
+
@session_service = SessionDetailService.instance
|
22
24
|
@pending_requests = nil
|
23
25
|
end
|
24
26
|
|
@@ -39,6 +41,7 @@ module Metasploit
|
|
39
41
|
end
|
40
42
|
|
41
43
|
# response from get_forward will be a queue to push messages onto and a response queue to retrieve result from
|
44
|
+
@session_service.add_request(request_task, @uri)
|
42
45
|
send << request_task
|
43
46
|
@pending_request = connection
|
44
47
|
|
@@ -47,6 +50,7 @@ module Metasploit
|
|
47
50
|
# now get the response once available and send back using this connection
|
48
51
|
begin
|
49
52
|
request_obj = recv.pop
|
53
|
+
@session_service.add_request(request_task, @uri)
|
50
54
|
@pending_request = nil
|
51
55
|
request_obj.headers.each do |line|
|
52
56
|
connection.write line
|
@@ -31,9 +31,9 @@ module Metasploit
|
|
31
31
|
end
|
32
32
|
end
|
33
33
|
|
34
|
-
def get_forward(
|
35
|
-
unless @forward_routes[
|
36
|
-
@forward_routes[
|
34
|
+
def get_forward(payload)
|
35
|
+
unless @forward_routes[payload].nil?
|
36
|
+
@forward_routes[payload]
|
37
37
|
else
|
38
38
|
@forward_routes['default']
|
39
39
|
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'digest/md5'
|
2
|
+
require 'singleton'
|
3
|
+
require 'metasploit/aggregator/tlv/packet'
|
4
|
+
require 'metasploit/aggregator/tlv/packet_parser'
|
5
|
+
require 'metasploit/aggregator/tlv/uuid'
|
6
|
+
|
7
|
+
module Metasploit
|
8
|
+
module Aggregator
|
9
|
+
class SessionDetailService
|
10
|
+
include Singleton
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
@mutex = Mutex.new
|
14
|
+
@tlv_queue = Queue.new
|
15
|
+
@thread = Thread.new { process_tlv }
|
16
|
+
# for now since all data is http no cipher is required on the parser
|
17
|
+
@parser = Metasploit::Aggregator::Tlv::PacketParser.new
|
18
|
+
@r, @w = IO.pipe
|
19
|
+
@payloads_count = 0;
|
20
|
+
@detail_cache = {}
|
21
|
+
end
|
22
|
+
|
23
|
+
def add_request(request, payload)
|
24
|
+
@tlv_queue << [ request, payload ]
|
25
|
+
if @detail_cache[payload] && @detail_cache[payload]['REMOTE_SOCKET'].nil?
|
26
|
+
@detail_cache[payload]['REMOTE_SOCKET'] = "#{request.socket.peeraddr[3]}:#{request.socket.peeraddr[1]}"
|
27
|
+
@detail_cache[payload]['LOCAL_SOCKET'] = "#{request.socket.addr[3]}:#{request.socket.addr[1]}"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def session_details(payload)
|
32
|
+
@detail_cache[payload]
|
33
|
+
end
|
34
|
+
|
35
|
+
def process_tlv
|
36
|
+
while true
|
37
|
+
begin
|
38
|
+
request, payload = @tlv_queue.pop
|
39
|
+
if request.body && request.body.length > 0
|
40
|
+
# process body as tlv
|
41
|
+
@w.write(request.body)
|
42
|
+
packet = @parser.recv(@r)
|
43
|
+
next unless packet
|
44
|
+
unless @detail_cache[payload]
|
45
|
+
@detail_cache[payload] = { 'ID' => (@payloads_count += 1) }
|
46
|
+
end
|
47
|
+
if packet.has_tlv?(Metasploit::Aggregator::Tlv::TLV_TYPE_UUID)
|
48
|
+
args = { :raw => packet.get_tlv_value(Metasploit::Aggregator::Tlv::TLV_TYPE_UUID) }
|
49
|
+
@detail_cache[payload]['UUID'] = Metasploit::Aggregator::Tlv::UUID.new(args)
|
50
|
+
end
|
51
|
+
if packet.has_tlv?(Metasploit::Aggregator::Tlv::TLV_TYPE_MACHINE_ID)
|
52
|
+
@detail_cache[payload]['MachineID'] = Digest::MD5.hexdigest(packet.get_tlv_value(Metasploit::Aggregator::Tlv::TLV_TYPE_MACHINE_ID).downcase.strip)
|
53
|
+
end
|
54
|
+
if packet.has_tlv?(Metasploit::Aggregator::Tlv::TLV_TYPE_USER_NAME)
|
55
|
+
@detail_cache[payload]['USER'] = packet.get_tlv_value(Metasploit::Aggregator::Tlv::TLV_TYPE_USER_NAME)
|
56
|
+
end
|
57
|
+
if packet.has_tlv?(Metasploit::Aggregator::Tlv::TLV_TYPE_COMPUTER_NAME)
|
58
|
+
@detail_cache[payload]['HOSTNAME'] = packet.get_tlv_value(Metasploit::Aggregator::Tlv::TLV_TYPE_COMPUTER_NAME)
|
59
|
+
end
|
60
|
+
if packet.has_tlv?(Metasploit::Aggregator::Tlv::TLV_TYPE_OS_NAME)
|
61
|
+
@detail_cache[payload]['OS'] = packet.get_tlv_value(Metasploit::Aggregator::Tlv::TLV_TYPE_OS_NAME)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
rescue
|
65
|
+
Logger.log $!
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,740 @@
|
|
1
|
+
# -*- coding: binary -*-
|
2
|
+
|
3
|
+
module Metasploit
|
4
|
+
module Aggregator
|
5
|
+
module Tlv
|
6
|
+
|
7
|
+
#
|
8
|
+
# Constants
|
9
|
+
#
|
10
|
+
PACKET_TYPE_REQUEST = 0
|
11
|
+
PACKET_TYPE_RESPONSE = 1
|
12
|
+
PACKET_TYPE_PLAIN_REQUEST = 10
|
13
|
+
PACKET_TYPE_PLAIN_RESPONSE = 11
|
14
|
+
|
15
|
+
#
|
16
|
+
# TLV Meta Types
|
17
|
+
#
|
18
|
+
TLV_META_TYPE_NONE = 0
|
19
|
+
TLV_META_TYPE_STRING = (1 << 16)
|
20
|
+
TLV_META_TYPE_UINT = (1 << 17)
|
21
|
+
TLV_META_TYPE_RAW = (1 << 18)
|
22
|
+
TLV_META_TYPE_BOOL = (1 << 19)
|
23
|
+
TLV_META_TYPE_QWORD = (1 << 20)
|
24
|
+
TLV_META_TYPE_COMPRESSED = (1 << 29)
|
25
|
+
TLV_META_TYPE_GROUP = (1 << 30)
|
26
|
+
TLV_META_TYPE_COMPLEX = (1 << 31)
|
27
|
+
|
28
|
+
# Exclude compressed from the mask since other meta types (e.g. RAW) can also
|
29
|
+
# be compressed
|
30
|
+
TLV_META_MASK = (
|
31
|
+
TLV_META_TYPE_STRING |
|
32
|
+
TLV_META_TYPE_UINT |
|
33
|
+
TLV_META_TYPE_RAW |
|
34
|
+
TLV_META_TYPE_BOOL |
|
35
|
+
TLV_META_TYPE_QWORD |
|
36
|
+
TLV_META_TYPE_GROUP |
|
37
|
+
TLV_META_TYPE_COMPLEX
|
38
|
+
)
|
39
|
+
|
40
|
+
#
|
41
|
+
# TLV base starting points
|
42
|
+
#
|
43
|
+
TLV_RESERVED = 0
|
44
|
+
TLV_EXTENSIONS = 20000
|
45
|
+
TLV_USER = 40000
|
46
|
+
TLV_TEMP = 60000
|
47
|
+
|
48
|
+
#
|
49
|
+
# TLV Specific Types
|
50
|
+
#
|
51
|
+
TLV_TYPE_ANY = TLV_META_TYPE_NONE | 0
|
52
|
+
TLV_TYPE_METHOD = TLV_META_TYPE_STRING | 1
|
53
|
+
TLV_TYPE_REQUEST_ID = TLV_META_TYPE_STRING | 2
|
54
|
+
TLV_TYPE_EXCEPTION = TLV_META_TYPE_GROUP | 3
|
55
|
+
TLV_TYPE_RESULT = TLV_META_TYPE_UINT | 4
|
56
|
+
|
57
|
+
|
58
|
+
TLV_TYPE_STRING = TLV_META_TYPE_STRING | 10
|
59
|
+
TLV_TYPE_UINT = TLV_META_TYPE_UINT | 11
|
60
|
+
TLV_TYPE_BOOL = TLV_META_TYPE_BOOL | 12
|
61
|
+
|
62
|
+
TLV_TYPE_LENGTH = TLV_META_TYPE_UINT | 25
|
63
|
+
TLV_TYPE_DATA = TLV_META_TYPE_RAW | 26
|
64
|
+
TLV_TYPE_FLAGS = TLV_META_TYPE_UINT | 27
|
65
|
+
|
66
|
+
TLV_TYPE_CHANNEL_ID = TLV_META_TYPE_UINT | 50
|
67
|
+
TLV_TYPE_CHANNEL_TYPE = TLV_META_TYPE_STRING | 51
|
68
|
+
TLV_TYPE_CHANNEL_DATA = TLV_META_TYPE_RAW | 52
|
69
|
+
TLV_TYPE_CHANNEL_DATA_GROUP = TLV_META_TYPE_GROUP | 53
|
70
|
+
TLV_TYPE_CHANNEL_CLASS = TLV_META_TYPE_UINT | 54
|
71
|
+
TLV_TYPE_CHANNEL_PARENTID = TLV_META_TYPE_UINT | 55
|
72
|
+
|
73
|
+
TLV_TYPE_SEEK_WHENCE = TLV_META_TYPE_UINT | 70
|
74
|
+
TLV_TYPE_SEEK_OFFSET = TLV_META_TYPE_UINT | 71
|
75
|
+
TLV_TYPE_SEEK_POS = TLV_META_TYPE_UINT | 72
|
76
|
+
|
77
|
+
TLV_TYPE_EXCEPTION_CODE = TLV_META_TYPE_UINT | 300
|
78
|
+
TLV_TYPE_EXCEPTION_STRING = TLV_META_TYPE_STRING | 301
|
79
|
+
|
80
|
+
TLV_TYPE_LIBRARY_PATH = TLV_META_TYPE_STRING | 400
|
81
|
+
TLV_TYPE_TARGET_PATH = TLV_META_TYPE_STRING | 401
|
82
|
+
TLV_TYPE_MIGRATE_PID = TLV_META_TYPE_UINT | 402
|
83
|
+
TLV_TYPE_MIGRATE_LEN = TLV_META_TYPE_UINT | 403
|
84
|
+
TLV_TYPE_MIGRATE_PAYLOAD = TLV_META_TYPE_STRING | 404
|
85
|
+
TLV_TYPE_MIGRATE_ARCH = TLV_META_TYPE_UINT | 405
|
86
|
+
TLV_TYPE_MIGRATE_BASE_ADDR = TLV_META_TYPE_UINT | 407
|
87
|
+
TLV_TYPE_MIGRATE_ENTRY_POINT = TLV_META_TYPE_UINT | 408
|
88
|
+
TLV_TYPE_MIGRATE_SOCKET_PATH = TLV_META_TYPE_STRING | 409
|
89
|
+
TLV_TYPE_MIGRATE_STUB_LEN = TLV_META_TYPE_UINT | 410
|
90
|
+
TLV_TYPE_MIGRATE_STUB = TLV_META_TYPE_STRING | 411
|
91
|
+
|
92
|
+
|
93
|
+
TLV_TYPE_TRANS_TYPE = TLV_META_TYPE_UINT | 430
|
94
|
+
TLV_TYPE_TRANS_URL = TLV_META_TYPE_STRING | 431
|
95
|
+
TLV_TYPE_TRANS_UA = TLV_META_TYPE_STRING | 432
|
96
|
+
TLV_TYPE_TRANS_COMM_TIMEOUT = TLV_META_TYPE_UINT | 433
|
97
|
+
TLV_TYPE_TRANS_SESSION_EXP = TLV_META_TYPE_UINT | 434
|
98
|
+
TLV_TYPE_TRANS_CERT_HASH = TLV_META_TYPE_RAW | 435
|
99
|
+
TLV_TYPE_TRANS_PROXY_HOST = TLV_META_TYPE_STRING | 436
|
100
|
+
TLV_TYPE_TRANS_PROXY_USER = TLV_META_TYPE_STRING | 437
|
101
|
+
TLV_TYPE_TRANS_PROXY_PASS = TLV_META_TYPE_STRING | 438
|
102
|
+
TLV_TYPE_TRANS_RETRY_TOTAL = TLV_META_TYPE_UINT | 439
|
103
|
+
TLV_TYPE_TRANS_RETRY_WAIT = TLV_META_TYPE_UINT | 440
|
104
|
+
TLV_TYPE_TRANS_GROUP = TLV_META_TYPE_GROUP | 441
|
105
|
+
|
106
|
+
TLV_TYPE_MACHINE_ID = TLV_META_TYPE_STRING | 460
|
107
|
+
TLV_TYPE_UUID = TLV_META_TYPE_RAW | 461
|
108
|
+
|
109
|
+
TLV_TYPE_CIPHER_NAME = TLV_META_TYPE_STRING | 500
|
110
|
+
TLV_TYPE_CIPHER_PARAMETERS = TLV_META_TYPE_GROUP | 501
|
111
|
+
|
112
|
+
#
|
113
|
+
# Core flags
|
114
|
+
#
|
115
|
+
LOAD_LIBRARY_FLAG_ON_DISK = (1 << 0)
|
116
|
+
LOAD_LIBRARY_FLAG_EXTENSION = (1 << 1)
|
117
|
+
LOAD_LIBRARY_FLAG_LOCAL = (1 << 2)
|
118
|
+
|
119
|
+
#
|
120
|
+
# Stdapi TLVs - Config
|
121
|
+
#
|
122
|
+
TLV_TYPE_COMPUTER_NAME = TLV_META_TYPE_STRING | 1040
|
123
|
+
TLV_TYPE_OS_NAME = TLV_META_TYPE_STRING | 1041
|
124
|
+
TLV_TYPE_USER_NAME = TLV_META_TYPE_STRING | 1042
|
125
|
+
TLV_TYPE_ARCHITECTURE = TLV_META_TYPE_STRING | 1043
|
126
|
+
TLV_TYPE_LANG_SYSTEM = TLV_META_TYPE_STRING | 1044
|
127
|
+
TLV_TYPE_SID = TLV_META_TYPE_STRING | 1045
|
128
|
+
TLV_TYPE_DOMAIN = TLV_META_TYPE_STRING | 1046
|
129
|
+
TLV_TYPE_LOGGED_ON_USER_COUNT = TLV_META_TYPE_UINT | 1047
|
130
|
+
TLV_TYPE_LOCAL_DATETIME = TLV_META_TYPE_STRING | 1048
|
131
|
+
|
132
|
+
###
|
133
|
+
#
|
134
|
+
# Base TLV (Type-Length-Value) class
|
135
|
+
#
|
136
|
+
###
|
137
|
+
class Tlv
|
138
|
+
attr_accessor :type, :value, :compress
|
139
|
+
|
140
|
+
##
|
141
|
+
#
|
142
|
+
# Constructor
|
143
|
+
#
|
144
|
+
##
|
145
|
+
|
146
|
+
#
|
147
|
+
# Returns an instance of a TLV.
|
148
|
+
#
|
149
|
+
def initialize(type, value = nil, compress=false)
|
150
|
+
@type = type
|
151
|
+
@compress = compress
|
152
|
+
|
153
|
+
if (value != nil)
|
154
|
+
if (type & TLV_META_TYPE_STRING == TLV_META_TYPE_STRING)
|
155
|
+
if (value.kind_of?(Integer))
|
156
|
+
@value = value.to_s
|
157
|
+
else
|
158
|
+
@value = value.dup
|
159
|
+
end
|
160
|
+
else
|
161
|
+
@value = value
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
def inspect
|
167
|
+
utype = type ^ TLV_META_TYPE_COMPRESSED
|
168
|
+
group = false
|
169
|
+
meta = case (utype & TLV_META_MASK)
|
170
|
+
when TLV_META_TYPE_STRING; "STRING"
|
171
|
+
when TLV_META_TYPE_UINT; "INT"
|
172
|
+
when TLV_META_TYPE_RAW; "RAW"
|
173
|
+
when TLV_META_TYPE_BOOL; "BOOL"
|
174
|
+
when TLV_META_TYPE_QWORD; "QWORD"
|
175
|
+
when TLV_META_TYPE_GROUP; group=true; "GROUP"
|
176
|
+
when TLV_META_TYPE_COMPLEX; "COMPLEX"
|
177
|
+
else; 'unknown-meta-type'
|
178
|
+
end
|
179
|
+
stype = "unknown-#{type}"
|
180
|
+
Metasploit::Aggregator::Tlv.constants.each do |value|
|
181
|
+
next unless Metasploit::Aggregator::Tlv.const_get(value) == type
|
182
|
+
stype = value.to_s
|
183
|
+
break
|
184
|
+
end
|
185
|
+
val = value.inspect
|
186
|
+
if val.length > 50
|
187
|
+
val = val[0,50] + ' ..."'
|
188
|
+
end
|
189
|
+
group ||= (self.class.to_s =~ /Packet/)
|
190
|
+
if group
|
191
|
+
tlvs_inspect = "tlvs=[\n"
|
192
|
+
@tlvs.each { |t|
|
193
|
+
tlvs_inspect << " #{t.inspect}\n"
|
194
|
+
}
|
195
|
+
tlvs_inspect << "]"
|
196
|
+
else
|
197
|
+
tlvs_inspect = "meta=#{meta.ljust 10} value=#{val}"
|
198
|
+
end
|
199
|
+
"#<#{self.class} type=#{stype.ljust 15} #{tlvs_inspect}>"
|
200
|
+
end
|
201
|
+
|
202
|
+
##
|
203
|
+
#
|
204
|
+
# Conditionals
|
205
|
+
#
|
206
|
+
##
|
207
|
+
|
208
|
+
#
|
209
|
+
# Checks to see if a TLVs meta type is equivalent to the meta type passed.
|
210
|
+
#
|
211
|
+
def meta_type?(meta)
|
212
|
+
return (self.type & meta == meta)
|
213
|
+
end
|
214
|
+
|
215
|
+
#
|
216
|
+
# Checks to see if the TLVs type is equivalent to the type passed.
|
217
|
+
#
|
218
|
+
def type?(type)
|
219
|
+
return self.type == type
|
220
|
+
end
|
221
|
+
|
222
|
+
#
|
223
|
+
# Checks to see if the TLVs value is equivalent to the value passed.
|
224
|
+
#
|
225
|
+
def value?(value)
|
226
|
+
return self.value == value
|
227
|
+
end
|
228
|
+
|
229
|
+
##
|
230
|
+
#
|
231
|
+
# Serializers
|
232
|
+
#
|
233
|
+
##
|
234
|
+
|
235
|
+
#
|
236
|
+
# Converts the TLV to raw.
|
237
|
+
#
|
238
|
+
def to_r
|
239
|
+
# Forcibly convert to ASCII-8BIT encoding
|
240
|
+
raw = value.to_s.unpack("C*").pack("C*")
|
241
|
+
|
242
|
+
if (self.type & TLV_META_TYPE_STRING == TLV_META_TYPE_STRING)
|
243
|
+
raw += "\x00"
|
244
|
+
elsif (self.type & TLV_META_TYPE_UINT == TLV_META_TYPE_UINT)
|
245
|
+
raw = [value].pack("N")
|
246
|
+
elsif (self.type & TLV_META_TYPE_QWORD == TLV_META_TYPE_QWORD)
|
247
|
+
raw = [ self.htonq( value.to_i ) ].pack("Q<")
|
248
|
+
elsif (self.type & TLV_META_TYPE_BOOL == TLV_META_TYPE_BOOL)
|
249
|
+
if (value == true)
|
250
|
+
raw = [1].pack("c")
|
251
|
+
else
|
252
|
+
raw = [0].pack("c")
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
# check if the tlv is to be compressed...
|
257
|
+
if( @compress )
|
258
|
+
raw_uncompressed = raw
|
259
|
+
# compress the raw data
|
260
|
+
raw_compressed = Rex::Text.zlib_deflate( raw_uncompressed )
|
261
|
+
# check we have actually made the raw data smaller...
|
262
|
+
# (small blobs often compress slightly larger then the origional)
|
263
|
+
# if the compressed data is not smaller, we dont use the compressed data
|
264
|
+
if( raw_compressed.length < raw_uncompressed.length )
|
265
|
+
# if so, set the TLV's type to indicate compression is used
|
266
|
+
self.type = self.type | TLV_META_TYPE_COMPRESSED
|
267
|
+
# update the raw data with the uncompressed data length + compressed data
|
268
|
+
# (we include the uncompressed data length as the C side will need to know this for decompression)
|
269
|
+
raw = [ raw_uncompressed.length ].pack("N") + raw_compressed
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
273
|
+
return [raw.length + 8, self.type].pack("NN") + raw
|
274
|
+
end
|
275
|
+
|
276
|
+
#
|
277
|
+
# Translates the raw format of the TLV into a sanitize version.
|
278
|
+
#
|
279
|
+
def from_r(raw)
|
280
|
+
self.value = nil
|
281
|
+
|
282
|
+
length, self.type = raw.unpack("NN");
|
283
|
+
|
284
|
+
# check if the tlv value has been compressed...
|
285
|
+
if( self.type & TLV_META_TYPE_COMPRESSED == TLV_META_TYPE_COMPRESSED )
|
286
|
+
# set this TLV as using compression
|
287
|
+
@compress = true
|
288
|
+
# remove the TLV_META_TYPE_COMPRESSED flag from the tlv type to restore the
|
289
|
+
# tlv type to its origional, allowing for transparent data compression.
|
290
|
+
self.type = self.type ^ TLV_META_TYPE_COMPRESSED
|
291
|
+
# decompress the compressed data (skipping the length and type DWORD's)
|
292
|
+
raw_decompressed = Rex::Text.zlib_inflate( raw[8..length-1] )
|
293
|
+
# update the length to reflect the decompressed data length (+8 for the length and type DWORD's)
|
294
|
+
length = raw_decompressed.length + 8
|
295
|
+
# update the raw buffer with the new length, decompressed data and updated type.
|
296
|
+
raw = [length, self.type].pack("NN") + raw_decompressed
|
297
|
+
end
|
298
|
+
|
299
|
+
if (self.type & TLV_META_TYPE_STRING == TLV_META_TYPE_STRING)
|
300
|
+
if (raw.length > 0)
|
301
|
+
self.value = raw[8..length-2]
|
302
|
+
else
|
303
|
+
self.value = nil
|
304
|
+
end
|
305
|
+
elsif (self.type & TLV_META_TYPE_UINT == TLV_META_TYPE_UINT)
|
306
|
+
self.value = raw.unpack("NNN")[2]
|
307
|
+
elsif (self.type & TLV_META_TYPE_QWORD == TLV_META_TYPE_QWORD)
|
308
|
+
self.value = raw.unpack("NNQ<")[2]
|
309
|
+
self.value = self.ntohq( self.value )
|
310
|
+
elsif (self.type & TLV_META_TYPE_BOOL == TLV_META_TYPE_BOOL)
|
311
|
+
self.value = raw.unpack("NNc")[2]
|
312
|
+
|
313
|
+
if (self.value == 1)
|
314
|
+
self.value = true
|
315
|
+
else
|
316
|
+
self.value = false
|
317
|
+
end
|
318
|
+
else
|
319
|
+
self.value = raw[8..length-1]
|
320
|
+
end
|
321
|
+
|
322
|
+
return length;
|
323
|
+
end
|
324
|
+
|
325
|
+
protected
|
326
|
+
|
327
|
+
def htonq( value )
|
328
|
+
if( [1].pack( 's' ) == [1].pack( 'n' ) )
|
329
|
+
return value
|
330
|
+
end
|
331
|
+
return [ value ].pack( 'Q<' ).reverse.unpack( 'Q<' ).first
|
332
|
+
end
|
333
|
+
|
334
|
+
def ntohq( value )
|
335
|
+
return htonq( value )
|
336
|
+
end
|
337
|
+
|
338
|
+
end
|
339
|
+
|
340
|
+
###
|
341
|
+
#
|
342
|
+
# Group TLVs contain zero or more TLVs
|
343
|
+
#
|
344
|
+
###
|
345
|
+
class GroupTlv < Tlv
|
346
|
+
attr_accessor :tlvs
|
347
|
+
|
348
|
+
##
|
349
|
+
#
|
350
|
+
# Constructor
|
351
|
+
#
|
352
|
+
##
|
353
|
+
|
354
|
+
#
|
355
|
+
# Initializes the group TLV container to the supplied type
|
356
|
+
# and creates an empty TLV array.
|
357
|
+
#
|
358
|
+
def initialize(type)
|
359
|
+
super(type)
|
360
|
+
|
361
|
+
self.tlvs = [ ]
|
362
|
+
end
|
363
|
+
|
364
|
+
##
|
365
|
+
#
|
366
|
+
# Group-based TLV accessors
|
367
|
+
#
|
368
|
+
##
|
369
|
+
|
370
|
+
#
|
371
|
+
# Enumerates TLVs of the supplied type.
|
372
|
+
#
|
373
|
+
def each(type = TLV_TYPE_ANY, &block)
|
374
|
+
get_tlvs(type).each(&block)
|
375
|
+
end
|
376
|
+
|
377
|
+
#
|
378
|
+
# Synonym for each.
|
379
|
+
#
|
380
|
+
def each_tlv(type = TLV_TYPE_ANY, &block)
|
381
|
+
each(type, &block)
|
382
|
+
end
|
383
|
+
|
384
|
+
#
|
385
|
+
# Enumerates TLVs of a supplied type with indexes.
|
386
|
+
#
|
387
|
+
def each_with_index(type = TLV_TYPE_ANY, &block)
|
388
|
+
get_tlvs(type).each_with_index(&block)
|
389
|
+
end
|
390
|
+
|
391
|
+
#
|
392
|
+
# Synonym for each_with_index.
|
393
|
+
#
|
394
|
+
def each_tlv_with_index(type = TLV_TYPE_ANY, &block)
|
395
|
+
each_with_index(type, block)
|
396
|
+
end
|
397
|
+
|
398
|
+
#
|
399
|
+
# Returns an array of TLVs for the given type.
|
400
|
+
#
|
401
|
+
def get_tlvs(type)
|
402
|
+
if (type == TLV_TYPE_ANY)
|
403
|
+
return self.tlvs
|
404
|
+
else
|
405
|
+
type_tlvs = []
|
406
|
+
|
407
|
+
self.tlvs.each() { |tlv|
|
408
|
+
if (tlv.type?(type))
|
409
|
+
type_tlvs << tlv
|
410
|
+
end
|
411
|
+
}
|
412
|
+
|
413
|
+
return type_tlvs
|
414
|
+
end
|
415
|
+
end
|
416
|
+
|
417
|
+
##
|
418
|
+
#
|
419
|
+
# TLV management
|
420
|
+
#
|
421
|
+
##
|
422
|
+
|
423
|
+
#
|
424
|
+
# Adds a TLV of a given type and value.
|
425
|
+
#
|
426
|
+
def add_tlv(type, value = nil, replace = false, compress=false)
|
427
|
+
|
428
|
+
# If we should replace any TLVs with the same type...remove them first
|
429
|
+
if (replace)
|
430
|
+
each(type) { |tlv|
|
431
|
+
if (tlv.type == type)
|
432
|
+
self.tlvs.delete(tlv)
|
433
|
+
end
|
434
|
+
}
|
435
|
+
end
|
436
|
+
|
437
|
+
if (type & TLV_META_TYPE_GROUP == TLV_META_TYPE_GROUP)
|
438
|
+
tlv = GroupTlv.new(type)
|
439
|
+
else
|
440
|
+
tlv = Tlv.new(type, value, compress)
|
441
|
+
end
|
442
|
+
|
443
|
+
self.tlvs << tlv
|
444
|
+
|
445
|
+
return tlv
|
446
|
+
end
|
447
|
+
|
448
|
+
#
|
449
|
+
# Adds zero or more TLVs to the packet.
|
450
|
+
#
|
451
|
+
def add_tlvs(tlvs)
|
452
|
+
if (tlvs != nil)
|
453
|
+
tlvs.each { |tlv|
|
454
|
+
add_tlv(tlv['type'], tlv['value'])
|
455
|
+
}
|
456
|
+
end
|
457
|
+
end
|
458
|
+
|
459
|
+
#
|
460
|
+
# Gets the first TLV of a given type.
|
461
|
+
#
|
462
|
+
def get_tlv(type, index = 0)
|
463
|
+
type_tlvs = get_tlvs(type)
|
464
|
+
|
465
|
+
if (type_tlvs.length > index)
|
466
|
+
return type_tlvs[index]
|
467
|
+
end
|
468
|
+
|
469
|
+
return nil
|
470
|
+
end
|
471
|
+
|
472
|
+
#
|
473
|
+
# Returns the value of a TLV if it exists, otherwise nil.
|
474
|
+
#
|
475
|
+
def get_tlv_value(type, index = 0)
|
476
|
+
tlv = get_tlv(type, index)
|
477
|
+
|
478
|
+
return (tlv != nil) ? tlv.value : nil
|
479
|
+
end
|
480
|
+
|
481
|
+
#
|
482
|
+
# Returns an array of values for all tlvs of type type.
|
483
|
+
#
|
484
|
+
def get_tlv_values(type)
|
485
|
+
get_tlvs(type).collect { |a| a.value }
|
486
|
+
end
|
487
|
+
|
488
|
+
#
|
489
|
+
# Checks to see if the container has a TLV of a given type.
|
490
|
+
#
|
491
|
+
def has_tlv?(type)
|
492
|
+
return get_tlv(type) != nil
|
493
|
+
end
|
494
|
+
|
495
|
+
#
|
496
|
+
# Zeros out the array of TLVs.
|
497
|
+
#
|
498
|
+
def reset
|
499
|
+
self.tlvs = []
|
500
|
+
end
|
501
|
+
|
502
|
+
##
|
503
|
+
#
|
504
|
+
# Serializers
|
505
|
+
#
|
506
|
+
##
|
507
|
+
|
508
|
+
#
|
509
|
+
# Converts all of the TLVs in the TLV array to raw and prefixes them
|
510
|
+
# with a container TLV of this instance's TLV type.
|
511
|
+
#
|
512
|
+
def to_r
|
513
|
+
raw = ''
|
514
|
+
|
515
|
+
self.each() { |tlv|
|
516
|
+
raw << tlv.to_r
|
517
|
+
}
|
518
|
+
|
519
|
+
return [raw.length + 8, self.type].pack("NN") + raw
|
520
|
+
end
|
521
|
+
|
522
|
+
#
|
523
|
+
# Converts the TLV group container from raw to all of the individual
|
524
|
+
# TLVs.
|
525
|
+
#
|
526
|
+
def from_r(raw)
|
527
|
+
offset = 8
|
528
|
+
|
529
|
+
# Reset the TLVs array
|
530
|
+
self.tlvs = []
|
531
|
+
self.type = raw.unpack("NN")[1]
|
532
|
+
|
533
|
+
# Enumerate all of the TLVs
|
534
|
+
while (offset < raw.length-1)
|
535
|
+
|
536
|
+
tlv = nil
|
537
|
+
|
538
|
+
# Get the length and type
|
539
|
+
length, type = raw[offset..offset+8].unpack("NN")
|
540
|
+
|
541
|
+
if (type & TLV_META_TYPE_GROUP == TLV_META_TYPE_GROUP)
|
542
|
+
tlv = GroupTlv.new(type)
|
543
|
+
else
|
544
|
+
tlv = Tlv.new(type)
|
545
|
+
end
|
546
|
+
|
547
|
+
tlv.from_r(raw[offset..offset+length])
|
548
|
+
|
549
|
+
# Insert it into the list of TLVs
|
550
|
+
tlvs << tlv
|
551
|
+
|
552
|
+
# Move up
|
553
|
+
offset += length
|
554
|
+
end
|
555
|
+
end
|
556
|
+
|
557
|
+
end
|
558
|
+
|
559
|
+
###
|
560
|
+
#
|
561
|
+
# The logical meterpreter packet class
|
562
|
+
#
|
563
|
+
###
|
564
|
+
class Packet < GroupTlv
|
565
|
+
attr_accessor :created_at
|
566
|
+
|
567
|
+
##
|
568
|
+
#
|
569
|
+
# Factory
|
570
|
+
#
|
571
|
+
##
|
572
|
+
|
573
|
+
#
|
574
|
+
# Creates a request with the supplied method.
|
575
|
+
#
|
576
|
+
def Packet.create_request(method = nil)
|
577
|
+
return Packet.new(PACKET_TYPE_REQUEST, method)
|
578
|
+
end
|
579
|
+
|
580
|
+
#
|
581
|
+
# Creates a response to a request if one is provided.
|
582
|
+
#
|
583
|
+
def Packet.create_response(request = nil)
|
584
|
+
response_type = PACKET_TYPE_RESPONSE
|
585
|
+
method = nil
|
586
|
+
|
587
|
+
if (request)
|
588
|
+
if (request.type?(PACKET_TYPE_PLAIN_REQUEST))
|
589
|
+
response_type = PACKET_TYPE_PLAIN_RESPONSE
|
590
|
+
end
|
591
|
+
|
592
|
+
method = request.method
|
593
|
+
end
|
594
|
+
|
595
|
+
return Packet.new(response_type, method)
|
596
|
+
end
|
597
|
+
|
598
|
+
##
|
599
|
+
#
|
600
|
+
# Constructor
|
601
|
+
#
|
602
|
+
##
|
603
|
+
|
604
|
+
#
|
605
|
+
# Initializes the packet to the supplied packet type and method,
|
606
|
+
# if any. If the packet is a request, a request identifier is
|
607
|
+
# created.
|
608
|
+
#
|
609
|
+
def initialize(type = nil, method = nil)
|
610
|
+
super(type)
|
611
|
+
|
612
|
+
if (method)
|
613
|
+
self.method = method
|
614
|
+
end
|
615
|
+
|
616
|
+
self.created_at = ::Time.now
|
617
|
+
|
618
|
+
# If it's a request, generate a random request identifier
|
619
|
+
if ((type == PACKET_TYPE_REQUEST) ||
|
620
|
+
(type == PACKET_TYPE_PLAIN_REQUEST))
|
621
|
+
rid = ''
|
622
|
+
|
623
|
+
32.times { |val| rid << rand(10).to_s }
|
624
|
+
|
625
|
+
add_tlv(TLV_TYPE_REQUEST_ID, rid)
|
626
|
+
end
|
627
|
+
end
|
628
|
+
|
629
|
+
#
|
630
|
+
# Override the function that creates the raw byte stream for
|
631
|
+
# sending so that it generates an XOR key, uses it to scramble
|
632
|
+
# the serialized TLV content, and then returns the key plus the
|
633
|
+
# scrambled data as the payload.
|
634
|
+
#
|
635
|
+
def to_r
|
636
|
+
raw = super
|
637
|
+
xor_key = rand(254) + 1
|
638
|
+
xor_key |= (rand(254) + 1) << 8
|
639
|
+
xor_key |= (rand(254) + 1) << 16
|
640
|
+
xor_key |= (rand(254) + 1) << 24
|
641
|
+
result = [xor_key].pack('N') + xor_bytes(xor_key, raw)
|
642
|
+
result
|
643
|
+
end
|
644
|
+
|
645
|
+
#
|
646
|
+
# Override the function that reads from a raw byte stream so
|
647
|
+
# that the XORing of data is included in the process prior to
|
648
|
+
# passing it on to the default functionality that can parse
|
649
|
+
# the TLV values.
|
650
|
+
#
|
651
|
+
def from_r(bytes)
|
652
|
+
xor_key = bytes[0,4].unpack('N')[0]
|
653
|
+
super(xor_bytes(xor_key, bytes[4, bytes.length]))
|
654
|
+
end
|
655
|
+
|
656
|
+
#
|
657
|
+
# Xor a set of bytes with a given DWORD xor key.
|
658
|
+
#
|
659
|
+
def xor_bytes(xor_key, bytes)
|
660
|
+
result = ''
|
661
|
+
bytes.bytes.zip([xor_key].pack('V').bytes.cycle).each do |b|
|
662
|
+
result << (b[0].ord ^ b[1].ord).chr
|
663
|
+
end
|
664
|
+
result
|
665
|
+
end
|
666
|
+
|
667
|
+
##
|
668
|
+
#
|
669
|
+
# Conditionals
|
670
|
+
#
|
671
|
+
##
|
672
|
+
|
673
|
+
#
|
674
|
+
# Checks to see if the packet is a response.
|
675
|
+
#
|
676
|
+
def response?
|
677
|
+
return ((self.type == PACKET_TYPE_RESPONSE) ||
|
678
|
+
(self.type == PACKET_TYPE_PLAIN_RESPONSE))
|
679
|
+
end
|
680
|
+
|
681
|
+
##
|
682
|
+
#
|
683
|
+
# Accessors
|
684
|
+
#
|
685
|
+
##
|
686
|
+
|
687
|
+
#
|
688
|
+
# Checks to see if the packet's method is equal to the supplied method.
|
689
|
+
#
|
690
|
+
def method?(method)
|
691
|
+
return (get_tlv_value(TLV_TYPE_METHOD) == method)
|
692
|
+
end
|
693
|
+
|
694
|
+
#
|
695
|
+
# Sets the packet's method TLV to the method supplied.
|
696
|
+
#
|
697
|
+
def method=(method)
|
698
|
+
add_tlv(TLV_TYPE_METHOD, method, true)
|
699
|
+
end
|
700
|
+
|
701
|
+
#
|
702
|
+
# Returns the value of the packet's method TLV.
|
703
|
+
#
|
704
|
+
def method
|
705
|
+
return get_tlv_value(TLV_TYPE_METHOD)
|
706
|
+
end
|
707
|
+
|
708
|
+
#
|
709
|
+
# Checks to see if the packet's result value is equal to the supplied
|
710
|
+
# result.
|
711
|
+
#
|
712
|
+
def result?(result)
|
713
|
+
return (get_tlv_value(TLV_TYPE_RESULT) == result)
|
714
|
+
end
|
715
|
+
|
716
|
+
#
|
717
|
+
# Sets the packet's result TLV.
|
718
|
+
#
|
719
|
+
def result=(result)
|
720
|
+
add_tlv(TLV_TYPE_RESULT, result, true)
|
721
|
+
end
|
722
|
+
|
723
|
+
#
|
724
|
+
# Gets the value of the packet's result TLV.
|
725
|
+
#
|
726
|
+
def result
|
727
|
+
return get_tlv_value(TLV_TYPE_RESULT)
|
728
|
+
end
|
729
|
+
|
730
|
+
#
|
731
|
+
# Gets the value of the packet's request identifier TLV.
|
732
|
+
#
|
733
|
+
def rid
|
734
|
+
return get_tlv_value(TLV_TYPE_REQUEST_ID)
|
735
|
+
end
|
736
|
+
end
|
737
|
+
|
738
|
+
|
739
|
+
end; end; end
|
740
|
+
|