diff --git a/lib/controller/action.py b/lib/controller/action.py
index 5142404ac..686399136 100644
--- a/lib/controller/action.py
+++ b/lib/controller/action.py
@@ -101,6 +101,10 @@ def action():
dumper.userSettings("database management system users privileges",
conf.dbmsHandler.getPrivileges(), "privilege")
+ if conf.getRoles:
+ dumper.userSettings("database management system users roles",
+ conf.dbmsHandler.getRoles(), "role")
+
if conf.getDbs:
dumper.lister("available databases", conf.dbmsHandler.getDbs())
diff --git a/lib/core/optiondict.py b/lib/core/optiondict.py
index c2e034f90..412b5e740 100644
--- a/lib/core/optiondict.py
+++ b/lib/core/optiondict.py
@@ -87,6 +87,7 @@ optDict = {
"getUsers": "boolean",
"getPasswordHashes": "boolean",
"getPrivileges": "boolean",
+ "getRoles": "boolean",
"getDbs": "boolean",
"getTables": "boolean",
"getColumns": "boolean",
diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py
index 7ec114d27..af0643603 100644
--- a/lib/parse/cmdline.py
+++ b/lib/parse/cmdline.py
@@ -247,6 +247,10 @@ def cmdLineParser():
action="store_true",
help="Enumerate DBMS users privileges")
+ enumeration.add_option("--roles", dest="getRoles",
+ action="store_true",
+ help="Enumerate DBMS users roles")
+
enumeration.add_option("--dbs", dest="getDbs", action="store_true",
help="Enumerate DBMS databases")
diff --git a/lib/parse/queriesfile.py b/lib/parse/queriesfile.py
index 3da921056..98cccc583 100644
--- a/lib/parse/queriesfile.py
+++ b/lib/parse/queriesfile.py
@@ -177,6 +177,14 @@ class queriesHandler(ContentHandler):
self.__queries.privileges = self.__privileges
+ elif name == "roles":
+ self.__roles = {}
+ self.__roles["inband"] = { "query": self.__inband, "query2": self.__inband2, "condition": self.__conditionInband, "condition2": self.__conditionInband2 }
+ self.__roles["blind"] = { "query": self.__blind, "query2": self.__blind2,
+ "count": self.__count, "count2": self.__count2 }
+
+ self.__queries.roles = self.__roles
+
elif name == "dbs":
self.__dbs = {}
self.__dbs["inband"] = { "query": self.__inband, "query2": self.__inband2 }
diff --git a/plugins/dbms/oracle/enumeration.py b/plugins/dbms/oracle/enumeration.py
index 25552d4a8..25b47475c 100644
--- a/plugins/dbms/oracle/enumeration.py
+++ b/plugins/dbms/oracle/enumeration.py
@@ -22,7 +22,12 @@ with sqlmap; if not, write to the Free Software Foundation, Inc., 51
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
"""
+from lib.core.data import conf
+from lib.core.data import kb
from lib.core.data import logger
+from lib.core.data import queries
+from lib.core.exception import sqlmapNoneDataException
+from lib.request import inject
from plugins.generic.enumeration import Enumeration as GenericEnumeration
@@ -30,6 +35,145 @@ class Enumeration(GenericEnumeration):
def __init__(self):
GenericEnumeration.__init__(self, "Oracle")
+ def getRoles(self, query2=False):
+ infoMsg = "fetching database users roles"
+
+ rootQuery = queries[kb.dbms].roles
+
+ if conf.user == "CU":
+ infoMsg += " for current user"
+ conf.user = self.getCurrentUser()
+
+ logger.info(infoMsg)
+
+ # Set containing the list of DBMS administrators
+ areAdmins = set()
+
+ if kb.unionPosition:
+ if query2:
+ query = rootQuery["inband"]["query2"]
+ condition = rootQuery["inband"]["condition2"]
+ else:
+ query = rootQuery["inband"]["query"]
+ condition = rootQuery["inband"]["condition"]
+
+ if conf.user:
+ users = conf.user.split(",")
+ query += " WHERE "
+ query += " OR ".join("%s = '%s'" % (condition, user) for user in users)
+
+ values = inject.getValue(query, blind=False)
+
+ if not values and not query2:
+ infoMsg = "trying with table USER_ROLE_PRIVS"
+ logger.info(infoMsg)
+
+ return self.getRoles(query2=True)
+
+ if values:
+ for value in values:
+ user = None
+ roles = set()
+
+ for count in xrange(0, len(value)):
+ # The first column is always the username
+ if count == 0:
+ user = value[count]
+
+ # The other columns are the roles
+ else:
+ role = value[count]
+
+ # In Oracle we get the list of roles as string
+ roles.add(role)
+
+ if self.__isAdminFromPrivileges(roles):
+ areAdmins.add(user)
+
+ if kb.data.cachedUsersRoles.has_key(user):
+ kb.data.cachedUsersRoles[user].extend(roles)
+ else:
+ kb.data.cachedUsersRoles[user] = list(roles)
+
+ if not kb.data.cachedUsersRoles:
+ conditionChar = "="
+
+ if conf.user:
+ users = conf.user.split(",")
+ else:
+ if not len(kb.data.cachedUsers):
+ users = self.getUsers()
+ else:
+ users = kb.data.cachedUsers
+
+ retrievedUsers = set()
+
+ for user in users:
+ unescapedUser = None
+
+ if user in retrievedUsers:
+ continue
+
+ infoMsg = "fetching number of roles "
+ infoMsg += "for user '%s'" % user
+ logger.info(infoMsg)
+
+ if unescapedUser:
+ queryUser = unescapedUser
+ else:
+ queryUser = user
+
+ if query2:
+ query = rootQuery["blind"]["count2"] % queryUser
+ else:
+ query = rootQuery["blind"]["count"] % queryUser
+ count = inject.getValue(query, inband=False, expected="int", charsetType=2)
+
+ if not count.isdigit() or not len(count) or count == "0":
+ if not count.isdigit() and not query2:
+ infoMsg = "trying with table USER_SYS_PRIVS"
+ logger.info(infoMsg)
+
+ return self.getPrivileges(query2=True)
+
+ warnMsg = "unable to retrieve the number of "
+ warnMsg += "roles for user '%s'" % user
+ logger.warn(warnMsg)
+ continue
+
+ infoMsg = "fetching roles for user '%s'" % user
+ logger.info(infoMsg)
+
+ roles = set()
+
+ indexRange = getRange(count, plusOne=True)
+
+ for index in indexRange:
+ if query2:
+ query = rootQuery["blind"]["query2"] % (queryUser, index)
+ else:
+ query = rootQuery["blind"]["query"] % (queryUser, index)
+ role = inject.getValue(query, inband=False)
+
+ # In Oracle we get the list of roles as string
+ roles.add(role)
+
+ if roles:
+ kb.data.cachedUsersRoles[user] = list(roles)
+ else:
+ warnMsg = "unable to retrieve the roles "
+ warnMsg += "for user '%s'" % user
+ logger.warn(warnMsg)
+
+ retrievedUsers.add(user)
+
+ if not kb.data.cachedUsersRoles:
+ errMsg = "unable to retrieve the roles "
+ errMsg += "for the database users"
+ raise sqlmapNoneDataException, errMsg
+
+ return ( kb.data.cachedUsersRoles, areAdmins )
+
def getDbs(self):
warnMsg = "on Oracle it is not possible to enumerate databases"
logger.warn(warnMsg)
diff --git a/plugins/generic/enumeration.py b/plugins/generic/enumeration.py
index 7d3f4a51a..bd47f563e 100644
--- a/plugins/generic/enumeration.py
+++ b/plugins/generic/enumeration.py
@@ -60,6 +60,7 @@ class Enumeration:
kb.data.cachedUsers = []
kb.data.cachedUsersPasswords = {}
kb.data.cachedUsersPrivileges = {}
+ kb.data.cachedUsersRoles = {}
kb.data.cachedDbs = []
kb.data.cachedTables = {}
kb.data.cachedColumns = {}
@@ -327,9 +328,14 @@ class Enumeration:
# that the user is DBA
dbaCondition |= ( kb.dbms == "MySQL" and not kb.data.has_information_schema and "super_priv" in privileges )
+ # In Firebird there is no specific privilege that means
+ # that the user is DBA
+ # TODO: confirm
+ 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 )
+
return dbaCondition
- def getPrivileges(self):
+ def getPrivileges(self, query2=False):
infoMsg = "fetching database users privileges"
rootQuery = queries[kb.dbms].privileges
@@ -377,7 +383,7 @@ class Enumeration:
( 2, "super" ),
( 3, "catupd" ),
)
-
+
firebirdPrivs = {
"S": "SELECT",
"I": "INSERT",
@@ -391,38 +397,32 @@ class Enumeration:
if kb.dbms == "MySQL" and not kb.data.has_information_schema:
query = rootQuery["inband"]["query2"]
condition = rootQuery["inband"]["condition2"]
+ elif kb.dbms == "Oracle" and query2:
+ query = rootQuery["inband"]["query2"]
+ condition = rootQuery["inband"]["condition2"]
else:
query = rootQuery["inband"]["query"]
condition = rootQuery["inband"]["condition"]
if conf.user:
- if "," in conf.user:
- users = conf.user.split(",")
- query += " WHERE "
- # NOTE: I assume that the user provided is not in
- # MySQL >= 5.0 syntax 'user'@'host'
- if kb.dbms == "MySQL" and kb.data.has_information_schema:
- queryUser = "%" + conf.user + "%"
- query += " OR ".join("%s LIKE '%s'" % (condition, "%" + user + "%") for user in users)
- else:
- query += " OR ".join("%s = '%s'" % (condition, user) for user in users)
+ users = conf.user.split(",")
+ query += " WHERE "
+ # NOTE: I assume that the user provided is not in
+ # MySQL >= 5.0 syntax 'user'@'host'
+ if kb.dbms == "MySQL" and kb.data.has_information_schema:
+ queryUser = "%" + conf.user + "%"
+ query += " OR ".join("%s LIKE '%s'" % (condition, "%" + user + "%") for user in users)
else:
- if kb.dbms == "MySQL":
- parsedUser = re.search("[\047]*(.*?)[\047]*\@", conf.user)
-
- if parsedUser:
- conf.user = parsedUser.groups()[0]
-
- # NOTE: I assume that the user provided is not in
- # MySQL >= 5.0 syntax 'user'@'host'
- if kb.dbms == "MySQL" and kb.data.has_information_schema:
- queryUser = "%" + conf.user + "%"
- query += " WHERE %s LIKE '%s'" % (condition, queryUser)
- else:
- query += " WHERE %s = '%s'" % (condition, conf.user)
+ query += " OR ".join("%s = '%s'" % (condition, user) for user in users)
values = inject.getValue(query, blind=False)
+ if not values and kb.dbms == "Oracle" and not query2:
+ infoMsg = "trying with table USER_SYS_PRIVS"
+ logger.info(infoMsg)
+
+ return self.getPrivileges(query2=True)
+
if values:
for value in values:
user = None
@@ -482,13 +482,8 @@ class Enumeration:
conf.user = parsedUser.groups()[0]
users = [ "%" + conf.user + "%" ]
-
- elif "," in conf.user:
- users = conf.user.split(",")
-
else:
- users = [ conf.user ]
-
+ users = conf.user.split(",")
else:
if not len(kb.data.cachedUsers):
users = self.getUsers()
@@ -519,11 +514,19 @@ class Enumeration:
query = rootQuery["blind"]["count2"] % queryUser
elif kb.dbms == "MySQL" and kb.data.has_information_schema:
query = rootQuery["blind"]["count"] % (conditionChar, queryUser)
+ elif kb.dbms == "Oracle" and query2:
+ query = rootQuery["blind"]["count2"] % queryUser
else:
query = rootQuery["blind"]["count"] % queryUser
count = inject.getValue(query, inband=False, expected="int", charsetType=2)
if not count.isdigit() or not len(count) or count == "0":
+ if not count.isdigit() and kb.dbms == "Oracle" and not query2:
+ infoMsg = "trying with table USER_SYS_PRIVS"
+ logger.info(infoMsg)
+
+ return self.getPrivileges(query2=True)
+
warnMsg = "unable to retrieve the number of "
warnMsg += "privileges for user '%s'" % user
logger.warn(warnMsg)
@@ -545,6 +548,8 @@ class Enumeration:
query = rootQuery["blind"]["query2"] % (queryUser, index)
elif kb.dbms == "MySQL" and kb.data.has_information_schema:
query = rootQuery["blind"]["query"] % (conditionChar, queryUser, index)
+ elif kb.dbms == "Oracle" and query2:
+ query = rootQuery["blind"]["query2"] % (queryUser, index)
elif kb.dbms == "Firebird":
query = rootQuery["blind"]["query"] % (index, queryUser)
else:
@@ -585,6 +590,8 @@ class Enumeration:
privileges.add(mysqlPriv)
i += 1
+
+ # In Firebird we get one letter for each privilege
elif kb.dbms == "Firebird":
privileges.add(firebirdPrivs[privilege.strip()])
@@ -613,6 +620,11 @@ class Enumeration:
return ( kb.data.cachedUsersPrivileges, areAdmins )
+ def getRoles(self, query2=False):
+ warnMsg = "on %s the concept of roles does not " % kb.dbms
+ warnMsg += "exist. sqlmap will enumerate privileges instead"
+ self.getPrivileges(query2)
+
def getDbs(self):
if kb.dbms == "MySQL" and not kb.data.has_information_schema:
warnMsg = "information_schema not available, "
diff --git a/sqlmap.conf b/sqlmap.conf
index 9e1466447..09c38a99d 100644
--- a/sqlmap.conf
+++ b/sqlmap.conf
@@ -248,6 +248,10 @@ getPasswordHashes = False
# Valid: True or False
getPrivileges = False
+# Enumerate back-end database management system users roles.
+# Valid: True or False
+getRoles = False
+
# Enumerate back-end database management system databases.
# Valid: True or False
getDbs = False
diff --git a/xml/queries.xml b/xml/queries.xml
index 1b476aa51..9a7b25352 100644
--- a/xml/queries.xml
+++ b/xml/queries.xml
@@ -42,6 +42,7 @@
+
@@ -83,9 +84,13 @@
-
+
-
+
+
@@ -94,10 +99,22 @@
+
-
-
+
+
+
+
+
+
+
@@ -160,6 +177,7 @@
+
@@ -214,6 +232,7 @@
+
@@ -265,6 +284,7 @@
+
@@ -339,6 +359,7 @@
+