rex 2.0.8 → 2.0.9

Sign up to get free protection for your applications and to get access to all the features.
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
  #