Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 2aadc5c

Browse files
committed
Added support for --roles (for Oracle ROLE_PRIVS). Enhanced Oracle --privileges to fall-back to USER_SYS_PRIVS if DBA_SYS_PRIVS is not accessible (so session user is not DBA) - Fixes ticket #180.
Minor enhancement to Firebird to determine if a DB user is a DBA. Minor code refactoring.
1 parent f4f6821 commit 2aadc5c

8 files changed

Lines changed: 232 additions & 34 deletions

File tree

lib/controller/action.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,10 @@ def action():
101101
dumper.userSettings("database management system users privileges",
102102
conf.dbmsHandler.getPrivileges(), "privilege")
103103

104+
if conf.getRoles:
105+
dumper.userSettings("database management system users roles",
106+
conf.dbmsHandler.getRoles(), "role")
107+
104108
if conf.getDbs:
105109
dumper.lister("available databases", conf.dbmsHandler.getDbs())
106110

lib/core/optiondict.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@
8787
"getUsers": "boolean",
8888
"getPasswordHashes": "boolean",
8989
"getPrivileges": "boolean",
90+
"getRoles": "boolean",
9091
"getDbs": "boolean",
9192
"getTables": "boolean",
9293
"getColumns": "boolean",

lib/parse/cmdline.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,10 @@ def cmdLineParser():
247247
action="store_true",
248248
help="Enumerate DBMS users privileges")
249249

250+
enumeration.add_option("--roles", dest="getRoles",
251+
action="store_true",
252+
help="Enumerate DBMS users roles")
253+
250254
enumeration.add_option("--dbs", dest="getDbs", action="store_true",
251255
help="Enumerate DBMS databases")
252256

lib/parse/queriesfile.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,14 @@ def endElement(self, name):
177177

178178
self.__queries.privileges = self.__privileges
179179

180+
elif name == "roles":
181+
self.__roles = {}
182+
self.__roles["inband"] = { "query": self.__inband, "query2": self.__inband2, "condition": self.__conditionInband, "condition2": self.__conditionInband2 }
183+
self.__roles["blind"] = { "query": self.__blind, "query2": self.__blind2,
184+
"count": self.__count, "count2": self.__count2 }
185+
186+
self.__queries.roles = self.__roles
187+
180188
elif name == "dbs":
181189
self.__dbs = {}
182190
self.__dbs["inband"] = { "query": self.__inband, "query2": self.__inband2 }

plugins/dbms/oracle/enumeration.py

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,158 @@
2222
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
2323
"""
2424

25+
from lib.core.data import conf
26+
from lib.core.data import kb
2527
from lib.core.data import logger
28+
from lib.core.data import queries
29+
from lib.core.exception import sqlmapNoneDataException
30+
from lib.request import inject
2631

2732
from plugins.generic.enumeration import Enumeration as GenericEnumeration
2833

2934
class Enumeration(GenericEnumeration):
3035
def __init__(self):
3136
GenericEnumeration.__init__(self, "Oracle")
3237

38+
def getRoles(self, query2=False):
39+
infoMsg = "fetching database users roles"
40+
41+
rootQuery = queries[kb.dbms].roles
42+
43+
if conf.user == "CU":
44+
infoMsg += " for current user"
45+
conf.user = self.getCurrentUser()
46+
47+
logger.info(infoMsg)
48+
49+
# Set containing the list of DBMS administrators
50+
areAdmins = set()
51+
52+
if kb.unionPosition:
53+
if query2:
54+
query = rootQuery["inband"]["query2"]
55+
condition = rootQuery["inband"]["condition2"]
56+
else:
57+
query = rootQuery["inband"]["query"]
58+
condition = rootQuery["inband"]["condition"]
59+
60+
if conf.user:
61+
users = conf.user.split(",")
62+
query += " WHERE "
63+
query += " OR ".join("%s = '%s'" % (condition, user) for user in users)
64+
65+
values = inject.getValue(query, blind=False)
66+
67+
if not values and not query2:
68+
infoMsg = "trying with table USER_ROLE_PRIVS"
69+
logger.info(infoMsg)
70+
71+
return self.getRoles(query2=True)
72+
73+
if values:
74+
for value in values:
75+
user = None
76+
roles = set()
77+
78+
for count in xrange(0, len(value)):
79+
# The first column is always the username
80+
if count == 0:
81+
user = value[count]
82+
83+
# The other columns are the roles
84+
else:
85+
role = value[count]
86+
87+
# In Oracle we get the list of roles as string
88+
roles.add(role)
89+
90+
if self.__isAdminFromPrivileges(roles):
91+
areAdmins.add(user)
92+
93+
if kb.data.cachedUsersRoles.has_key(user):
94+
kb.data.cachedUsersRoles[user].extend(roles)
95+
else:
96+
kb.data.cachedUsersRoles[user] = list(roles)
97+
98+
if not kb.data.cachedUsersRoles:
99+
conditionChar = "="
100+
101+
if conf.user:
102+
users = conf.user.split(",")
103+
else:
104+
if not len(kb.data.cachedUsers):
105+
users = self.getUsers()
106+
else:
107+
users = kb.data.cachedUsers
108+
109+
retrievedUsers = set()
110+
111+
for user in users:
112+
unescapedUser = None
113+
114+
if user in retrievedUsers:
115+
continue
116+
117+
infoMsg = "fetching number of roles "
118+
infoMsg += "for user '%s'" % user
119+
logger.info(infoMsg)
120+
121+
if unescapedUser:
122+
queryUser = unescapedUser
123+
else:
124+
queryUser = user
125+
126+
if query2:
127+
query = rootQuery["blind"]["count2"] % queryUser
128+
else:
129+
query = rootQuery["blind"]["count"] % queryUser
130+
count = inject.getValue(query, inband=False, expected="int", charsetType=2)
131+
132+
if not count.isdigit() or not len(count) or count == "0":
133+
if not count.isdigit() and not query2:
134+
infoMsg = "trying with table USER_SYS_PRIVS"
135+
logger.info(infoMsg)
136+
137+
return self.getPrivileges(query2=True)
138+
139+
warnMsg = "unable to retrieve the number of "
140+
warnMsg += "roles for user '%s'" % user
141+
logger.warn(warnMsg)
142+
continue
143+
144+
infoMsg = "fetching roles for user '%s'" % user
145+
logger.info(infoMsg)
146+
147+
roles = set()
148+
149+
indexRange = getRange(count, plusOne=True)
150+
151+
for index in indexRange:
152+
if query2:
153+
query = rootQuery["blind"]["query2"] % (queryUser, index)
154+
else:
155+
query = rootQuery["blind"]["query"] % (queryUser, index)
156+
role = inject.getValue(query, inband=False)
157+
158+
# In Oracle we get the list of roles as string
159+
roles.add(role)
160+
161+
if roles:
162+
kb.data.cachedUsersRoles[user] = list(roles)
163+
else:
164+
warnMsg = "unable to retrieve the roles "
165+
warnMsg += "for user '%s'" % user
166+
logger.warn(warnMsg)
167+
168+
retrievedUsers.add(user)
169+
170+
if not kb.data.cachedUsersRoles:
171+
errMsg = "unable to retrieve the roles "
172+
errMsg += "for the database users"
173+
raise sqlmapNoneDataException, errMsg
174+
175+
return ( kb.data.cachedUsersRoles, areAdmins )
176+
33177
def getDbs(self):
34178
warnMsg = "on Oracle it is not possible to enumerate databases"
35179
logger.warn(warnMsg)

plugins/generic/enumeration.py

Lines changed: 42 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ def __init__(self, dbms):
6060
kb.data.cachedUsers = []
6161
kb.data.cachedUsersPasswords = {}
6262
kb.data.cachedUsersPrivileges = {}
63+
kb.data.cachedUsersRoles = {}
6364
kb.data.cachedDbs = []
6465
kb.data.cachedTables = {}
6566
kb.data.cachedColumns = {}
@@ -327,9 +328,14 @@ def __isAdminFromPrivileges(self, privileges):
327328
# that the user is DBA
328329
dbaCondition |= ( kb.dbms == "MySQL" and not kb.data.has_information_schema and "super_priv" in privileges )
329330

331+
# In Firebird there is no specific privilege that means
332+
# that the user is DBA
333+
# TODO: confirm
334+
dbaCondition |= ( kb.dbms == "Firebird" and "SELECT" in privileges and "INSERT" in privileges and "UPDATE" in privileges and "DELETE" in privileges and "REFERENCES" in privileges and "EXECUTE" in privileges )
335+
330336
return dbaCondition
331337

332-
def getPrivileges(self):
338+
def getPrivileges(self, query2=False):
333339
infoMsg = "fetching database users privileges"
334340

335341
rootQuery = queries[kb.dbms].privileges
@@ -377,7 +383,7 @@ def getPrivileges(self):
377383
( 2, "super" ),
378384
( 3, "catupd" ),
379385
)
380-
386+
381387
firebirdPrivs = {
382388
"S": "SELECT",
383389
"I": "INSERT",
@@ -391,37 +397,31 @@ def getPrivileges(self):
391397
if kb.dbms == "MySQL" and not kb.data.has_information_schema:
392398
query = rootQuery["inband"]["query2"]
393399
condition = rootQuery["inband"]["condition2"]
400+
elif kb.dbms == "Oracle" and query2:
401+
query = rootQuery["inband"]["query2"]
402+
condition = rootQuery["inband"]["condition2"]
394403
else:
395404
query = rootQuery["inband"]["query"]
396405
condition = rootQuery["inband"]["condition"]
397406

398407
if conf.user:
399-
if "," in conf.user:
400-
users = conf.user.split(",")
401-
query += " WHERE "
402-
# NOTE: I assume that the user provided is not in
403-
# MySQL >= 5.0 syntax 'user'@'host'
404-
if kb.dbms == "MySQL" and kb.data.has_information_schema:
405-
queryUser = "%" + conf.user + "%"
406-
query += " OR ".join("%s LIKE '%s'" % (condition, "%" + user + "%") for user in users)
407-
else:
408-
query += " OR ".join("%s = '%s'" % (condition, user) for user in users)
408+
users = conf.user.split(",")
409+
query += " WHERE "
410+
# NOTE: I assume that the user provided is not in
411+
# MySQL >= 5.0 syntax 'user'@'host'
412+
if kb.dbms == "MySQL" and kb.data.has_information_schema:
413+
queryUser = "%" + conf.user + "%"
414+
query += " OR ".join("%s LIKE '%s'" % (condition, "%" + user + "%") for user in users)
409415
else:
410-
if kb.dbms == "MySQL":
411-
parsedUser = re.search("[\047]*(.*?)[\047]*\@", conf.user)
416+
query += " OR ".join("%s = '%s'" % (condition, user) for user in users)
412417

413-
if parsedUser:
414-
conf.user = parsedUser.groups()[0]
418+
values = inject.getValue(query, blind=False)
415419

416-
# NOTE: I assume that the user provided is not in
417-
# MySQL >= 5.0 syntax 'user'@'host'
418-
if kb.dbms == "MySQL" and kb.data.has_information_schema:
419-
queryUser = "%" + conf.user + "%"
420-
query += " WHERE %s LIKE '%s'" % (condition, queryUser)
421-
else:
422-
query += " WHERE %s = '%s'" % (condition, conf.user)
420+
if not values and kb.dbms == "Oracle" and not query2:
421+
infoMsg = "trying with table USER_SYS_PRIVS"
422+
logger.info(infoMsg)
423423

424-
values = inject.getValue(query, blind=False)
424+
return self.getPrivileges(query2=True)
425425

426426
if values:
427427
for value in values:
@@ -482,13 +482,8 @@ def getPrivileges(self):
482482
conf.user = parsedUser.groups()[0]
483483

484484
users = [ "%" + conf.user + "%" ]
485-
486-
elif "," in conf.user:
487-
users = conf.user.split(",")
488-
489485
else:
490-
users = [ conf.user ]
491-
486+
users = conf.user.split(",")
492487
else:
493488
if not len(kb.data.cachedUsers):
494489
users = self.getUsers()
@@ -519,11 +514,19 @@ def getPrivileges(self):
519514
query = rootQuery["blind"]["count2"] % queryUser
520515
elif kb.dbms == "MySQL" and kb.data.has_information_schema:
521516
query = rootQuery["blind"]["count"] % (conditionChar, queryUser)
517+
elif kb.dbms == "Oracle" and query2:
518+
query = rootQuery["blind"]["count2"] % queryUser
522519
else:
523520
query = rootQuery["blind"]["count"] % queryUser
524521
count = inject.getValue(query, inband=False, expected="int", charsetType=2)
525522

526523
if not count.isdigit() or not len(count) or count == "0":
524+
if not count.isdigit() and kb.dbms == "Oracle" and not query2:
525+
infoMsg = "trying with table USER_SYS_PRIVS"
526+
logger.info(infoMsg)
527+
528+
return self.getPrivileges(query2=True)
529+
527530
warnMsg = "unable to retrieve the number of "
528531
warnMsg += "privileges for user '%s'" % user
529532
logger.warn(warnMsg)
@@ -545,6 +548,8 @@ def getPrivileges(self):
545548
query = rootQuery["blind"]["query2"] % (queryUser, index)
546549
elif kb.dbms == "MySQL" and kb.data.has_information_schema:
547550
query = rootQuery["blind"]["query"] % (conditionChar, queryUser, index)
551+
elif kb.dbms == "Oracle" and query2:
552+
query = rootQuery["blind"]["query2"] % (queryUser, index)
548553
elif kb.dbms == "Firebird":
549554
query = rootQuery["blind"]["query"] % (index, queryUser)
550555
else:
@@ -585,6 +590,8 @@ def getPrivileges(self):
585590
privileges.add(mysqlPriv)
586591

587592
i += 1
593+
594+
# In Firebird we get one letter for each privilege
588595
elif kb.dbms == "Firebird":
589596
privileges.add(firebirdPrivs[privilege.strip()])
590597

@@ -613,6 +620,11 @@ def getPrivileges(self):
613620

614621
return ( kb.data.cachedUsersPrivileges, areAdmins )
615622

623+
def getRoles(self, query2=False):
624+
warnMsg = "on %s the concept of roles does not " % kb.dbms
625+
warnMsg += "exist. sqlmap will enumerate privileges instead"
626+
self.getPrivileges(query2)
627+
616628
def getDbs(self):
617629
if kb.dbms == "MySQL" and not kb.data.has_information_schema:
618630
warnMsg = "information_schema not available, "

sqlmap.conf

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,10 @@ getPasswordHashes = False
248248
# Valid: True or False
249249
getPrivileges = False
250250

251+
# Enumerate back-end database management system users roles.
252+
# Valid: True or False
253+
getRoles = False
254+
251255
# Enumerate back-end database management system databases.
252256
# Valid: True or False
253257
getDbs = False

0 commit comments

Comments
 (0)