resume of brute forced data is now available

This commit is contained in:
Miroslav Stampar 2010-12-27 14:17:20 +00:00
parent c7a160bf72
commit 9fb0e0fc85
5 changed files with 101 additions and 11 deletions

View File

@ -1128,6 +1128,10 @@ def __setKnowledgeBaseAttributes(flushAll=True):
kb.authHeader = None kb.authHeader = None
kb.bannerFp = advancedDict() kb.bannerFp = advancedDict()
kb.brute = advancedDict()
kb.brute.tables = []
kb.brute.columns = []
kb.cache = advancedDict() kb.cache = advancedDict()
kb.cache.content = {} kb.cache.content = {}
kb.cache.regex = {} kb.cache.regex = {}

View File

@ -21,6 +21,7 @@ from lib.core.data import logger
from lib.core.datatype import injectionDict from lib.core.datatype import injectionDict
from lib.core.enums import PAYLOAD from lib.core.enums import PAYLOAD
from lib.core.enums import PLACE from lib.core.enums import PLACE
from lib.core.settings import METADB_SUFFIX
from lib.core.settings import MSSQL_ALIASES from lib.core.settings import MSSQL_ALIASES
from lib.core.settings import MYSQL_ALIASES from lib.core.settings import MYSQL_ALIASES
from lib.core.settings import PGSQL_ALIASES from lib.core.settings import PGSQL_ALIASES
@ -357,6 +358,35 @@ def resumeConfKb(expression, url, value):
else: else:
conf.os = os conf.os = os
elif expression == "TABLE_EXISTS" and url == conf.url:
table = unSafeFormatString(value[:-1])
if '.' in table:
db, table = table.split('.')
else:
db = "%s%s" % (kb.dbms, METADB_SUFFIX)
logMsg = "resuming brute forced table name "
logMsg += "'%s' from session file" % table
logger.info(logMsg)
kb.brute.tables.append((db, table))
elif expression == "COLUMN_EXISTS" and url == conf.url:
table, column = unSafeFormatString(value[:-1]).split('..')
colName, colType = column.split(' ')
if '.' in table:
db, table = table.split('.')
else:
db = "%s%s" % (kb.dbms, METADB_SUFFIX)
logMsg = "resuming brute forced column name "
logMsg += "'%s' for table '%s' from session file" % (colName, table)
logger.info(logMsg)
kb.brute.columns.append((db, table, colName, colType))
elif expression == "Union comment" and url == conf.url: elif expression == "Union comment" and url == conf.url:
kb.unionComment = unSafeFormatString(value[:-1]) kb.unionComment = unSafeFormatString(value[:-1])

View File

@ -403,6 +403,7 @@ def getValue(expression, blind=True, inband=True, error=True, time=True, fromUse
query = expandAsteriskForColumns(query) query = expandAsteriskForColumns(query)
value = None value = None
found = False found = False
if query and not 'COUNT(*)' in query:
query = query.replace("DISTINCT ", "") query = query.replace("DISTINCT ", "")
count = 0 count = 0

View File

@ -11,6 +11,7 @@ import threading
import time import time
from lib.core.common import clearConsoleLine from lib.core.common import clearConsoleLine
from lib.core.common import dataToSessionFile
from lib.core.common import dataToStdout from lib.core.common import dataToStdout
from lib.core.common import filterListValue from lib.core.common import filterListValue
from lib.core.common import getFileItems from lib.core.common import getFileItems
@ -26,6 +27,7 @@ from lib.core.enums import DBMS
from lib.core.exception import sqlmapMissingMandatoryOptionException from lib.core.exception import sqlmapMissingMandatoryOptionException
from lib.core.exception import sqlmapThreadException from lib.core.exception import sqlmapThreadException
from lib.core.settings import METADB_SUFFIX from lib.core.settings import METADB_SUFFIX
from lib.core.session import safeFormatString
from lib.request import inject from lib.request import inject
def tableExists(tableFile, regex=None): def tableExists(tableFile, regex=None):
@ -59,13 +61,19 @@ def tableExists(tableFile, regex=None):
tbllock.release() tbllock.release()
if conf.db and not conf.db.endswith(METADB_SUFFIX): if conf.db and not conf.db.endswith(METADB_SUFFIX):
table = "%s.%s" % (conf.db, table) fullTableName = "%s.%s" % (conf.db, table)
result = inject.checkBooleanExpression("%s" % safeStringFormat("EXISTS(SELECT %d FROM %s)", (randomInt(1), table))) else:
fullTableName = table
result = inject.checkBooleanExpression("%s" % safeStringFormat("EXISTS(SELECT %d FROM %s)", (randomInt(1), fullTableName)))
iolock.acquire() iolock.acquire()
if result: if result:
retVal.append(table) retVal.append(table)
dataToSessionFile("[%s][%s][%s][TABLE_EXISTS][%s]\n" % (conf.url,\
kb.injection.place, safeFormatString(conf.parameters[kb.injection.place]),\
safeFormatString(fullTableName)))
if conf.verbose in (1, 2): if conf.verbose in (1, 2):
clearConsoleLine(True) clearConsoleLine(True)
infoMsg = "\r[%s] [INFO] retrieved: %s\n" % (time.strftime("%X"), table) infoMsg = "\r[%s] [INFO] retrieved: %s\n" % (time.strftime("%X"), table)
@ -227,13 +235,17 @@ def columnExists(columnFile, regex=None):
columns = {} columns = {}
for column in retVal: for column in retVal:
result = inject.checkBooleanExpression("%s" % safeStringFormat("EXISTS(SELECT %s FROM %s WHERE RND(%s)>0)", (column, table, column))) result = inject.checkBooleanExpression("%s" % safeStringFormat("EXISTS(SELECT %s FROM %s WHERE ROUND(%s)>0)", (column, table, column)))
if result: if result:
columns[column] = 'numeric' columns[column] = 'numeric'
else: else:
columns[column] = 'non-numeric' columns[column] = 'non-numeric'
dataToSessionFile("[%s][%s][%s][COLUMN_EXISTS][%s..%s %s]\n" % (conf.url, kb.injection.place,\
safeFormatString(conf.parameters[kb.injection.place]), safeFormatString(table),\
safeFormatString(column), safeFormatString(columns[column])))
kb.data.cachedColumns[conf.db] = {conf.tbl: columns} kb.data.cachedColumns[conf.db] = {conf.tbl: columns}
return kb.data.cachedColumns return kb.data.cachedColumns

View File

@ -725,6 +725,8 @@ class Enumeration:
def getTables(self): def getTables(self):
bruteForce = False bruteForce = False
self.forceDbmsEnum()
if kb.dbms == DBMS.MYSQL and not kb.data.has_information_schema: if kb.dbms == DBMS.MYSQL and not kb.data.has_information_schema:
errMsg = "information_schema not available, " errMsg = "information_schema not available, "
errMsg += "back-end DBMS is MySQL < 5.0" errMsg += "back-end DBMS is MySQL < 5.0"
@ -738,6 +740,22 @@ class Enumeration:
bruteForce = True bruteForce = True
if bruteForce: if bruteForce:
resumeAvailable = False
for db, table in kb.brute.tables:
if db == conf.db:
resumeAvailable = True
break
if resumeAvailable:
for db, table in kb.brute.tables:
if db == conf.db:
if not kb.data.cachedTables.has_key(conf.db):
kb.data.cachedTables[conf.db] = [table]
else:
kb.data.cachedTables[conf.db].append(table)
return kb.data.cachedTables
message = "do you want to use common table existance check? [Y/n/q]" message = "do you want to use common table existance check? [Y/n/q]"
test = readInput(message, default="Y") test = readInput(message, default="Y")
@ -748,8 +766,6 @@ class Enumeration:
else: else:
return tableExists(paths.COMMON_TABLES) return tableExists(paths.COMMON_TABLES)
self.forceDbmsEnum()
infoMsg = "fetching tables" infoMsg = "fetching tables"
if conf.db: if conf.db:
infoMsg += " for database '%s'" % conf.db infoMsg += " for database '%s'" % conf.db
@ -869,6 +885,11 @@ class Enumeration:
def getColumns(self, onlyColNames=False): def getColumns(self, onlyColNames=False):
bruteForce = False bruteForce = False
if "." in conf.tbl:
conf.db, conf.tbl = conf.tbl.split(".")
self.forceDbmsEnum()
if kb.dbms == DBMS.MYSQL and not kb.data.has_information_schema: if kb.dbms == DBMS.MYSQL and not kb.data.has_information_schema:
errMsg = "information_schema not available, " errMsg = "information_schema not available, "
errMsg += "back-end DBMS is MySQL < 5.0" errMsg += "back-end DBMS is MySQL < 5.0"
@ -882,6 +903,21 @@ class Enumeration:
bruteForce = True bruteForce = True
if bruteForce: if bruteForce:
resumeAvailable = False
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}
return kb.data.cachedColumns
message = "do you want to use common columns existance check? [Y/n/q]" message = "do you want to use common columns existance check? [Y/n/q]"
test = readInput(message, default="Y") test = readInput(message, default="Y")
@ -896,11 +932,6 @@ class Enumeration:
errMsg = "missing table parameter" errMsg = "missing table parameter"
raise sqlmapMissingMandatoryOptionException, errMsg raise sqlmapMissingMandatoryOptionException, errMsg
if "." in conf.tbl:
conf.db, conf.tbl = conf.tbl.split(".")
self.forceDbmsEnum()
if not conf.db: if not conf.db:
warnMsg = "missing database parameter, sqlmap is going to " warnMsg = "missing database parameter, sqlmap is going to "
warnMsg += "use the current database to enumerate table " warnMsg += "use the current database to enumerate table "
@ -1219,6 +1250,7 @@ class Enumeration:
if kb.dbms == DBMS.ACCESS: if kb.dbms == DBMS.ACCESS:
validColumnList = False validColumnList = False
validPivotValue = False
for column in colList: for column in colList:
infoMsg = "fetching number of distinct " infoMsg = "fetching number of distinct "
@ -1235,6 +1267,8 @@ class Enumeration:
infoMsg += "for retrieving row data" infoMsg += "for retrieving row data"
logger.info(infoMsg) logger.info(infoMsg)
validPivotValue = True
colList.remove(column) colList.remove(column)
colList.insert(0, column) colList.insert(0, column)
break break
@ -1243,8 +1277,16 @@ class Enumeration:
errMsg = "all column name(s) provided are non-existent" errMsg = "all column name(s) provided are non-existent"
raise sqlmapNoneDataException, errMsg raise sqlmapNoneDataException, errMsg
if not validPivotValue:
warnMsg = "no proper pivot column provided (with unique values)."
warnMsg += " all row data won't be retrieved."
logger.warn(warnMsg)
pivotValue = " " pivotValue = " "
breakRetrieval = False
for index in indexRange: for index in indexRange:
if breakRetrieval:
break
for column in colList: for column in colList:
if column not in lengths: if column not in lengths:
lengths[column] = 0 lengths[column] = 0
@ -1264,6 +1306,7 @@ class Enumeration:
value = inject.getValue(query, inband=False) value = inject.getValue(query, inband=False)
if column == colList[0]: if column == colList[0]:
if not value: if not value:
breakRetrieval = True
break break
else: else:
pivotValue = value pivotValue = value