rex 2.0.8 → 2.0.9
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/rex.rb +1 -0
- data/lib/rex/arch.rb +5 -0
- data/lib/rex/arch/x86.rb +19 -5
- data/lib/rex/arch/zarch.rb +17 -0
- data/lib/rex/compat.rb +5 -4
- data/lib/rex/constants.rb +3 -1
- data/lib/rex/encoder/alpha2/alpha_mixed.rb +70 -9
- data/lib/rex/encoder/alpha2/alpha_upper.rb +67 -8
- data/lib/rex/exploitation/cmdstager.rb +1 -0
- data/lib/rex/exploitation/cmdstager/certutil.rb +115 -0
- data/lib/rex/exploitation/cmdstager/echo.rb +6 -3
- data/lib/rex/exploitation/egghunter.rb +1 -1
- data/lib/rex/google/geolocation.rb +68 -0
- data/lib/rex/io/bidirectional_pipe.rb +0 -4
- data/lib/rex/java/serialization.rb +2 -0
- data/lib/rex/java/serialization/decode_error.rb +11 -0
- data/lib/rex/java/serialization/encode_error.rb +11 -0
- data/lib/rex/java/serialization/model.rb +2 -0
- data/lib/rex/java/serialization/model/annotation.rb +3 -3
- data/lib/rex/java/serialization/model/block_data.rb +3 -3
- data/lib/rex/java/serialization/model/block_data_long.rb +3 -3
- data/lib/rex/java/serialization/model/class_desc.rb +6 -6
- data/lib/rex/java/serialization/model/contents.rb +17 -10
- data/lib/rex/java/serialization/model/field.rb +12 -11
- data/lib/rex/java/serialization/model/long_utf.rb +3 -3
- data/lib/rex/java/serialization/model/new_array.rb +22 -23
- data/lib/rex/java/serialization/model/new_class.rb +57 -0
- data/lib/rex/java/serialization/model/new_class_desc.rb +15 -16
- data/lib/rex/java/serialization/model/new_enum.rb +5 -5
- data/lib/rex/java/serialization/model/new_object.rb +22 -17
- data/lib/rex/java/serialization/model/proxy_class_desc.rb +109 -0
- data/lib/rex/java/serialization/model/reference.rb +4 -4
- data/lib/rex/java/serialization/model/stream.rb +7 -7
- data/lib/rex/java/serialization/model/utf.rb +3 -3
- data/lib/rex/json_hash_file.rb +94 -0
- data/lib/rex/logging/log_sink.rb +1 -0
- data/lib/rex/logging/sinks/timestamp_flatfile.rb +21 -0
- data/lib/rex/parser/appscan_nokogiri.rb +13 -23
- data/lib/rex/parser/fs/ntfs.rb +10 -5
- data/lib/rex/parser/nmap_nokogiri.rb +3 -1
- data/lib/rex/parser/openvas_nokogiri.rb +70 -73
- data/lib/rex/parser/winscp.rb +108 -0
- data/lib/rex/parser/x509_certificate.rb +92 -0
- data/lib/rex/payloads.rb +0 -1
- data/lib/rex/payloads/meterpreter/config.rb +154 -0
- data/lib/rex/payloads/meterpreter/uri_checksum.rb +136 -0
- data/lib/rex/post/meterpreter.rb +1 -1
- data/lib/rex/post/meterpreter/client.rb +26 -3
- data/lib/rex/post/meterpreter/client_core.rb +387 -75
- data/lib/rex/post/meterpreter/extensions/android/android.rb +127 -37
- data/lib/rex/post/meterpreter/extensions/android/tlv.rb +46 -25
- data/lib/rex/post/meterpreter/extensions/extapi/extapi.rb +4 -0
- data/lib/rex/post/meterpreter/extensions/extapi/ntds/ntds.rb +39 -0
- data/lib/rex/post/meterpreter/extensions/extapi/pageant/pageant.rb +44 -0
- data/lib/rex/post/meterpreter/extensions/extapi/tlv.rb +9 -0
- data/lib/rex/post/meterpreter/extensions/kiwi/kiwi.rb +16 -1
- data/lib/rex/post/meterpreter/extensions/priv/priv.rb +1 -1
- data/lib/rex/post/meterpreter/extensions/python/python.rb +114 -0
- data/lib/rex/post/meterpreter/extensions/python/tlv.rb +21 -0
- data/lib/rex/post/meterpreter/extensions/stdapi/fs/dir.rb +17 -14
- data/lib/rex/post/meterpreter/extensions/stdapi/fs/file.rb +33 -12
- data/lib/rex/post/meterpreter/extensions/stdapi/fs/mount.rb +57 -0
- data/lib/rex/post/meterpreter/extensions/stdapi/railgun/def/def_kernel32.rb +3 -3
- data/lib/rex/post/meterpreter/extensions/stdapi/stdapi.rb +3 -1
- data/lib/rex/post/meterpreter/extensions/stdapi/sys/config.rb +2 -0
- data/lib/rex/post/meterpreter/extensions/stdapi/sys/process.rb +16 -3
- data/lib/rex/post/meterpreter/extensions/stdapi/sys/registry.rb +29 -6
- data/lib/rex/post/meterpreter/extensions/stdapi/sys/registry_subsystem/registry_key.rb +5 -1
- data/lib/rex/post/meterpreter/extensions/stdapi/tlv.rb +18 -6
- data/lib/rex/post/meterpreter/extensions/stdapi/ui.rb +2 -2
- data/lib/rex/post/meterpreter/extensions/stdapi/webcam/webcam.rb +34 -36
- data/lib/rex/post/meterpreter/packet.rb +29 -0
- data/lib/rex/post/meterpreter/packet_dispatcher.rb +20 -7
- data/lib/rex/post/meterpreter/ui/console.rb +1 -0
- data/lib/rex/post/meterpreter/ui/console/command_dispatcher/android.rb +230 -72
- data/lib/rex/post/meterpreter/ui/console/command_dispatcher/core.rb +544 -34
- data/lib/rex/post/meterpreter/ui/console/command_dispatcher/extapi/adsi.rb +188 -57
- data/lib/rex/post/meterpreter/ui/console/command_dispatcher/kiwi.rb +115 -93
- data/lib/rex/post/meterpreter/ui/console/command_dispatcher/lanattacks/dhcp.rb +1 -1
- data/lib/rex/post/meterpreter/ui/console/command_dispatcher/mimikatz.rb +1 -1
- data/lib/rex/post/meterpreter/ui/console/command_dispatcher/priv/elevate.rb +49 -15
- data/lib/rex/post/meterpreter/ui/console/command_dispatcher/priv/timestomp.rb +11 -2
- data/lib/rex/post/meterpreter/ui/console/command_dispatcher/python.rb +187 -0
- data/lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/fs.rb +324 -133
- data/lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/net.rb +52 -2
- data/lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/sys.rb +68 -65
- data/lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/ui.rb +9 -1
- data/lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/webcam.rb +113 -118
- data/lib/rex/post/meterpreter/ui/console/interactive_channel.rb +3 -0
- data/lib/rex/powershell.rb +62 -0
- data/lib/rex/powershell/command.rb +359 -0
- data/lib/rex/{exploitation/powershell → powershell}/function.rb +0 -2
- data/lib/rex/{exploitation/powershell → powershell}/obfu.rb +0 -2
- data/lib/rex/{exploitation/powershell → powershell}/output.rb +11 -5
- data/lib/rex/{exploitation/powershell → powershell}/param.rb +0 -2
- data/lib/rex/powershell/parser.rb +182 -0
- data/lib/rex/powershell/payload.rb +78 -0
- data/lib/rex/{exploitation/powershell → powershell}/psh_methods.rb +16 -2
- data/lib/rex/{exploitation/powershell → powershell}/script.rb +2 -4
- data/lib/rex/proto/dcerpc/client.rb +6 -6
- data/lib/rex/proto/dcerpc/exceptions.rb +26 -0
- data/lib/rex/proto/http/client.rb +3 -3
- data/lib/rex/proto/http/client_request.rb +0 -5
- data/lib/rex/proto/http/response.rb +86 -0
- data/lib/rex/proto/ipmi/utils.rb +30 -26
- data/lib/rex/proto/kerberos/client.rb +1 -1
- data/lib/rex/proto/kerberos/model/kdc_request.rb +2 -2
- data/lib/rex/proto/rfb/client.rb +8 -3
- data/lib/rex/proto/rfb/constants.rb +1 -1
- data/lib/rex/proto/rmi.rb +2 -0
- data/lib/rex/proto/rmi/decode_error.rb +10 -0
- data/lib/rex/proto/rmi/exception.rb +10 -0
- data/lib/rex/proto/rmi/model.rb +5 -0
- data/lib/rex/proto/rmi/model/call.rb +4 -4
- data/lib/rex/proto/rmi/model/call_data.rb +137 -0
- data/lib/rex/proto/rmi/model/dgc_ack.rb +2 -2
- data/lib/rex/proto/rmi/model/element.rb +26 -11
- data/lib/rex/proto/rmi/model/output_header.rb +4 -4
- data/lib/rex/proto/rmi/model/ping.rb +2 -2
- data/lib/rex/proto/rmi/model/ping_ack.rb +2 -2
- data/lib/rex/proto/rmi/model/protocol_ack.rb +2 -2
- data/lib/rex/proto/rmi/model/return_data.rb +5 -5
- data/lib/rex/proto/rmi/model/return_value.rb +124 -0
- data/lib/rex/proto/rmi/model/unique_identifier.rb +77 -0
- data/lib/rex/proto/steam.rb +3 -0
- data/lib/rex/proto/steam/message.rb +125 -0
- data/lib/rex/proto/tftp/client.rb +35 -14
- data/lib/rex/random_identifier_generator.rb +2 -0
- data/lib/rex/ropbuilder.rb +1 -1
- data/lib/rex/socket/parameters.rb +9 -0
- data/lib/rex/socket/ssl_tcp.rb +25 -41
- data/lib/rex/socket/ssl_tcp_server.rb +10 -21
- data/lib/rex/sslscan/result.rb +20 -1
- data/lib/rex/text.rb +241 -55
- data/lib/rex/ui/output.rb +0 -3
- data/lib/rex/ui/subscriber.rb +0 -10
- data/lib/rex/ui/text/color.rb +9 -0
- data/lib/rex/ui/text/dispatcher_shell.rb +1 -0
- data/lib/rex/ui/text/output.rb +15 -4
- data/lib/rex/ui/text/output/file.rb +1 -0
- data/lib/rex/ui/text/output/stdio.rb +0 -16
- data/lib/rex/ui/text/shell.rb +3 -0
- data/lib/rex/ui/text/table.rb +85 -19
- data/lib/rex/user_agent.rb +118 -0
- data/rex.gemspec +2 -2
- metadata +41 -14
- data/lib/rex/exploitation/powershell.rb +0 -62
- data/lib/rex/exploitation/powershell/parser.rb +0 -183
- data/lib/rex/payloads/meterpreter.rb +0 -2
- data/lib/rex/payloads/meterpreter/patch.rb +0 -136
@@ -177,7 +177,7 @@ class Console::CommandDispatcher::Lanattacks::Dhcp
|
|
177
177
|
|
178
178
|
def print_dhcp_load_options_usage
|
179
179
|
print("dhcp_load_options <datastore> [-h]\n\n" +
|
180
|
-
"Load settings from a
|
180
|
+
"Load settings from a datastore to the active DHCP server.\n\n" +
|
181
181
|
"The datastore must be a hash of name/value pairs.\n" +
|
182
182
|
"Valid names are:\n" +
|
183
183
|
@@dhcp_set_option_valid_options.map {|o| " - #{o}\n" }.join('') +
|
@@ -37,7 +37,7 @@ class Console::CommandDispatcher::Mimikatz
|
|
37
37
|
#
|
38
38
|
def commands
|
39
39
|
{
|
40
|
-
"mimikatz_command" => "Run a custom
|
40
|
+
"mimikatz_command" => "Run a custom command",
|
41
41
|
"wdigest" => "Attempt to retrieve wdigest creds",
|
42
42
|
"msv" => "Attempt to retrieve msv creds (hashes)",
|
43
43
|
"livessp" => "Attempt to retrieve livessp creds",
|
@@ -17,17 +17,20 @@ class Console::CommandDispatcher::Priv::Elevate
|
|
17
17
|
|
18
18
|
include Console::CommandDispatcher
|
19
19
|
|
20
|
-
ELEVATE_TECHNIQUE_NONE
|
21
|
-
ELEVATE_TECHNIQUE_ANY
|
22
|
-
ELEVATE_TECHNIQUE_SERVICE_NAMEDPIPE
|
23
|
-
ELEVATE_TECHNIQUE_SERVICE_NAMEDPIPE2
|
24
|
-
ELEVATE_TECHNIQUE_SERVICE_TOKENDUP
|
25
|
-
|
26
|
-
ELEVATE_TECHNIQUE_DESCRIPTION =
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
20
|
+
ELEVATE_TECHNIQUE_NONE = -1
|
21
|
+
ELEVATE_TECHNIQUE_ANY = 0
|
22
|
+
ELEVATE_TECHNIQUE_SERVICE_NAMEDPIPE = 1
|
23
|
+
ELEVATE_TECHNIQUE_SERVICE_NAMEDPIPE2 = 2
|
24
|
+
ELEVATE_TECHNIQUE_SERVICE_TOKENDUP = 3
|
25
|
+
|
26
|
+
ELEVATE_TECHNIQUE_DESCRIPTION =
|
27
|
+
[
|
28
|
+
"All techniques available",
|
29
|
+
"Named Pipe Impersonation (In Memory/Admin)",
|
30
|
+
"Named Pipe Impersonation (Dropper/Admin)",
|
31
|
+
"Token Duplication (In Memory/Admin)"
|
32
|
+
]
|
33
|
+
|
31
34
|
#
|
32
35
|
# List of supported commands.
|
33
36
|
#
|
@@ -45,6 +48,25 @@ class Console::CommandDispatcher::Priv::Elevate
|
|
45
48
|
end
|
46
49
|
|
47
50
|
|
51
|
+
#
|
52
|
+
# Returns the description of the technique(s)
|
53
|
+
#
|
54
|
+
def translate_technique_index(index)
|
55
|
+
translation = ''
|
56
|
+
|
57
|
+
case index
|
58
|
+
when 0
|
59
|
+
desc = ELEVATE_TECHNIQUE_DESCRIPTION.dup
|
60
|
+
desc.shift
|
61
|
+
translation = desc
|
62
|
+
else
|
63
|
+
translation = [ ELEVATE_TECHNIQUE_DESCRIPTION[index] ]
|
64
|
+
end
|
65
|
+
|
66
|
+
translation
|
67
|
+
end
|
68
|
+
|
69
|
+
|
48
70
|
#
|
49
71
|
# Attempt to elevate the meterpreter to that of local system.
|
50
72
|
#
|
@@ -73,17 +95,29 @@ class Console::CommandDispatcher::Priv::Elevate
|
|
73
95
|
}
|
74
96
|
|
75
97
|
if( technique < 0 or technique >= ELEVATE_TECHNIQUE_DESCRIPTION.length )
|
76
|
-
print_error( "Technique '#{technique}' is out of range." )
|
98
|
+
print_error( "Technique '#{technique}' is out of range." )
|
77
99
|
return false;
|
78
100
|
end
|
79
101
|
|
80
|
-
|
102
|
+
begin
|
103
|
+
result = client.priv.getsystem( technique )
|
104
|
+
rescue Rex::Post::Meterpreter::RequestError => e
|
105
|
+
print_error("#{e.message} The following was attempted:")
|
106
|
+
translate_technique_index(technique).each do |desc|
|
107
|
+
print_error(desc)
|
108
|
+
end
|
109
|
+
elog("#{e.class} #{e.message} (Technique: #{technique})\n#{e.backtrace * "\n"}")
|
110
|
+
return
|
111
|
+
end
|
81
112
|
|
82
113
|
# got system?
|
83
114
|
if result[0]
|
84
|
-
print_line( "...got system
|
115
|
+
print_line( "...got system via technique #{result[1]} (#{translate_technique_index(result[1]).first})." )
|
85
116
|
else
|
86
|
-
print_line( "...failed to get system
|
117
|
+
print_line( "...failed to get system while attempting the following:" )
|
118
|
+
translate_technique_index(technique).each do |desc|
|
119
|
+
print_error(desc)
|
120
|
+
end
|
87
121
|
end
|
88
122
|
|
89
123
|
return result
|
@@ -52,12 +52,21 @@ class Console::CommandDispatcher::Priv::Timestomp
|
|
52
52
|
#
|
53
53
|
def cmd_timestomp(*args)
|
54
54
|
if (args.length < 2)
|
55
|
-
print_line("\nUsage: timestomp file_path
|
55
|
+
print_line("\nUsage: timestomp OPTIONS file_path\n" +
|
56
56
|
@@timestomp_opts.usage)
|
57
57
|
return
|
58
58
|
end
|
59
59
|
|
60
|
-
file_path =
|
60
|
+
file_path = nil
|
61
|
+
args.each { |a| file_path = a unless a[0] == "-" }
|
62
|
+
|
63
|
+
if file_path.nil?
|
64
|
+
print_line("\nNo file_path specified.")
|
65
|
+
return
|
66
|
+
end
|
67
|
+
|
68
|
+
args.delete(file_path)
|
69
|
+
|
61
70
|
modified = nil
|
62
71
|
accessed = nil
|
63
72
|
creation = nil
|
@@ -0,0 +1,187 @@
|
|
1
|
+
# -*- coding: binary -*-
|
2
|
+
require 'rex/post/meterpreter'
|
3
|
+
|
4
|
+
module Rex
|
5
|
+
module Post
|
6
|
+
module Meterpreter
|
7
|
+
module Ui
|
8
|
+
|
9
|
+
###
|
10
|
+
#
|
11
|
+
# Python extension - interact with a python interpreter
|
12
|
+
#
|
13
|
+
###
|
14
|
+
class Console::CommandDispatcher::Python
|
15
|
+
|
16
|
+
Klass = Console::CommandDispatcher::Python
|
17
|
+
|
18
|
+
include Console::CommandDispatcher
|
19
|
+
|
20
|
+
#
|
21
|
+
# Name for this dispatcher
|
22
|
+
#
|
23
|
+
def name
|
24
|
+
'Python'
|
25
|
+
end
|
26
|
+
|
27
|
+
#
|
28
|
+
# List of supported commands.
|
29
|
+
#
|
30
|
+
def commands
|
31
|
+
{
|
32
|
+
'python_reset' => 'Resets/restarts the Python interpreter',
|
33
|
+
'python_execute' => 'Execute a python command string',
|
34
|
+
'python_import' => 'Import/run a python file or module'
|
35
|
+
}
|
36
|
+
end
|
37
|
+
|
38
|
+
def cmd_python_reset(*args)
|
39
|
+
client.python.reset
|
40
|
+
print_good('Python interpreter successfully reset')
|
41
|
+
end
|
42
|
+
|
43
|
+
@@python_import_opts = Rex::Parser::Arguments.new(
|
44
|
+
'-h' => [false, 'Help banner'],
|
45
|
+
'-f' => [true, 'Path to the file (.py, .pyc), or module directory to import'],
|
46
|
+
'-n' => [true, 'Name of the module (optional, for single files only)'],
|
47
|
+
'-r' => [true, 'Name of the variable containing the result (optional, single files only)']
|
48
|
+
)
|
49
|
+
|
50
|
+
def python_import_usage
|
51
|
+
print_line('Usage: python_import <-f file path> [-n mod name] [-r result var name]')
|
52
|
+
print_line
|
53
|
+
print_line('Loads a python code file or module from disk into memory on the target.')
|
54
|
+
print_line('The module loader requires a path to a folder that contains the module,')
|
55
|
+
print_line('and the folder name will be used as the module name. Only .py files will')
|
56
|
+
print_line('work with modules.')
|
57
|
+
print_line(@@python_import_opts.usage)
|
58
|
+
end
|
59
|
+
|
60
|
+
#
|
61
|
+
# Import/run a python file
|
62
|
+
#
|
63
|
+
def cmd_python_import(*args)
|
64
|
+
if args.length == 0 || args.include?('-h')
|
65
|
+
python_import_usage
|
66
|
+
return false
|
67
|
+
end
|
68
|
+
|
69
|
+
result_var = nil
|
70
|
+
source = nil
|
71
|
+
mod_name = nil
|
72
|
+
|
73
|
+
@@python_import_opts.parse(args) { |opt, idx, val|
|
74
|
+
case opt
|
75
|
+
when '-f'
|
76
|
+
source = val
|
77
|
+
when '-n'
|
78
|
+
mod_name = val
|
79
|
+
when '-r'
|
80
|
+
result_var = val
|
81
|
+
end
|
82
|
+
}
|
83
|
+
|
84
|
+
unless source
|
85
|
+
print_error("The -f parameter must be specified")
|
86
|
+
return false
|
87
|
+
end
|
88
|
+
|
89
|
+
if ::File.directory?(source)
|
90
|
+
files = ::Find.find(source).select { |p| /.*\.py$/ =~ p }
|
91
|
+
if files.length == 0
|
92
|
+
fail_with("No .py files found in #{source}")
|
93
|
+
end
|
94
|
+
|
95
|
+
base_name = ::File.basename(source)
|
96
|
+
unless source.end_with?('/')
|
97
|
+
source << '/'
|
98
|
+
end
|
99
|
+
|
100
|
+
print_status("Importing #{source} with base module name #{base_name} ...")
|
101
|
+
|
102
|
+
files.each do |file|
|
103
|
+
rel_path = file[source.length, file.length - source.length]
|
104
|
+
parts = rel_path.split('/')
|
105
|
+
|
106
|
+
mod_parts = [base_name] + parts[0, parts.length - 1]
|
107
|
+
|
108
|
+
if parts[-1] != '__init__.py'
|
109
|
+
mod_parts << ::File.basename(parts[-1], '.*')
|
110
|
+
end
|
111
|
+
|
112
|
+
mod_name = mod_parts.join('.')
|
113
|
+
print_status("Importing #{file} as #{mod_name} ...")
|
114
|
+
result = client.python.import(file, mod_name, nil)
|
115
|
+
handle_exec_result(result, nil)
|
116
|
+
end
|
117
|
+
else
|
118
|
+
print_status("Importing #{source} ...")
|
119
|
+
result = client.python.import(source, mod_name, result_var)
|
120
|
+
handle_exec_result(result, result_var)
|
121
|
+
end
|
122
|
+
|
123
|
+
end
|
124
|
+
|
125
|
+
@@python_execute_opts = Rex::Parser::Arguments.new(
|
126
|
+
'-h' => [false, 'Help banner'],
|
127
|
+
'-r' => [true, 'Name of the variable containing the result (optional)']
|
128
|
+
)
|
129
|
+
|
130
|
+
def python_execute_usage
|
131
|
+
print_line('Usage: python_execute <python code> [-r result var name]')
|
132
|
+
print_line
|
133
|
+
print_line('Runs the given python string on the target. If a result is required,')
|
134
|
+
print_line('it should be stored in a python variable, and that variable should')
|
135
|
+
print_line('passed using the -r parameter.')
|
136
|
+
print_line(@@python_execute_opts.usage)
|
137
|
+
end
|
138
|
+
|
139
|
+
#
|
140
|
+
# Execute a simple python command string
|
141
|
+
#
|
142
|
+
def cmd_python_execute(*args)
|
143
|
+
if args.length == 0 || args.include?('-h')
|
144
|
+
python_execute_usage
|
145
|
+
return false
|
146
|
+
end
|
147
|
+
|
148
|
+
code = args.shift
|
149
|
+
result_var = nil
|
150
|
+
|
151
|
+
@@python_execute_opts.parse(args) { |opt, idx, val|
|
152
|
+
case opt
|
153
|
+
when '-r'
|
154
|
+
result_var = val
|
155
|
+
end
|
156
|
+
}
|
157
|
+
|
158
|
+
result = client.python.execute_string(code, result_var)
|
159
|
+
|
160
|
+
handle_exec_result(result, result_var)
|
161
|
+
end
|
162
|
+
|
163
|
+
private
|
164
|
+
|
165
|
+
def handle_exec_result(result, result_var)
|
166
|
+
if result[:result]
|
167
|
+
print_good("#{result_var} = #{result[:result]}")
|
168
|
+
elsif result[:stdout].length == 0 and result[:stderr].length == 0
|
169
|
+
print_good("Command executed without returning a result")
|
170
|
+
end
|
171
|
+
|
172
|
+
if result[:stdout].length > 0
|
173
|
+
print_good("Content written to stdout:\n#{result[:stdout]}")
|
174
|
+
end
|
175
|
+
|
176
|
+
if result[:stderr].length > 0
|
177
|
+
print_error("Content written to stderr:\n#{result[:stderr]}")
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
end
|
182
|
+
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
@@ -1,5 +1,6 @@
|
|
1
1
|
# -*- coding: binary -*-
|
2
2
|
require 'tempfile'
|
3
|
+
require 'filesize'
|
3
4
|
require 'rex/post/meterpreter'
|
4
5
|
|
5
6
|
module Rex
|
@@ -30,49 +31,63 @@ class Console::CommandDispatcher::Stdapi::Fs
|
|
30
31
|
@@upload_opts = Rex::Parser::Arguments.new(
|
31
32
|
"-h" => [ false, "Help banner." ],
|
32
33
|
"-r" => [ false, "Upload recursively." ])
|
34
|
+
#
|
35
|
+
# Options for the ls command
|
36
|
+
#
|
37
|
+
@@ls_opts = Rex::Parser::Arguments.new(
|
38
|
+
"-h" => [ false, "Help banner." ],
|
39
|
+
"-S" => [ true, "Search string." ],
|
40
|
+
"-t" => [ false, "Sort by time" ],
|
41
|
+
"-s" => [ false, "Sort by size" ],
|
42
|
+
"-r" => [ false, "Reverse sort order" ],
|
43
|
+
"-x" => [ false, "Show short file names" ],
|
44
|
+
"-l" => [ false, "List in long format (default)" ],
|
45
|
+
"-R" => [ false, "Recursively list subdirectories encountered" ])
|
33
46
|
|
34
47
|
#
|
35
48
|
# List of supported commands.
|
36
49
|
#
|
37
50
|
def commands
|
38
51
|
all = {
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
52
|
+
'cat' => 'Read the contents of a file to the screen',
|
53
|
+
'cd' => 'Change directory',
|
54
|
+
'del' => 'Delete the specified file',
|
55
|
+
'download' => 'Download a file or directory',
|
56
|
+
'edit' => 'Edit a file',
|
57
|
+
'getlwd' => 'Print local working directory',
|
58
|
+
'getwd' => 'Print working directory',
|
59
|
+
'lcd' => 'Change local working directory',
|
60
|
+
'lpwd' => 'Print local working directory',
|
61
|
+
'ls' => 'List files',
|
62
|
+
'mkdir' => 'Make directory',
|
63
|
+
'pwd' => 'Print working directory',
|
64
|
+
'rm' => 'Delete the specified file',
|
65
|
+
'mv' => 'Move source to destination',
|
66
|
+
'rmdir' => 'Remove directory',
|
67
|
+
'search' => 'Search for files',
|
68
|
+
'upload' => 'Upload a file or directory',
|
69
|
+
'show_mount' => 'List all mount points/logical drives',
|
56
70
|
}
|
57
71
|
|
58
72
|
reqs = {
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
73
|
+
'cat' => [],
|
74
|
+
'cd' => ['stdapi_fs_chdir'],
|
75
|
+
'del' => ['stdapi_fs_rm'],
|
76
|
+
'download' => [],
|
77
|
+
'edit' => [],
|
78
|
+
'getlwd' => [],
|
79
|
+
'getwd' => ['stdapi_fs_getwd'],
|
80
|
+
'lcd' => [],
|
81
|
+
'lpwd' => [],
|
82
|
+
'ls' => ['stdapi_fs_stat', 'stdapi_fs_ls'],
|
83
|
+
'mkdir' => ['stdapi_fs_mkdir'],
|
84
|
+
'pwd' => ['stdapi_fs_getwd'],
|
85
|
+
'rmdir' => ['stdapi_fs_delete_dir'],
|
86
|
+
'rm' => ['stdapi_fs_delete_file'],
|
87
|
+
'mv' => ['stdapi_fs_file_move'],
|
88
|
+
'search' => ['stdapi_fs_search'],
|
89
|
+
'upload' => [],
|
90
|
+
'show_mount' => ['stdapi_fs_mount_show'],
|
76
91
|
}
|
77
92
|
|
78
93
|
all.delete_if do |cmd, desc|
|
@@ -99,57 +114,101 @@ class Console::CommandDispatcher::Stdapi::Fs
|
|
99
114
|
#
|
100
115
|
# Search for files.
|
101
116
|
#
|
102
|
-
def cmd_search(
|
117
|
+
def cmd_search(*args)
|
103
118
|
|
104
119
|
root = nil
|
105
|
-
glob = nil
|
106
120
|
recurse = true
|
121
|
+
globs = []
|
122
|
+
files = []
|
107
123
|
|
108
124
|
opts = Rex::Parser::Arguments.new(
|
109
125
|
"-h" => [ false, "Help Banner." ],
|
110
126
|
"-d" => [ true, "The directory/drive to begin searching from. Leave empty to search all drives. (Default: #{root})" ],
|
111
|
-
"-f" => [ true, "
|
127
|
+
"-f" => [ true, "A file pattern glob to search for. (e.g. *secret*.doc?)" ],
|
112
128
|
"-r" => [ true, "Recursivly search sub directories. (Default: #{recurse})" ]
|
113
129
|
)
|
114
130
|
|
115
131
|
opts.parse(args) { | opt, idx, val |
|
116
132
|
case opt
|
117
133
|
when "-h"
|
118
|
-
print_line(
|
119
|
-
print_line(
|
120
|
-
print_line(
|
134
|
+
print_line("Usage: search [-d dir] [-r recurse] -f pattern [-f pattern]...")
|
135
|
+
print_line("Search for files.")
|
136
|
+
print_line(opts.usage)
|
121
137
|
return
|
122
138
|
when "-d"
|
123
139
|
root = val
|
124
140
|
when "-f"
|
125
|
-
|
141
|
+
globs << val
|
126
142
|
when "-r"
|
127
|
-
recurse = false if
|
143
|
+
recurse = false if val =~ /^(f|n|0)/i
|
128
144
|
end
|
129
145
|
}
|
130
146
|
|
131
|
-
if
|
132
|
-
print_error(
|
147
|
+
if globs.empty?
|
148
|
+
print_error("You must specify a valid file glob to search for, e.g. >search -f *.doc")
|
133
149
|
return
|
134
150
|
end
|
135
151
|
|
136
|
-
|
152
|
+
globs.uniq.each do |glob|
|
153
|
+
files += client.fs.file.search(root, glob, recurse)
|
154
|
+
end
|
137
155
|
|
138
|
-
if
|
139
|
-
print_line(
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
156
|
+
if files.empty?
|
157
|
+
print_line("No files matching your search were found.")
|
158
|
+
return
|
159
|
+
end
|
160
|
+
|
161
|
+
print_line("Found #{files.length} result#{ files.length > 1 ? 's' : '' }...")
|
162
|
+
files.each do | file |
|
163
|
+
if file['size'] > 0
|
164
|
+
print(" #{file['path']}#{ file['path'].empty? ? '' : '\\' }#{file['name']} (#{file['size']} bytes)\n")
|
165
|
+
else
|
166
|
+
print(" #{file['path']}#{ file['path'].empty? ? '' : '\\' }#{file['name']}\n")
|
146
167
|
end
|
147
|
-
else
|
148
|
-
print_line( "No files matching your search were found." )
|
149
168
|
end
|
150
169
|
|
151
170
|
end
|
152
171
|
|
172
|
+
#
|
173
|
+
# Show all the mount points/logical drives (currently geared towards
|
174
|
+
# the Windows Meterpreter).
|
175
|
+
#
|
176
|
+
def cmd_show_mount(*args)
|
177
|
+
if args.include?('-h')
|
178
|
+
print_line('Usage: show_mount')
|
179
|
+
return true
|
180
|
+
end
|
181
|
+
|
182
|
+
mounts = client.fs.mount.show_mount
|
183
|
+
|
184
|
+
table = Rex::Ui::Text::Table.new(
|
185
|
+
'Header' => 'Mounts / Drives',
|
186
|
+
'Indent' => 0,
|
187
|
+
'SortIndex' => 0,
|
188
|
+
'Columns' => [
|
189
|
+
'Name', 'Type', 'Size (Total)', 'Size (Free)', 'Mapped to'
|
190
|
+
]
|
191
|
+
)
|
192
|
+
|
193
|
+
mounts.each do |d|
|
194
|
+
ts = ::Filesize.from("#{d[:total_space]} B").pretty.split(' ')
|
195
|
+
fs = ::Filesize.from("#{d[:free_space]} B").pretty.split(' ')
|
196
|
+
table << [
|
197
|
+
d[:name],
|
198
|
+
d[:type],
|
199
|
+
"#{ts[0].rjust(6)} #{ts[1].ljust(3)}",
|
200
|
+
"#{fs[0].rjust(6)} #{fs[1].ljust(3)}",
|
201
|
+
d[:unc]
|
202
|
+
]
|
203
|
+
end
|
204
|
+
|
205
|
+
print_line
|
206
|
+
print_line(table.to_s)
|
207
|
+
print_line
|
208
|
+
print_line("Total mounts/drives: #{mounts.length}")
|
209
|
+
print_line
|
210
|
+
end
|
211
|
+
|
153
212
|
#
|
154
213
|
# Reads the contents of a file and prints them to the screen.
|
155
214
|
#
|
@@ -223,29 +282,41 @@ class Console::CommandDispatcher::Stdapi::Fs
|
|
223
282
|
|
224
283
|
alias :cmd_del :cmd_rm
|
225
284
|
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
285
|
+
#
|
286
|
+
# Move source to destination
|
287
|
+
#
|
288
|
+
def cmd_mv(*args)
|
289
|
+
if (args.length < 2)
|
290
|
+
print_line("Usage: mv oldfile newfile")
|
291
|
+
return true
|
292
|
+
end
|
293
|
+
client.fs.file.mv(args[0],args[1])
|
294
|
+
return true
|
295
|
+
end
|
234
296
|
|
235
|
-
|
297
|
+
alias :cmd_move :cmd_mv
|
298
|
+
alias :cmd_rename :cmd_mv
|
236
299
|
|
237
|
-
|
238
|
-
|
300
|
+
#
|
301
|
+
# Move source to destination
|
302
|
+
#
|
303
|
+
def cmd_cp(*args)
|
304
|
+
if (args.length < 2)
|
305
|
+
print_line("Usage: cp oldfile newfile")
|
306
|
+
return true
|
307
|
+
end
|
308
|
+
client.fs.file.cp(args[0],args[1])
|
309
|
+
return true
|
310
|
+
end
|
239
311
|
|
240
|
-
|
241
|
-
alias :cmd_rename :cmd_mv
|
312
|
+
alias :cmd_copy :cmd_cp
|
242
313
|
|
243
314
|
|
244
315
|
def cmd_download_help
|
245
|
-
print_line
|
316
|
+
print_line("Usage: download [options] src1 src2 src3 ... destination")
|
246
317
|
print_line
|
247
|
-
print_line
|
248
|
-
print_line
|
318
|
+
print_line("Downloads remote files and directories to the local machine.")
|
319
|
+
print_line(@@download_opts.usage)
|
249
320
|
end
|
250
321
|
|
251
322
|
#
|
@@ -289,24 +360,62 @@ class Console::CommandDispatcher::Stdapi::Fs
|
|
289
360
|
dest = last
|
290
361
|
end
|
291
362
|
|
363
|
+
# Download to a directory, not a pattern
|
364
|
+
if client.fs.file.is_glob?(dest)
|
365
|
+
dest = ::File.dirname(dest)
|
366
|
+
end
|
367
|
+
|
292
368
|
# Go through each source item and download them
|
293
369
|
src_items.each { |src|
|
294
|
-
|
370
|
+
glob = nil
|
371
|
+
if client.fs.file.is_glob?(src)
|
372
|
+
glob = ::File.basename(src)
|
373
|
+
src = ::File.dirname(src)
|
374
|
+
end
|
295
375
|
|
296
|
-
if
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
376
|
+
# Use search if possible for recursive pattern matching. It will work
|
377
|
+
# more intuitively since it will not try to match on intermediate
|
378
|
+
# directories, only file names.
|
379
|
+
if glob && recursive && client.commands.include?('stdapi_fs_search')
|
380
|
+
|
381
|
+
files = client.fs.file.search(src, glob, recursive)
|
382
|
+
if !files.empty?
|
383
|
+
print_line("Downloading #{files.length} file#{files.length > 1 ? 's' : ''}...")
|
384
|
+
|
385
|
+
files.each do |file|
|
386
|
+
src_separator = client.fs.file.separator
|
387
|
+
src_path = file['path'] + client.fs.file.separator + file['name']
|
388
|
+
dest_path = src_path.tr(src_separator, ::File::SEPARATOR)
|
389
|
+
|
390
|
+
client.fs.file.download(dest_path, src_path) do |step, src, dst|
|
391
|
+
puts step
|
392
|
+
print_status("#{step.ljust(11)}: #{src} -> #{dst}")
|
393
|
+
client.framework.events.on_session_download(client, src, dest) if msf_loaded?
|
394
|
+
end
|
395
|
+
end
|
396
|
+
|
397
|
+
else
|
398
|
+
print_status("No matching files found for download")
|
399
|
+
end
|
400
|
+
|
401
|
+
else
|
402
|
+
# Perform direct matching
|
403
|
+
stat = client.fs.file.stat(src)
|
404
|
+
if (stat.directory?)
|
405
|
+
client.fs.dir.download(dest, src, recursive, true, glob) do |step, src, dst|
|
406
|
+
print_status("#{step.ljust(11)}: #{src} -> #{dst}")
|
407
|
+
client.framework.events.on_session_download(client, src, dest) if msf_loaded?
|
408
|
+
end
|
409
|
+
elsif (stat.file?)
|
410
|
+
client.fs.file.download(dest, src) do |step, src, dst|
|
411
|
+
print_status("#{step.ljust(11)}: #{src} -> #{dst}")
|
412
|
+
client.framework.events.on_session_download(client, src, dest) if msf_loaded?
|
413
|
+
end
|
414
|
+
end
|
306
415
|
end
|
307
416
|
}
|
308
417
|
|
309
|
-
|
418
|
+
true
|
310
419
|
end
|
311
420
|
|
312
421
|
#
|
@@ -324,16 +433,8 @@ class Console::CommandDispatcher::Stdapi::Fs
|
|
324
433
|
meterp_temp.binmode
|
325
434
|
temp_path = meterp_temp.path
|
326
435
|
|
327
|
-
|
328
|
-
|
329
|
-
client.fs.file.download_file(temp_path, args[0])
|
330
|
-
rescue RequestError => re
|
331
|
-
# If the file doesn't exist, then it's okay. Otherwise, throw the
|
332
|
-
# error.
|
333
|
-
if re.result != 2
|
334
|
-
raise $!
|
335
|
-
end
|
336
|
-
end
|
436
|
+
# Try to download the file, but don't worry if it doesn't exist
|
437
|
+
client.fs.file.download_file(temp_path, args[0]) rescue nil
|
337
438
|
|
338
439
|
# Spawn the editor (default to vi)
|
339
440
|
editor = Rex::Compat.getenv('EDITOR') || 'vi'
|
@@ -357,49 +458,139 @@ class Console::CommandDispatcher::Stdapi::Fs
|
|
357
458
|
|
358
459
|
alias cmd_getlwd cmd_lpwd
|
359
460
|
|
461
|
+
|
462
|
+
def cmd_ls_help
|
463
|
+
print_line "Usage: ls [options]"
|
464
|
+
print_line
|
465
|
+
print_line "Lists contents of directory or file info, searchable"
|
466
|
+
print_line @@ls_opts.usage
|
467
|
+
end
|
468
|
+
|
469
|
+
def list_path(path, columns, sort, order, short, recursive = false, depth = 0, search_term = nil)
|
470
|
+
|
471
|
+
# avoid infinite recursion
|
472
|
+
if depth > 100
|
473
|
+
return
|
474
|
+
end
|
475
|
+
|
476
|
+
tbl = Rex::Ui::Text::Table.new(
|
477
|
+
'Header' => "Listing: #{path}",
|
478
|
+
'SortIndex' => columns.index(sort),
|
479
|
+
'SortOrder' => order,
|
480
|
+
'Columns' => columns,
|
481
|
+
'SearchTerm' => search_term)
|
482
|
+
|
483
|
+
items = 0
|
484
|
+
|
485
|
+
# Enumerate each item...
|
486
|
+
# No need to sort as Table will do it for us
|
487
|
+
client.fs.dir.entries_with_info(path).each do |p|
|
488
|
+
|
489
|
+
ffstat = p['StatBuf']
|
490
|
+
fname = p['FileName'] || 'unknown'
|
491
|
+
|
492
|
+
row = [
|
493
|
+
ffstat ? ffstat.prettymode : '',
|
494
|
+
ffstat ? ffstat.size : '',
|
495
|
+
ffstat ? ffstat.ftype[0,3] : '',
|
496
|
+
ffstat ? ffstat.mtime : '',
|
497
|
+
fname
|
498
|
+
]
|
499
|
+
row.insert(4, p['FileShortName'] || '') if short
|
500
|
+
|
501
|
+
if fname != '.' && fname != '..'
|
502
|
+
if row.join(' ') =~ /#{search_term}/
|
503
|
+
tbl << row
|
504
|
+
items += 1
|
505
|
+
end
|
506
|
+
|
507
|
+
if recursive && ffstat && ffstat.directory?
|
508
|
+
if client.fs.file.is_glob?(path)
|
509
|
+
child_path = ::File.dirname(path) + ::File::SEPARATOR + fname
|
510
|
+
child_path += ::File::SEPARATOR + ::File.basename(path)
|
511
|
+
else
|
512
|
+
child_path = path + ::File::SEPARATOR + fname
|
513
|
+
end
|
514
|
+
begin
|
515
|
+
list_path(child_path, columns, sort, order, short, recursive, depth + 1, search_term)
|
516
|
+
rescue RequestError
|
517
|
+
end
|
518
|
+
end
|
519
|
+
end
|
520
|
+
end
|
521
|
+
|
522
|
+
if items > 0
|
523
|
+
print_line(tbl.to_s)
|
524
|
+
else
|
525
|
+
print_line("No entries exist in #{path}")
|
526
|
+
end
|
527
|
+
end
|
528
|
+
|
360
529
|
#
|
361
530
|
# Lists files
|
362
531
|
#
|
363
|
-
# TODO: make this more useful
|
364
|
-
#
|
365
532
|
def cmd_ls(*args)
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
533
|
+
# Set defaults
|
534
|
+
path = client.fs.dir.getwd
|
535
|
+
search_term = nil
|
536
|
+
sort = 'Name'
|
537
|
+
short = nil
|
538
|
+
order = :forward
|
539
|
+
recursive = nil
|
540
|
+
|
541
|
+
# Parse the args
|
542
|
+
@@ls_opts.parse(args) { |opt, idx, val|
|
543
|
+
case opt
|
544
|
+
# Sort options
|
545
|
+
when '-s'
|
546
|
+
sort = 'Size'
|
547
|
+
when '-t'
|
548
|
+
sort = 'Last modified'
|
549
|
+
# Output options
|
550
|
+
when '-x'
|
551
|
+
short = true
|
552
|
+
when '-l'
|
553
|
+
short = nil
|
554
|
+
when '-r'
|
555
|
+
order = :reverse
|
556
|
+
when '-R'
|
557
|
+
recursive = true
|
558
|
+
# Search
|
559
|
+
when '-S'
|
560
|
+
search_term = val
|
561
|
+
if search_term.nil?
|
562
|
+
print_error("Enter a search term")
|
563
|
+
return true
|
564
|
+
else
|
565
|
+
search_term = /#{search_term}/nmi
|
566
|
+
end
|
567
|
+
# Help and path
|
568
|
+
when "-h"
|
569
|
+
cmd_ls_help
|
570
|
+
return 0
|
571
|
+
when nil
|
572
|
+
path = val
|
573
|
+
end
|
574
|
+
}
|
378
575
|
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
p['StatBuf'] ? p['StatBuf'].ftype[0,3] : '',
|
391
|
-
p['StatBuf'] ? p['StatBuf'].mtime : '',
|
392
|
-
p['FileName'] || 'unknown'
|
393
|
-
]
|
394
|
-
|
395
|
-
items += 1
|
396
|
-
}
|
397
|
-
|
398
|
-
if (items > 0)
|
399
|
-
print("\n" + tbl.to_s + "\n")
|
400
|
-
else
|
401
|
-
print_line("No entries exist in #{path}")
|
576
|
+
columns = [ 'Mode', 'Size', 'Type', 'Last modified', 'Name' ]
|
577
|
+
columns.insert(4, 'Short Name') if short
|
578
|
+
|
579
|
+
stat_path = path
|
580
|
+
|
581
|
+
# Check session capabilities
|
582
|
+
is_glob = client.fs.file.is_glob?(path)
|
583
|
+
if is_glob
|
584
|
+
if !client.commands.include?('stdapi_fs_search')
|
585
|
+
print_line('File globbing not supported with this session')
|
586
|
+
return
|
402
587
|
end
|
588
|
+
stat_path = ::File.dirname(path)
|
589
|
+
end
|
590
|
+
|
591
|
+
stat = client.fs.file.stat(stat_path)
|
592
|
+
if stat.directory?
|
593
|
+
list_path(path, columns, sort, order, short, recursive, 0, search_term)
|
403
594
|
else
|
404
595
|
print_line("#{stat.prettymode} #{stat.size} #{stat.ftype[0,3]} #{stat.mtime} #{path}")
|
405
596
|
end
|
@@ -452,10 +643,10 @@ class Console::CommandDispatcher::Stdapi::Fs
|
|
452
643
|
end
|
453
644
|
|
454
645
|
def cmd_upload_help
|
455
|
-
print_line
|
646
|
+
print_line("Usage: upload [options] src1 src2 src3 ... destination")
|
456
647
|
print_line
|
457
|
-
print_line
|
458
|
-
print_line
|
648
|
+
print_line("Uploads local files and directories to the remote machine.")
|
649
|
+
print_line(@@upload_opts.usage)
|
459
650
|
end
|
460
651
|
|
461
652
|
#
|