Class: Rex::Post::Meterpreter::Ui::Console::CommandDispatcher::Extapi::Adsi
- Inherits:
-
Object
- Object
- Rex::Post::Meterpreter::Ui::Console::CommandDispatcher::Extapi::Adsi
- Defined in:
- lib/rex/post/meterpreter/ui/console/command_dispatcher/extapi/adsi.rb
Overview
Extended API ADSI management user interface.
Constant Summary collapse
- Klass =
Console::CommandDispatcher::Extapi::Adsi
- DEFAULT_MAX_RESULTS =
Zero indicates “no limit”
0
- DEFAULT_PAGE_SIZE =
0
- @@adsi_nested_group_user_enum_opts =
Options for the adsi_nested_group_user_enum command.
Rex::Parser::Arguments.new( '-h' => [false, 'Help banner'], '-o' => [true, 'Path to output file.'], '-m' => [true, 'Maximum results to return.'], '-p' => [true, 'Result set page size.'] )
- @@adsi_user_enum_opts =
Options for the adsi_user_enum command.
Rex::Parser::Arguments.new( '-h' => [false, 'Help banner.'], '-o' => [true, 'Path to output file.'], '-m' => [true, 'Maximum results to return.'], '-p' => [true, 'Result set page size.'] )
- @@adsi_group_enum_opts =
Options for the adsi_group_enum command.
Rex::Parser::Arguments.new( '-h' => [false, 'Help banner.'], '-o' => [true, 'Path to output file.'], '-m' => [true, 'Maximum results to return.'], '-p' => [true, 'Result set page size.'] )
- @@adsi_computer_enum_opts =
Options for the adsi_computer_enum command.
Rex::Parser::Arguments.new( '-h' => [false, 'Help banner.'], '-o' => [true, 'Path to output file.'], '-m' => [true, 'Maximum results to return.'], '-p' => [true, 'Result set page size.'] )
- @@adsi_dc_enum_opts =
Options for the adsi_dc_enum command.
Rex::Parser::Arguments.new( '-h' => [false, 'Help banner.'], '-o' => [true, 'Path to output file.'], '-m' => [true, 'Maximum results to return.'], '-p' => [true, 'Result set page size.'] )
- @@adsi_domain_query_opts =
Options for the adsi_domain_query command.
Rex::Parser::Arguments.new( '-h' => [false, 'Help banner.'], '-o' => [true, 'Path to output file.'], '-m' => [true, 'Maximum results to return.'], '-p' => [true, 'Result set page size.'] )
Constants included from Extensions::Extapi
Extensions::Extapi::COMMAND_ID_EXTAPI_ADSI_DOMAIN_QUERY, Extensions::Extapi::COMMAND_ID_EXTAPI_CLIPBOARD_GET_DATA, Extensions::Extapi::COMMAND_ID_EXTAPI_CLIPBOARD_MONITOR_DUMP, Extensions::Extapi::COMMAND_ID_EXTAPI_CLIPBOARD_MONITOR_PAUSE, Extensions::Extapi::COMMAND_ID_EXTAPI_CLIPBOARD_MONITOR_PURGE, Extensions::Extapi::COMMAND_ID_EXTAPI_CLIPBOARD_MONITOR_RESUME, Extensions::Extapi::COMMAND_ID_EXTAPI_CLIPBOARD_MONITOR_START, Extensions::Extapi::COMMAND_ID_EXTAPI_CLIPBOARD_MONITOR_STOP, Extensions::Extapi::COMMAND_ID_EXTAPI_CLIPBOARD_SET_DATA, Extensions::Extapi::COMMAND_ID_EXTAPI_NTDS_PARSE, Extensions::Extapi::COMMAND_ID_EXTAPI_PAGEANT_SEND_QUERY, Extensions::Extapi::COMMAND_ID_EXTAPI_SERVICE_CONTROL, Extensions::Extapi::COMMAND_ID_EXTAPI_SERVICE_ENUM, Extensions::Extapi::COMMAND_ID_EXTAPI_SERVICE_QUERY, Extensions::Extapi::COMMAND_ID_EXTAPI_WINDOW_ENUM, Extensions::Extapi::COMMAND_ID_EXTAPI_WMI_QUERY, Extensions::Extapi::EXTENSION_ID_EXTAPI, Extensions::Extapi::TLV_TYPE_EXTENSION_EXTAPI, Extensions::Extapi::TLV_TYPE_EXTENSION_PAGEANT_BLOB_IN, Extensions::Extapi::TLV_TYPE_EXTENSION_PAGEANT_ERRORMESSAGE, Extensions::Extapi::TLV_TYPE_EXTENSION_PAGEANT_RETURNEDBLOB, Extensions::Extapi::TLV_TYPE_EXTENSION_PAGEANT_SIZE_IN, Extensions::Extapi::TLV_TYPE_EXTENSION_PAGEANT_STATUS, Extensions::Extapi::TLV_TYPE_EXT_ADSI_ARRAY, Extensions::Extapi::TLV_TYPE_EXT_ADSI_BIGNUMBER, Extensions::Extapi::TLV_TYPE_EXT_ADSI_BOOL, Extensions::Extapi::TLV_TYPE_EXT_ADSI_DN, Extensions::Extapi::TLV_TYPE_EXT_ADSI_DOMAIN, Extensions::Extapi::TLV_TYPE_EXT_ADSI_FIELD, Extensions::Extapi::TLV_TYPE_EXT_ADSI_FILTER, Extensions::Extapi::TLV_TYPE_EXT_ADSI_MAXRESULTS, Extensions::Extapi::TLV_TYPE_EXT_ADSI_NUMBER, Extensions::Extapi::TLV_TYPE_EXT_ADSI_PAGESIZE, Extensions::Extapi::TLV_TYPE_EXT_ADSI_PATH, Extensions::Extapi::TLV_TYPE_EXT_ADSI_PATH_PATH, Extensions::Extapi::TLV_TYPE_EXT_ADSI_PATH_TYPE, Extensions::Extapi::TLV_TYPE_EXT_ADSI_PATH_VOL, Extensions::Extapi::TLV_TYPE_EXT_ADSI_RAW, Extensions::Extapi::TLV_TYPE_EXT_ADSI_RESULT, Extensions::Extapi::TLV_TYPE_EXT_ADSI_STRING, Extensions::Extapi::TLV_TYPE_EXT_CLIPBOARD_DOWNLOAD, Extensions::Extapi::TLV_TYPE_EXT_CLIPBOARD_MON_CAP_IMG_DATA, Extensions::Extapi::TLV_TYPE_EXT_CLIPBOARD_MON_DUMP, Extensions::Extapi::TLV_TYPE_EXT_CLIPBOARD_MON_PURGE, Extensions::Extapi::TLV_TYPE_EXT_CLIPBOARD_MON_WIN_CLASS, Extensions::Extapi::TLV_TYPE_EXT_CLIPBOARD_TYPE_FILE, Extensions::Extapi::TLV_TYPE_EXT_CLIPBOARD_TYPE_FILES, Extensions::Extapi::TLV_TYPE_EXT_CLIPBOARD_TYPE_FILE_NAME, Extensions::Extapi::TLV_TYPE_EXT_CLIPBOARD_TYPE_FILE_SIZE, Extensions::Extapi::TLV_TYPE_EXT_CLIPBOARD_TYPE_IMAGE_JPG, Extensions::Extapi::TLV_TYPE_EXT_CLIPBOARD_TYPE_IMAGE_JPG_DATA, Extensions::Extapi::TLV_TYPE_EXT_CLIPBOARD_TYPE_IMAGE_JPG_DIMX, Extensions::Extapi::TLV_TYPE_EXT_CLIPBOARD_TYPE_IMAGE_JPG_DIMY, Extensions::Extapi::TLV_TYPE_EXT_CLIPBOARD_TYPE_TEXT, Extensions::Extapi::TLV_TYPE_EXT_CLIPBOARD_TYPE_TEXT_CONTENT, Extensions::Extapi::TLV_TYPE_EXT_CLIPBOARD_TYPE_TIMESTAMP, Extensions::Extapi::TLV_TYPE_EXT_SERVICE_CTRL_NAME, Extensions::Extapi::TLV_TYPE_EXT_SERVICE_CTRL_OP, Extensions::Extapi::TLV_TYPE_EXT_SERVICE_ENUM_DISPLAYNAME, Extensions::Extapi::TLV_TYPE_EXT_SERVICE_ENUM_GROUP, Extensions::Extapi::TLV_TYPE_EXT_SERVICE_ENUM_INTERACTIVE, Extensions::Extapi::TLV_TYPE_EXT_SERVICE_ENUM_NAME, Extensions::Extapi::TLV_TYPE_EXT_SERVICE_ENUM_PID, Extensions::Extapi::TLV_TYPE_EXT_SERVICE_ENUM_STATUS, Extensions::Extapi::TLV_TYPE_EXT_SERVICE_QUERY_DACL, Extensions::Extapi::TLV_TYPE_EXT_SERVICE_QUERY_DISPLAYNAME, Extensions::Extapi::TLV_TYPE_EXT_SERVICE_QUERY_INTERACTIVE, Extensions::Extapi::TLV_TYPE_EXT_SERVICE_QUERY_LOADORDERGROUP, Extensions::Extapi::TLV_TYPE_EXT_SERVICE_QUERY_PATH, Extensions::Extapi::TLV_TYPE_EXT_SERVICE_QUERY_STARTNAME, Extensions::Extapi::TLV_TYPE_EXT_SERVICE_QUERY_STARTTYPE, Extensions::Extapi::TLV_TYPE_EXT_SERVICE_QUERY_STATUS, Extensions::Extapi::TLV_TYPE_EXT_WINDOW_ENUM_CLASSNAME, Extensions::Extapi::TLV_TYPE_EXT_WINDOW_ENUM_GROUP, Extensions::Extapi::TLV_TYPE_EXT_WINDOW_ENUM_HANDLE, Extensions::Extapi::TLV_TYPE_EXT_WINDOW_ENUM_INCLUDEUNKNOWN, Extensions::Extapi::TLV_TYPE_EXT_WINDOW_ENUM_PID, Extensions::Extapi::TLV_TYPE_EXT_WINDOW_ENUM_TITLE, Extensions::Extapi::TLV_TYPE_EXT_WMI_DOMAIN, Extensions::Extapi::TLV_TYPE_EXT_WMI_ERROR, Extensions::Extapi::TLV_TYPE_EXT_WMI_FIELD, Extensions::Extapi::TLV_TYPE_EXT_WMI_FIELDS, Extensions::Extapi::TLV_TYPE_EXT_WMI_QUERY, Extensions::Extapi::TLV_TYPE_EXT_WMI_VALUE, Extensions::Extapi::TLV_TYPE_EXT_WMI_VALUES, Extensions::Extapi::TLV_TYPE_NTDS_PATH, Extensions::Extapi::TLV_TYPE_NTDS_TEST
Instance Attribute Summary
Attributes included from Ui::Text::DispatcherShell::CommandDispatcher
Instance Method Summary collapse
- #adsi_computer_enum_usage ⇒ Object
- #adsi_dc_enum_usage ⇒ Object
- #adsi_domain_query_usage ⇒ Object
- #adsi_group_enum_usage ⇒ Object
- #adsi_nested_group_user_enum_usage ⇒ Object
- #adsi_user_enum_usage ⇒ Object
-
#cmd_adsi_computer_enum(*args) ⇒ Object
Enumerate domain computers.
-
#cmd_adsi_dc_enum(*args) ⇒ Object
Enumerate domain dcs.
-
#cmd_adsi_domain_query(*args) ⇒ Object
Enumerate domain objects.
-
#cmd_adsi_group_enum(*args) ⇒ Object
Enumerate domain groups.
-
#cmd_adsi_nested_group_user_enum(*args) ⇒ Object
Enumerate domain groups.
-
#cmd_adsi_user_enum(*args) ⇒ Object
Enumerate domain users.
-
#commands ⇒ Object
List of supported commands.
-
#name ⇒ Object
Name for this dispatcher.
-
#to_table_row(result) ⇒ Array[String]
protected
Convert an ADSI result row to a table row so that it can be rendered to screen appropriately.
Methods included from Rex::Post::Meterpreter::Ui::Console::CommandDispatcher
check_hash, #client, #docs_dir, #filter_commands, #initialize, #log_error, #msf_loaded?, set_hash, #unknown_command
Methods included from Ui::Text::DispatcherShell::CommandDispatcher
#cmd_help, #cmd_help_help, #cmd_help_tabs, #deprecated_cmd, #deprecated_commands, #deprecated_help, #docs_dir, #help_to_s, included, #initialize, #print, #print_error, #print_good, #print_line, #print_status, #print_warning, #tab_complete_directory, #tab_complete_filenames, #tab_complete_generic, #tab_complete_source_address, #unknown_command, #update_prompt
Instance Method Details
#adsi_computer_enum_usage ⇒ Object
188 189 190 191 192 193 194 195 |
# File 'lib/rex/post/meterpreter/ui/console/command_dispatcher/extapi/adsi.rb', line 188 def adsi_computer_enum_usage print_line('USAGE:') print_line(' adsi_computer_enum <domain> [-h] [-m maxresults] [-p pagesize] [-o file]') print_line print_line('DESCRIPTION:') print_line(' Enumerate all computers on the target domain.') print_line(@@adsi_computer_enum_opts.usage) end |
#adsi_dc_enum_usage ⇒ Object
226 227 228 229 230 231 232 233 |
# File 'lib/rex/post/meterpreter/ui/console/command_dispatcher/extapi/adsi.rb', line 226 def adsi_dc_enum_usage print_line('USAGE:') print_line(' adsi_dc_enum <domain> [-h] [-m maxresults] [-p pagesize] [-o file]') print_line print_line('DESCRIPTION:') print_line(' Enumerate the domain controllers on the target domain.') print_line(@@adsi_dc_enum_opts.usage) end |
#adsi_domain_query_usage ⇒ Object
264 265 266 267 268 269 270 271 |
# File 'lib/rex/post/meterpreter/ui/console/command_dispatcher/extapi/adsi.rb', line 264 def adsi_domain_query_usage print_line('USAGE:') print_line(' adsi_domain_query <domain> <filter> <field 1> [field 2 [field ..]] [-h] [-m maxresults] [-p pagesize] [-o file]') print_line print_line('DESCRIPTION:') print_line(' Enumerates the objects on the target domain, returning the set of fields that are specified.') print_line(@@adsi_domain_query_opts.usage) end |
#adsi_group_enum_usage ⇒ Object
148 149 150 151 152 153 154 155 156 157 158 159 |
# File 'lib/rex/post/meterpreter/ui/console/command_dispatcher/extapi/adsi.rb', line 148 def adsi_group_enum_usage print_line('USAGE:') print_line(' adsi_nested_group_user_enum <domain> [-h] [-m maxresults] [-p pagesize] [-o file]') print_line print_line('DESCRIPTION:') print_line(' Enumerate all groups on the target domain.') print_line print_line('EXAMPLE:') print_line(' The example below will list all groups on the STUFUS domain.') print_line(' adsi_group_enum STUFUS') print_line(@@adsi_group_enum_opts.usage) end |
#adsi_nested_group_user_enum_usage ⇒ Object
66 67 68 69 70 71 72 73 74 75 76 77 78 79 |
# File 'lib/rex/post/meterpreter/ui/console/command_dispatcher/extapi/adsi.rb', line 66 def adsi_nested_group_user_enum_usage print_line('USAGE:') print_line(' adsi_nested_group_user_enum <domain> <Group DN> [-h] [-m maxresults] [-p pagesize] [-o file]') print_line print_line('DESCRIPTION:') print_line(' Enumerate the users who are members of the named group, taking nested groups into account.') print_line(' For example, specifying the "Domain Admins" group DN will list all users who are effectively') print_line(' members of the Domain Admins group, even if they are in practice members of intermediary groups.') print_line print_line('EXAMPLE:') print_line(' The example below will list all members of the "Domain Admins" group on the STUFUS domain:') print_line(' adsi_nested_group_user_enum STUFUS "CN=Domain Admins,CN=Users,DC=mwrinfosecurity,DC=com"') print_line(@@adsi_nested_group_user_enum_opts.usage) end |
#adsi_user_enum_usage ⇒ Object
111 112 113 114 115 116 117 118 119 |
# File 'lib/rex/post/meterpreter/ui/console/command_dispatcher/extapi/adsi.rb', line 111 def adsi_user_enum_usage print_line('USAGE:') print_line(' adsi_user_enum <domain> [-h] [-m maxresults] [-p pagesize] [-o file]') print_line print_line('DESCRIPTION:') print_line(' Enumerate all users on the target domain.') print_line(' Enumeration returns information such as the user name, SAM account name, status, comments etc') print_line(@@adsi_user_enum_opts.usage) end |
#cmd_adsi_computer_enum(*args) ⇒ Object
Enumerate domain computers.
200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 |
# File 'lib/rex/post/meterpreter/ui/console/command_dispatcher/extapi/adsi.rb', line 200 def cmd_adsi_computer_enum(*args) args.unshift('-h') if args.length == 0 if args.include?('-h') adsi_computer_enum_usage return true end domain = args.shift filter = '(objectClass=computer)' fields = ['name', 'dnshostname', 'distinguishedname', 'operatingsystem', 'operatingsystemversion', 'operatingsystemservicepack', 'description', 'comment' ] args = [domain, filter] + fields + args return cmd_adsi_domain_query(*args) end |
#cmd_adsi_dc_enum(*args) ⇒ Object
Enumerate domain dcs.
238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 |
# File 'lib/rex/post/meterpreter/ui/console/command_dispatcher/extapi/adsi.rb', line 238 def cmd_adsi_dc_enum(*args) args.unshift('-h') if args.length == 0 if args.include?('-h') adsi_dc_enum_usage return true end domain = args.shift # This LDAP filter will pull out domain controllers filter = '(&(objectCategory=computer)(userAccountControl:1.2.840.113556.1.4.803:=8192))' fields = ['name', 'dnshostname', 'distinguishedname', 'operatingsystem', 'operatingsystemversion', 'operatingsystemservicepack', 'description', 'comment' ] args = [domain, filter] + fields + args return cmd_adsi_domain_query(*args) end |
#cmd_adsi_domain_query(*args) ⇒ Object
Enumerate domain objects.
276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 |
# File 'lib/rex/post/meterpreter/ui/console/command_dispatcher/extapi/adsi.rb', line 276 def cmd_adsi_domain_query(*args) page_size = DEFAULT_PAGE_SIZE max_results = DEFAULT_MAX_RESULTS args.unshift('-h') if args.length < 3 output_file = nil @@adsi_domain_query_opts.parse(args) { |opt, idx, val| case opt when '-p' page_size = val.to_i when '-o' output_file = val when '-m' max_results = val.to_i when '-h' adsi_domain_query_usage return true end } # Assume that the flags are passed in at the end. Safe? switch_index = args.index { |a| a.start_with?('-') } if switch_index args = args.first(switch_index) end domain = args.shift filter = args.shift objects = client.extapi.adsi.domain_query(domain, filter, max_results, page_size, args) table = Rex::Text::Table.new( 'Header' => "#{domain} Objects", 'Indent' => 0, 'SortIndex' => 0, 'Columns' => objects[:fields] ) objects[:results].each do |c| table << to_table_row(c) end print_line print_line(table.to_s) print_line("Total objects: #{objects[:results].length}") print_line if output_file ::File.open(output_file, 'w') do |f| f.write("#{table.to_s}\n") f.write("\nTotal objects: #{objects[:results].length}\n") end end return true end |
#cmd_adsi_group_enum(*args) ⇒ Object
Enumerate domain groups.
164 165 166 167 168 169 170 171 172 173 174 175 176 |
# File 'lib/rex/post/meterpreter/ui/console/command_dispatcher/extapi/adsi.rb', line 164 def cmd_adsi_group_enum(*args) args.unshift('-h') if args.length == 0 if args.include?('-h') adsi_group_enum_usage return true end domain = args.shift filter = '(objectClass=group)' fields = ['name', 'distinguishedname', 'description',] args = [domain, filter] + fields + args return cmd_adsi_domain_query(*args) end |
#cmd_adsi_nested_group_user_enum(*args) ⇒ Object
Enumerate domain groups.
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 |
# File 'lib/rex/post/meterpreter/ui/console/command_dispatcher/extapi/adsi.rb', line 84 def cmd_adsi_nested_group_user_enum(*args) args.unshift('-h') if args.length == 0 if args.include?('-h') || args.length < 2 adsi_nested_group_user_enum_usage return true end domain = args.shift groupdn = args.shift # This OID (canonical name = LDAP_MATCHING_RULE_IN_CHAIN) will recursively search each 'memberof' parent # https://support.microsoft.com/en-us/kb/275523 for more information -stufus filter = "(&(objectClass=user)(memberof:1.2.840.113556.1.4.1941:=#{groupdn}))" fields = ['samaccountname', 'name', 'distinguishedname', 'description', 'comment'] args = [domain, filter] + fields + args return cmd_adsi_domain_query(*args) end |
#cmd_adsi_user_enum(*args) ⇒ Object
Enumerate domain users.
124 125 126 127 128 129 130 131 132 133 134 135 136 |
# File 'lib/rex/post/meterpreter/ui/console/command_dispatcher/extapi/adsi.rb', line 124 def cmd_adsi_user_enum(*args) args.unshift('-h') if args.length == 0 if args.include?('-h') adsi_user_enum_usage return true end domain = args.shift filter = '(objectClass=user)' fields = ['samaccountname', 'name', 'distinguishedname', 'description', 'comment'] args = [domain, filter] + fields + args return cmd_adsi_domain_query(*args) end |
#commands ⇒ Object
List of supported commands.
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
# File 'lib/rex/post/meterpreter/ui/console/command_dispatcher/extapi/adsi.rb', line 29 def commands all = { 'adsi_user_enum' => 'Enumerate all users on the specified domain.', 'adsi_group_enum' => 'Enumerate all groups on the specified domain.', 'adsi_nested_group_user_enum' => 'Recursively enumerate users who are effectively members of the group specified.', 'adsi_computer_enum' => 'Enumerate all computers on the specified domain.', 'adsi_dc_enum' => 'Enumerate all domain controllers on the specified domain.', 'adsi_domain_query' => 'Enumerate all objects on the specified domain that match a filter.' } reqs = { 'adsi_user_enum' => [COMMAND_ID_EXTAPI_ADSI_DOMAIN_QUERY], 'adsi_group_enum' => [COMMAND_ID_EXTAPI_ADSI_DOMAIN_QUERY], 'adsi_nested_group_user_enum' => [COMMAND_ID_EXTAPI_ADSI_DOMAIN_QUERY], 'adsi_computer_enum' => [COMMAND_ID_EXTAPI_ADSI_DOMAIN_QUERY], 'adsi_dc_enum' => [COMMAND_ID_EXTAPI_ADSI_DOMAIN_QUERY], 'adsi_domain_query' => [COMMAND_ID_EXTAPI_ADSI_DOMAIN_QUERY], } filter_commands(all, reqs) end |
#name ⇒ Object
Name for this dispatcher
52 53 54 |
# File 'lib/rex/post/meterpreter/ui/console/command_dispatcher/extapi/adsi.rb', line 52 def name 'Extapi: ADSI Management' end |
#to_table_row(result) ⇒ Array[String] (protected)
Convert an ADSI result row to a table row so that it can
be rendered to screen appropriately.
344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 |
# File 'lib/rex/post/meterpreter/ui/console/command_dispatcher/extapi/adsi.rb', line 344 def to_table_row(result) values = [] result.each do |v| case v[:type] when :string, :number, :bool values << v[:value].to_s when :raw # for UI level stuff, rendering raw as hex is really the only option values << Rex::Text.to_hex(v[:value], '') when :array val = "#{to_table_row(v[:value]).join(', ')}" # we'll truncate the output of the array because it could be excessive if we # don't. Users who want the detail of this stuff should probably script it. if val.length > 50 val = "<#{val[0,50]}..." end values << val when :dn values << "#{value[:label]}: #{value[:string] || Rex::Text.to_hex(value[:raw], '')}" when :path values << "Vol: #{v[:volume]}, Path: #{v[:path]}, Type: #{v[:vol_type]}" when :unknown values << '(unknown)' end end values end |