CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/lib/rex/post/meterpreter/command_mapper.rb
Views: 11784
1
# -*- coding: binary -*-
2
3
require 'rex/post/meterpreter/extension_mapper'
4
require 'rex/post/meterpreter/core_ids'
5
require 'rex/post/meterpreter/client_core'
6
7
module Rex
8
module Post
9
module Meterpreter
10
11
class CommandMapper
12
@@cached_tlv_types = {}
13
14
# Get the numeric command ID for the specified command name.
15
#
16
# @param [String] name The name of the command to retrieve the ID for. This
17
# parameter is case insensitive.
18
# @return [Integer, nil] The command ID or nil if the name does not exist.
19
def self.get_command_id(name)
20
name = name.downcase
21
22
return nil unless name.include?('_')
23
24
mod_name, cmd_name = name.split('_', 2)
25
if mod_name == 'core'
26
mod = Rex::Post::Meterpreter
27
else
28
mod = Rex::Post::Meterpreter::ExtensionMapper.get_extension_module(mod_name)
29
end
30
31
return nil unless mod
32
33
const_name = "COMMAND_ID_#{mod_name.upcase}_#{cmd_name.upcase}"
34
return nil unless mod.const_defined?(const_name)
35
36
mod.const_get(const_name)
37
end
38
39
# Get the string command name for the specified command ID.
40
#
41
# @param [Integer] id The ID of the command to retrieve the name for.
42
# @return [String, nil] The command name or nil if the ID does not exist.
43
def self.get_command_name(id)
44
extension_id = id - (id % COMMAND_ID_RANGE)
45
if extension_id == Rex::Post::Meterpreter::ClientCore.extension_id # this is the meterpreter core which is not exactly an extension.
46
mod = Rex::Post::Meterpreter
47
else
48
mod_name = Rex::Post::Meterpreter::ExtensionMapper.get_extension_name(extension_id)
49
mod = Rex::Post::Meterpreter::ExtensionMapper.get_extension_module(mod_name)
50
end
51
52
return nil unless mod
53
54
command_name = mod.constants.select { |c| c.to_s.start_with?('COMMAND_ID_') }.find { |c| id == mod.const_get(c) }
55
56
return nil unless command_name
57
58
command_name.to_s.delete_prefix('COMMAND_ID_').downcase
59
end
60
61
# Get all of the string command names for the specified extensions.
62
#
63
# @param [Array<String>] extensions The names of the extensions to retrieve
64
# all of the command names for. The extension names are case insensitive. If
65
# no extensions are specified, all extensions will be enumerated.
66
# @return [Array<String>] An array of all of the enumerated command names.
67
def self.get_command_names(*extensions)
68
self.get_commands(*extensions).keys
69
end
70
71
# Get a hash of all command name strings mapped to their numeric IDs.
72
#
73
# @param [Array<String>] extensions The names of the extensions to retrieve
74
# all of the commands for. The extension names are case insensitive. If
75
# no extensions are specified, all extensions will be enumerated.
76
# @return [Hash<String, Integer>] An hash of all of the enumerated commands.
77
def self.get_commands(*extensions)
78
extensions = ['core'] + Rex::Post::Meterpreter::ExtensionMapper.get_extension_names if extensions.empty?
79
80
commands = {}
81
extensions.each do |mod_name|
82
mod_name = mod_name.downcase
83
84
if mod_name == 'core'
85
mod = Rex::Post::Meterpreter
86
else
87
begin
88
mod = Rex::Post::Meterpreter::ExtensionMapper.get_extension_module(mod_name)
89
rescue RuntimeError
90
next
91
end
92
end
93
94
constants = mod.constants.select { |name| name.to_s.start_with?("COMMAND_ID_#{mod_name.upcase}") }
95
commands.merge!(constants.map { |name| [name.to_s.delete_prefix('COMMAND_ID_').downcase, mod.const_get(name)] }.to_h)
96
end
97
98
commands
99
end
100
101
102
# Get the TLV Type symbols that are defined with the value
103
# Potential return values are [], [:TLV_TYPE_A], and [:TLV_TYPE_A, :PACKET_TYPE_B]
104
#
105
# Returning an array is a solution to having multiple TLV types having the same value, as documented here:
106
# https://github.com/rapid7/metasploit-framework/issues/16267
107
#
108
# @param Integer value The value of the TLV type to retrieve the TLV type names for.
109
# @return [Array<Symbol>] An array of symbols of all TLV types that are defined with the value. Can be empty.
110
def self.get_tlv_names(value)
111
return @@cached_tlv_types[value] unless @@cached_tlv_types[value].nil? || @@cached_tlv_types[value].empty?
112
113
# Default to arrays that contain TLV Types, so that we only deal with one data type
114
@@cached_tlv_types = Hash.new { |h, k| h[k] = Set.new }
115
116
available_modules = [
117
::Rex::Post::Meterpreter,
118
*::Rex::Post::Meterpreter::ExtensionMapper.get_extension_klasses,
119
# Railgun is a special case that defines extra TLV_TYPES inside an extension
120
Rex::Post::Meterpreter::Extensions::Stdapi::Railgun
121
].uniq
122
123
available_modules.each do |mod|
124
mod.constants.each do |const|
125
next unless const.to_s.start_with?('TLV_TYPE_') || const.to_s.start_with?('PACKET_')
126
127
@@cached_tlv_types[mod.const_get(const)] << const
128
end
129
end
130
131
@@cached_tlv_types[value]
132
end
133
end
134
135
end
136
end
137
end
138
139