rex 2.0.8 → 2.0.9

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.
Files changed (151) hide show
  1. checksums.yaml +4 -4
  2. data/lib/rex.rb +1 -0
  3. data/lib/rex/arch.rb +5 -0
  4. data/lib/rex/arch/x86.rb +19 -5
  5. data/lib/rex/arch/zarch.rb +17 -0
  6. data/lib/rex/compat.rb +5 -4
  7. data/lib/rex/constants.rb +3 -1
  8. data/lib/rex/encoder/alpha2/alpha_mixed.rb +70 -9
  9. data/lib/rex/encoder/alpha2/alpha_upper.rb +67 -8
  10. data/lib/rex/exploitation/cmdstager.rb +1 -0
  11. data/lib/rex/exploitation/cmdstager/certutil.rb +115 -0
  12. data/lib/rex/exploitation/cmdstager/echo.rb +6 -3
  13. data/lib/rex/exploitation/egghunter.rb +1 -1
  14. data/lib/rex/google/geolocation.rb +68 -0
  15. data/lib/rex/io/bidirectional_pipe.rb +0 -4
  16. data/lib/rex/java/serialization.rb +2 -0
  17. data/lib/rex/java/serialization/decode_error.rb +11 -0
  18. data/lib/rex/java/serialization/encode_error.rb +11 -0
  19. data/lib/rex/java/serialization/model.rb +2 -0
  20. data/lib/rex/java/serialization/model/annotation.rb +3 -3
  21. data/lib/rex/java/serialization/model/block_data.rb +3 -3
  22. data/lib/rex/java/serialization/model/block_data_long.rb +3 -3
  23. data/lib/rex/java/serialization/model/class_desc.rb +6 -6
  24. data/lib/rex/java/serialization/model/contents.rb +17 -10
  25. data/lib/rex/java/serialization/model/field.rb +12 -11
  26. data/lib/rex/java/serialization/model/long_utf.rb +3 -3
  27. data/lib/rex/java/serialization/model/new_array.rb +22 -23
  28. data/lib/rex/java/serialization/model/new_class.rb +57 -0
  29. data/lib/rex/java/serialization/model/new_class_desc.rb +15 -16
  30. data/lib/rex/java/serialization/model/new_enum.rb +5 -5
  31. data/lib/rex/java/serialization/model/new_object.rb +22 -17
  32. data/lib/rex/java/serialization/model/proxy_class_desc.rb +109 -0
  33. data/lib/rex/java/serialization/model/reference.rb +4 -4
  34. data/lib/rex/java/serialization/model/stream.rb +7 -7
  35. data/lib/rex/java/serialization/model/utf.rb +3 -3
  36. data/lib/rex/json_hash_file.rb +94 -0
  37. data/lib/rex/logging/log_sink.rb +1 -0
  38. data/lib/rex/logging/sinks/timestamp_flatfile.rb +21 -0
  39. data/lib/rex/parser/appscan_nokogiri.rb +13 -23
  40. data/lib/rex/parser/fs/ntfs.rb +10 -5
  41. data/lib/rex/parser/nmap_nokogiri.rb +3 -1
  42. data/lib/rex/parser/openvas_nokogiri.rb +70 -73
  43. data/lib/rex/parser/winscp.rb +108 -0
  44. data/lib/rex/parser/x509_certificate.rb +92 -0
  45. data/lib/rex/payloads.rb +0 -1
  46. data/lib/rex/payloads/meterpreter/config.rb +154 -0
  47. data/lib/rex/payloads/meterpreter/uri_checksum.rb +136 -0
  48. data/lib/rex/post/meterpreter.rb +1 -1
  49. data/lib/rex/post/meterpreter/client.rb +26 -3
  50. data/lib/rex/post/meterpreter/client_core.rb +387 -75
  51. data/lib/rex/post/meterpreter/extensions/android/android.rb +127 -37
  52. data/lib/rex/post/meterpreter/extensions/android/tlv.rb +46 -25
  53. data/lib/rex/post/meterpreter/extensions/extapi/extapi.rb +4 -0
  54. data/lib/rex/post/meterpreter/extensions/extapi/ntds/ntds.rb +39 -0
  55. data/lib/rex/post/meterpreter/extensions/extapi/pageant/pageant.rb +44 -0
  56. data/lib/rex/post/meterpreter/extensions/extapi/tlv.rb +9 -0
  57. data/lib/rex/post/meterpreter/extensions/kiwi/kiwi.rb +16 -1
  58. data/lib/rex/post/meterpreter/extensions/priv/priv.rb +1 -1
  59. data/lib/rex/post/meterpreter/extensions/python/python.rb +114 -0
  60. data/lib/rex/post/meterpreter/extensions/python/tlv.rb +21 -0
  61. data/lib/rex/post/meterpreter/extensions/stdapi/fs/dir.rb +17 -14
  62. data/lib/rex/post/meterpreter/extensions/stdapi/fs/file.rb +33 -12
  63. data/lib/rex/post/meterpreter/extensions/stdapi/fs/mount.rb +57 -0
  64. data/lib/rex/post/meterpreter/extensions/stdapi/railgun/def/def_kernel32.rb +3 -3
  65. data/lib/rex/post/meterpreter/extensions/stdapi/stdapi.rb +3 -1
  66. data/lib/rex/post/meterpreter/extensions/stdapi/sys/config.rb +2 -0
  67. data/lib/rex/post/meterpreter/extensions/stdapi/sys/process.rb +16 -3
  68. data/lib/rex/post/meterpreter/extensions/stdapi/sys/registry.rb +29 -6
  69. data/lib/rex/post/meterpreter/extensions/stdapi/sys/registry_subsystem/registry_key.rb +5 -1
  70. data/lib/rex/post/meterpreter/extensions/stdapi/tlv.rb +18 -6
  71. data/lib/rex/post/meterpreter/extensions/stdapi/ui.rb +2 -2
  72. data/lib/rex/post/meterpreter/extensions/stdapi/webcam/webcam.rb +34 -36
  73. data/lib/rex/post/meterpreter/packet.rb +29 -0
  74. data/lib/rex/post/meterpreter/packet_dispatcher.rb +20 -7
  75. data/lib/rex/post/meterpreter/ui/console.rb +1 -0
  76. data/lib/rex/post/meterpreter/ui/console/command_dispatcher/android.rb +230 -72
  77. data/lib/rex/post/meterpreter/ui/console/command_dispatcher/core.rb +544 -34
  78. data/lib/rex/post/meterpreter/ui/console/command_dispatcher/extapi/adsi.rb +188 -57
  79. data/lib/rex/post/meterpreter/ui/console/command_dispatcher/kiwi.rb +115 -93
  80. data/lib/rex/post/meterpreter/ui/console/command_dispatcher/lanattacks/dhcp.rb +1 -1
  81. data/lib/rex/post/meterpreter/ui/console/command_dispatcher/mimikatz.rb +1 -1
  82. data/lib/rex/post/meterpreter/ui/console/command_dispatcher/priv/elevate.rb +49 -15
  83. data/lib/rex/post/meterpreter/ui/console/command_dispatcher/priv/timestomp.rb +11 -2
  84. data/lib/rex/post/meterpreter/ui/console/command_dispatcher/python.rb +187 -0
  85. data/lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/fs.rb +324 -133
  86. data/lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/net.rb +52 -2
  87. data/lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/sys.rb +68 -65
  88. data/lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/ui.rb +9 -1
  89. data/lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/webcam.rb +113 -118
  90. data/lib/rex/post/meterpreter/ui/console/interactive_channel.rb +3 -0
  91. data/lib/rex/powershell.rb +62 -0
  92. data/lib/rex/powershell/command.rb +359 -0
  93. data/lib/rex/{exploitation/powershell → powershell}/function.rb +0 -2
  94. data/lib/rex/{exploitation/powershell → powershell}/obfu.rb +0 -2
  95. data/lib/rex/{exploitation/powershell → powershell}/output.rb +11 -5
  96. data/lib/rex/{exploitation/powershell → powershell}/param.rb +0 -2
  97. data/lib/rex/powershell/parser.rb +182 -0
  98. data/lib/rex/powershell/payload.rb +78 -0
  99. data/lib/rex/{exploitation/powershell → powershell}/psh_methods.rb +16 -2
  100. data/lib/rex/{exploitation/powershell → powershell}/script.rb +2 -4
  101. data/lib/rex/proto/dcerpc/client.rb +6 -6
  102. data/lib/rex/proto/dcerpc/exceptions.rb +26 -0
  103. data/lib/rex/proto/http/client.rb +3 -3
  104. data/lib/rex/proto/http/client_request.rb +0 -5
  105. data/lib/rex/proto/http/response.rb +86 -0
  106. data/lib/rex/proto/ipmi/utils.rb +30 -26
  107. data/lib/rex/proto/kerberos/client.rb +1 -1
  108. data/lib/rex/proto/kerberos/model/kdc_request.rb +2 -2
  109. data/lib/rex/proto/rfb/client.rb +8 -3
  110. data/lib/rex/proto/rfb/constants.rb +1 -1
  111. data/lib/rex/proto/rmi.rb +2 -0
  112. data/lib/rex/proto/rmi/decode_error.rb +10 -0
  113. data/lib/rex/proto/rmi/exception.rb +10 -0
  114. data/lib/rex/proto/rmi/model.rb +5 -0
  115. data/lib/rex/proto/rmi/model/call.rb +4 -4
  116. data/lib/rex/proto/rmi/model/call_data.rb +137 -0
  117. data/lib/rex/proto/rmi/model/dgc_ack.rb +2 -2
  118. data/lib/rex/proto/rmi/model/element.rb +26 -11
  119. data/lib/rex/proto/rmi/model/output_header.rb +4 -4
  120. data/lib/rex/proto/rmi/model/ping.rb +2 -2
  121. data/lib/rex/proto/rmi/model/ping_ack.rb +2 -2
  122. data/lib/rex/proto/rmi/model/protocol_ack.rb +2 -2
  123. data/lib/rex/proto/rmi/model/return_data.rb +5 -5
  124. data/lib/rex/proto/rmi/model/return_value.rb +124 -0
  125. data/lib/rex/proto/rmi/model/unique_identifier.rb +77 -0
  126. data/lib/rex/proto/steam.rb +3 -0
  127. data/lib/rex/proto/steam/message.rb +125 -0
  128. data/lib/rex/proto/tftp/client.rb +35 -14
  129. data/lib/rex/random_identifier_generator.rb +2 -0
  130. data/lib/rex/ropbuilder.rb +1 -1
  131. data/lib/rex/socket/parameters.rb +9 -0
  132. data/lib/rex/socket/ssl_tcp.rb +25 -41
  133. data/lib/rex/socket/ssl_tcp_server.rb +10 -21
  134. data/lib/rex/sslscan/result.rb +20 -1
  135. data/lib/rex/text.rb +241 -55
  136. data/lib/rex/ui/output.rb +0 -3
  137. data/lib/rex/ui/subscriber.rb +0 -10
  138. data/lib/rex/ui/text/color.rb +9 -0
  139. data/lib/rex/ui/text/dispatcher_shell.rb +1 -0
  140. data/lib/rex/ui/text/output.rb +15 -4
  141. data/lib/rex/ui/text/output/file.rb +1 -0
  142. data/lib/rex/ui/text/output/stdio.rb +0 -16
  143. data/lib/rex/ui/text/shell.rb +3 -0
  144. data/lib/rex/ui/text/table.rb +85 -19
  145. data/lib/rex/user_agent.rb +118 -0
  146. data/rex.gemspec +2 -2
  147. metadata +41 -14
  148. data/lib/rex/exploitation/powershell.rb +0 -62
  149. data/lib/rex/exploitation/powershell/parser.rb +0 -183
  150. data/lib/rex/payloads/meterpreter.rb +0 -2
  151. 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 datstore to the active DHCP server.\n\n" +
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 commannd",
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 = -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 = [ "All techniques available",
27
- "Service - Named Pipe Impersonation (In Memory/Admin)",
28
- "Service - Named Pipe Impersonation (Dropper/Admin)",
29
- "Service - Token Duplication (In Memory/Admin)"
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
- result = client.priv.getsystem( technique )
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 (via technique #{result[1]})." );
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 OPTIONS\n" +
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 = args.shift
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
- "cat" => "Read the contents of a file to the screen",
40
- "cd" => "Change directory",
41
- "del" => "Delete the specified file",
42
- "download" => "Download a file or directory",
43
- "edit" => "Edit a file",
44
- "getlwd" => "Print local working directory",
45
- "getwd" => "Print working directory",
46
- "lcd" => "Change local working directory",
47
- "lpwd" => "Print local working directory",
48
- "ls" => "List files",
49
- "mkdir" => "Make directory",
50
- "pwd" => "Print working directory",
51
- "rm" => "Delete the specified file",
52
- "mv" => "Move source to destination",
53
- "rmdir" => "Remove directory",
54
- "search" => "Search for files",
55
- "upload" => "Upload a file or directory",
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
- "cat" => [ ],
60
- "cd" => [ "stdapi_fs_chdir" ],
61
- "del" => [ "stdapi_fs_rm" ],
62
- "download" => [ ],
63
- "edit" => [ ],
64
- "getlwd" => [ ],
65
- "getwd" => [ "stdapi_fs_getwd" ],
66
- "lcd" => [ ],
67
- "lpwd" => [ ],
68
- "ls" => [ "stdapi_fs_stat", "stdapi_fs_ls" ],
69
- "mkdir" => [ "stdapi_fs_mkdir" ],
70
- "pwd" => [ "stdapi_fs_getwd" ],
71
- "rmdir" => [ "stdapi_fs_delete_dir" ],
72
- "rm" => [ "stdapi_fs_delete_file" ],
73
- "mv" => [ "stdapi_fs_file_move" ],
74
- "search" => [ "stdapi_fs_search" ],
75
- "upload" => [ ],
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( *args )
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, "The file pattern glob to search for. (e.g. *secret*.doc?)" ],
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( "Usage: search [-d dir] [-r recurse] -f pattern" )
119
- print_line( "Search for files." )
120
- print_line( opts.usage )
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
- glob = val
141
+ globs << val
126
142
  when "-r"
127
- recurse = false if( val =~ /^(f|n|0)/i )
143
+ recurse = false if val =~ /^(f|n|0)/i
128
144
  end
129
145
  }
130
146
 
131
- if( not glob )
132
- print_error( "You must specify a valid file glob to search for, e.g. >search -f *.doc" )
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
- files = client.fs.file.search( root, glob, recurse )
152
+ globs.uniq.each do |glob|
153
+ files += client.fs.file.search(root, glob, recurse)
154
+ end
137
155
 
138
- if( not files.empty? )
139
- print_line( "Found #{files.length} result#{ files.length > 1 ? 's' : '' }..." )
140
- files.each do | file |
141
- if( file['size'] > 0 )
142
- print( " #{file['path']}#{ file['path'].empty? ? '' : '\\' }#{file['name']} (#{file['size']} bytes)\n" )
143
- else
144
- print( " #{file['path']}#{ file['path'].empty? ? '' : '\\' }#{file['name']}\n" )
145
- end
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
- # Move source to destination
228
- #
229
- def cmd_mv(*args)
230
- if (args.length < 2)
231
- print_line("Usage: mv oldfile newfile")
232
- return true
233
- end
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
- client.fs.file.mv(args[0],args[1])
297
+ alias :cmd_move :cmd_mv
298
+ alias :cmd_rename :cmd_mv
236
299
 
237
- return true
238
- end
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
- alias :cmd_move :cmd_mv
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 "Usage: download [options] src1 src2 src3 ... destination"
316
+ print_line("Usage: download [options] src1 src2 src3 ... destination")
246
317
  print_line
247
- print_line "Downloads remote files and directories to the local machine."
248
- print_line @@download_opts.usage
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
- stat = client.fs.file.stat(src)
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 (stat.directory?)
297
- client.fs.dir.download(dest, src, recursive, true) { |step, src, dst|
298
- print_status("#{step.ljust(11)}: #{src} -> #{dst}")
299
- client.framework.events.on_session_download(client, src, dest) if msf_loaded?
300
- }
301
- elsif (stat.file?)
302
- client.fs.file.download(dest, src) { |step, src, dst|
303
- print_status("#{step.ljust(11)}: #{src} -> #{dst}")
304
- client.framework.events.on_session_download(client, src, dest) if msf_loaded?
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
- return true
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
- begin
328
- # Download the remote file to the temporary file
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
- path = args[0] || client.fs.dir.getwd
367
- tbl = Rex::Ui::Text::Table.new(
368
- 'Header' => "Listing: #{path}",
369
- 'SortIndex' => 4,
370
- 'Columns' =>
371
- [
372
- 'Mode',
373
- 'Size',
374
- 'Type',
375
- 'Last modified',
376
- 'Name',
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
- items = 0
380
- stat = client.fs.file.stat(path)
381
- if stat.directory?
382
- # Enumerate each item...
383
- # No need to sort as Table will do it for us
384
- client.fs.dir.entries_with_info(path).each { |p|
385
-
386
- tbl <<
387
- [
388
- p['StatBuf'] ? p['StatBuf'].prettymode : '',
389
- p['StatBuf'] ? p['StatBuf'].size : '',
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 "Usage: upload [options] src1 src2 src3 ... destination"
646
+ print_line("Usage: upload [options] src1 src2 src3 ... destination")
456
647
  print_line
457
- print_line "Uploads local files and directories to the remote machine."
458
- print_line @@upload_opts.usage
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
  #