This commit is contained in:
Miroslav Stampar 2019-06-27 17:28:43 +02:00
parent c938d77be9
commit aa9b5e4e0c
20 changed files with 1790 additions and 34 deletions

1641
data/txt/common-files.txt Normal file

File diff suppressed because it is too large Load Diff

View File

@ -17,6 +17,7 @@ from lib.core.exception import SqlmapNoneDataException
from lib.core.exception import SqlmapUnsupportedDBMSException
from lib.core.settings import SUPPORTED_DBMS
from lib.utils.brute import columnExists
from lib.utils.brute import fileExists
from lib.utils.brute import tableExists
def action():
@ -199,6 +200,14 @@ def action():
if conf.fileWrite:
conf.dbmsHandler.writeFile(conf.fileWrite, conf.fileDest, conf.fileWriteType)
if conf.commonFiles:
try:
conf.dumper.rFile(fileExists(paths.COMMON_FILES))
except SqlmapNoneDataException as ex:
logger.critical(ex)
except:
raise
# Operating system options
if conf.osCmd:
conf.dbmsHandler.osCmd()

View File

@ -1346,6 +1346,7 @@ def setPaths(rootPath):
# sqlmap files
paths.COMMON_COLUMNS = os.path.join(paths.SQLMAP_TXT_PATH, "common-columns.txt")
paths.COMMON_FILES = os.path.join(paths.SQLMAP_TXT_PATH, "common-files.txt")
paths.COMMON_TABLES = os.path.join(paths.SQLMAP_TXT_PATH, "common-tables.txt")
paths.COMMON_OUTPUTS = os.path.join(paths.SQLMAP_TXT_PATH, 'common-outputs.txt')
paths.SQL_KEYWORDS = os.path.join(paths.SQLMAP_TXT_PATH, "keywords.txt")
@ -4637,6 +4638,8 @@ def decodeDbmsHexValue(value, raw=False):
def _(value):
retVal = value
if value and isinstance(value, six.string_types):
value = value.strip()
if len(value) % 2 != 0:
retVal = (decodeHex(value[:-1]) + b'?') if len(value) > 1 else value
singleTimeWarnMessage("there was a problem decoding value '%s' from expected hexadecimal form" % value)

View File

@ -160,6 +160,7 @@ optDict = {
"Brute": {
"commonTables": "boolean",
"commonColumns": "boolean",
"commonFiles": "boolean",
},
"User-defined function": {

View File

@ -18,7 +18,7 @@ from lib.core.enums import OS
from thirdparty.six import unichr as _unichr
# sqlmap version (<major>.<minor>.<month>.<monthly commit>)
VERSION = "1.3.6.56"
VERSION = "1.3.6.57"
TYPE = "dev" if VERSION.count('.') > 2 and VERSION.split('.')[-1] != '0' else "stable"
TYPE_COLORS = {"dev": 33, "stable": 90, "pip": 34}
VERSION_STRING = "sqlmap/%s#%s" % ('.'.join(VERSION.split('.')[:-1]) if VERSION.count('.') > 2 and VERSION.split('.')[-1] == '0' else VERSION, TYPE)

View File

@ -586,7 +586,7 @@ def _createFilesDir():
Create the file directory.
"""
if not conf.fileRead:
if not any((conf.fileRead, conf.commonFiles)):
return
conf.filePath = paths.SQLMAP_FILES_PATH % conf.hostname

View File

@ -204,7 +204,6 @@ def runThreads(numThreads, threadFunction, cleanupFunction=None, forwardExceptio
traceback.print_exc()
finally:
kb.bruteMode = False
kb.threadContinue = True
kb.threadException = False

View File

@ -502,6 +502,9 @@ def cmdLineParser(argv=None):
brute.add_argument("--common-columns", dest="commonColumns", action="store_true",
help="Check existence of common columns")
brute.add_argument("--common-files", dest="commonFiles", action="store_true",
help="Check existence of common files")
# User-defined function options
udf = parser.add_argument_group("User-defined function injection", "These options can be used to create custom user-defined functions")

View File

@ -109,7 +109,7 @@ class UDF(object):
return output
def udfCheckNeeded(self):
if (not conf.fileRead or (conf.fileRead and not Backend.isDbms(DBMS.PGSQL))) and "sys_fileread" in self.sysUdfs:
if (not any((conf.fileRead, conf.commonFiles)) or (any((conf.fileRead, conf.commonFiles)) and not Backend.isDbms(DBMS.PGSQL))) and "sys_fileread" in self.sysUdfs:
self.sysUdfs.pop("sys_fileread")
if not conf.osPwn:

View File

@ -43,6 +43,7 @@ from lib.core.dicts import FROM_DUMMY_TABLE
from lib.core.enums import DBMS
from lib.core.enums import HASHDB_KEYS
from lib.core.enums import HTTP_HEADER
from lib.core.enums import PAYLOAD
from lib.core.exception import SqlmapDataException
from lib.core.settings import CHECK_ZERO_COLUMNS_THRESHOLD
from lib.core.settings import MAX_ERROR_CHUNK_LENGTH
@ -123,7 +124,7 @@ def _oneShotErrorUse(expression, field=None, chunkTest=False):
nulledCastedField = queries[Backend.getIdentifiedDbms()].substring.query % (nulledCastedField, offset, kb.errorChunkLength)
# Forge the error-based SQL injection request
vector = kb.injection.data[kb.technique].vector
vector = kb.injection.data[PAYLOAD.TECHNIQUE.ERROR].vector
query = agent.prefixQuery(vector)
query = agent.suffixQuery(query)
injExpression = expression.replace(field, nulledCastedField, 1) if field else expression
@ -134,7 +135,7 @@ def _oneShotErrorUse(expression, field=None, chunkTest=False):
# Perform the request
page, headers, _ = Request.queryPage(payload, content=True, raise404=False)
incrementCounter(kb.technique)
incrementCounter(PAYLOAD.TECHNIQUE.ERROR)
if page and conf.noEscape:
page = re.sub(r"('|\%%27)%s('|\%%27).*?('|\%%27)%s('|\%%27)" % (kb.chars.start, kb.chars.stop), "", page)
@ -247,7 +248,7 @@ def _errorFields(expression, expressionFields, expressionFieldsList, num=None, e
if not kb.threadContinue:
return None
if not suppressOutput:
if not any((suppressOutput, kb.bruteMode)):
if kb.fileReadMode and output and output.strip():
print()
elif output is not None and not (threadData.resumed and kb.suppressResumeInfo) and not (emptyFields and field in emptyFields):
@ -298,7 +299,7 @@ def errorUse(expression, dump=False):
SQL injection vulnerability on the affected parameter.
"""
initTechnique(kb.technique)
initTechnique(PAYLOAD.TECHNIQUE.ERROR)
abortedFlag = False
count = None
@ -460,7 +461,7 @@ def errorUse(expression, dump=False):
duration = calculateDeltaSeconds(start)
if not kb.bruteMode:
debugMsg = "performed %d queries in %.2f seconds" % (kb.counters[kb.technique], duration)
debugMsg = "performed %d queries in %.2f seconds" % (kb.counters[PAYLOAD.TECHNIQUE.ERROR], duration)
logger.debug(debugMsg)
return value

View File

@ -312,6 +312,7 @@ def _unionTestByCharBruteforce(comment, place, parameter, value, prefix, suffix)
return validPayload, vector
@stackedmethod
def unionTest(comment, place, parameter, value, prefix, suffix):
"""
This method tests if the target URL is affected by an union

View File

@ -376,7 +376,7 @@ def unionUse(expression, unpack=True, dump=False):
threadData.shared.value.extend(arrayizeValue(_))
del threadData.shared.buffered[0]
if conf.verbose == 1 and not (threadData.resumed and kb.suppressResumeInfo) and not threadData.shared.showEta:
if conf.verbose == 1 and not (threadData.resumed and kb.suppressResumeInfo) and not threadData.shared.showEta and not kb.bruteMode:
_ = ','.join("'%s'" % _ for _ in (flattenValue(arrayizeValue(items)) if not isinstance(items, six.string_types) else [items]))
status = "[%s] [INFO] %s: %s" % (time.strftime("%X"), "resumed" if threadData.resumed else "retrieved", _ if kb.safeCharEncode else safecharencode(_))

View File

@ -7,6 +7,7 @@ See the file 'LICENSE' for copying permission
from __future__ import division
import logging
import time
from lib.core.common import Backend
@ -16,20 +17,26 @@ from lib.core.common import filterListValue
from lib.core.common import getFileItems
from lib.core.common import getPageWordSet
from lib.core.common import hashDBWrite
from lib.core.common import isNoneValue
from lib.core.common import popValue
from lib.core.common import pushValue
from lib.core.common import randomInt
from lib.core.common import randomStr
from lib.core.common import readInput
from lib.core.common import safeSQLIdentificatorNaming
from lib.core.common import safeStringFormat
from lib.core.common import unArrayizeValue
from lib.core.common import unsafeSQLIdentificatorNaming
from lib.core.data import conf
from lib.core.data import kb
from lib.core.data import logger
from lib.core.decorators import stackedmethod
from lib.core.enums import DBMS
from lib.core.enums import HASHDB_KEYS
from lib.core.enums import PAYLOAD
from lib.core.exception import SqlmapDataException
from lib.core.exception import SqlmapMissingMandatoryOptionException
from lib.core.exception import SqlmapNoneDataException
from lib.core.settings import BRUTE_COLUMN_EXISTS_TEMPLATE
from lib.core.settings import BRUTE_TABLE_EXISTS_TEMPLATE
from lib.core.settings import METADB_SUFFIX
@ -136,7 +143,6 @@ def tableExists(tableFile, regex=None):
try:
runThreads(conf.threads, tableExistsThread, threadChoice=True)
except KeyboardInterrupt:
warnMsg = "user aborted during table existence "
warnMsg += "check. sqlmap will display partial output"
@ -252,11 +258,12 @@ def columnExists(columnFile, regex=None):
try:
runThreads(conf.threads, columnExistsThread, threadChoice=True)
except KeyboardInterrupt:
warnMsg = "user aborted during column existence "
warnMsg += "check. sqlmap will display partial output"
logger.warn(warnMsg)
finally:
kb.bruteMode = False
clearConsoleLine(True)
dataToStdout("\n")
@ -287,3 +294,81 @@ def columnExists(columnFile, regex=None):
hashDBWrite(HASHDB_KEYS.KB_BRUTE_COLUMNS, kb.brute.columns, True)
return kb.data.cachedColumns
@stackedmethod
def fileExists(pathFile):
retVal = []
paths = getFileItems(pathFile, unique=True)
kb.bruteMode = True
try:
conf.dbmsHandler.readFile(randomStr())
except SqlmapNoneDataException:
pass
except:
kb.bruteMode = False
raise
threadData = getCurrentThreadData()
threadData.shared.count = 0
threadData.shared.limit = len(paths)
threadData.shared.value = []
def fileExistsThread():
threadData = getCurrentThreadData()
while kb.threadContinue:
kb.locks.count.acquire()
if threadData.shared.count < threadData.shared.limit:
path = paths[threadData.shared.count]
threadData.shared.count += 1
kb.locks.count.release()
else:
kb.locks.count.release()
break
try:
result = unArrayizeValue(conf.dbmsHandler.readFile(path))
except SqlmapNoneDataException:
result = None
kb.locks.io.acquire()
if not isNoneValue(result):
threadData.shared.value.append(result)
if conf.verbose in (1, 2) and not conf.api:
clearConsoleLine(True)
infoMsg = "[%s] [INFO] retrieved: '%s'\n" % (time.strftime("%X"), path)
dataToStdout(infoMsg, True)
if conf.verbose in (1, 2):
status = '%d/%d items (%d%%)' % (threadData.shared.count, threadData.shared.limit, round(100.0 * threadData.shared.count / threadData.shared.limit))
dataToStdout("\r[%s] [INFO] tried %s" % (time.strftime("%X"), status), True)
kb.locks.io.release()
try:
pushValue(logger.getEffectiveLevel())
logger.setLevel(logging.CRITICAL)
runThreads(conf.threads, fileExistsThread, threadChoice=True)
except KeyboardInterrupt:
warnMsg = "user aborted during file existence "
warnMsg += "check. sqlmap will display partial output"
logger.warn(warnMsg)
finally:
kb.bruteMode = False
logger.setLevel(popValue())
clearConsoleLine(True)
dataToStdout("\n")
if not threadData.shared.value:
warnMsg = "no file(s) found"
logger.warn(warnMsg)
else:
retVal = threadData.shared.value
return retVal

View File

@ -78,7 +78,6 @@ from lib.core.enums import HASH
from lib.core.enums import MKSTEMP_PREFIX
from lib.core.exception import SqlmapDataException
from lib.core.exception import SqlmapUserQuitException
from lib.core.patch import resolveCrossReferences
from lib.core.settings import COMMON_PASSWORD_SUFFIXES
from lib.core.settings import COMMON_USER_COLUMNS
from lib.core.settings import DEV_EMAIL_ADDRESS

View File

@ -18,6 +18,7 @@ from lib.core.compat import xrange
from lib.core.convert import encodeBase64
from lib.core.convert import encodeHex
from lib.core.data import conf
from lib.core.data import kb
from lib.core.data import logger
from lib.core.enums import CHARSET_TYPE
from lib.core.enums import EXPECTED
@ -82,6 +83,7 @@ class Filesystem(GenericFilesystem):
return chunkName
def stackedReadFile(self, remoteFile):
if not kb.bruteMode:
infoMsg = "fetching file: '%s'" % remoteFile
logger.info(infoMsg)

View File

@ -31,6 +31,7 @@ from plugins.generic.filesystem import Filesystem as GenericFilesystem
class Filesystem(GenericFilesystem):
def nonStackedReadFile(self, rFile):
if not kb.bruteMode:
infoMsg = "fetching file: '%s'" % rFile
logger.info(infoMsg)
@ -39,6 +40,7 @@ class Filesystem(GenericFilesystem):
return result
def stackedReadFile(self, remoteFile):
if not kb.bruteMode:
infoMsg = "fetching file: '%s'" % remoteFile
logger.info(infoMsg)
@ -64,6 +66,7 @@ class Filesystem(GenericFilesystem):
warnMsg += "file '%s'" % remoteFile
if conf.direct or isTechniqueAvailable(PAYLOAD.TECHNIQUE.UNION):
if not kb.bruteMode:
warnMsg += ", going to fall-back to simpler UNION technique"
logger.warn(warnMsg)
result = self.nonStackedReadFile(remoteFile)

View File

@ -32,6 +32,7 @@ class Filesystem(GenericFilesystem):
Request.queryPage(payload, content=False, raise404=False, silent=True, noteResponseTime=False)
for remoteFile in remoteFile.split(','):
if not kb.bruteMode:
infoMsg = "fetching file: '%s'" % remoteFile
logger.info(infoMsg)
@ -42,10 +43,11 @@ class Filesystem(GenericFilesystem):
if not isNoneValue(fileContent):
fileContent = decodeDbmsHexValue(fileContent, True)
if fileContent:
if fileContent.strip():
localFilePath = dataToOutFile(remoteFile, fileContent)
localFilePaths.append(localFilePath)
else:
elif not kb.bruteMode:
errMsg = "no data retrieved"
logger.error(errMsg)

View File

@ -9,6 +9,7 @@ import os
from lib.core.common import randomInt
from lib.core.compat import xrange
from lib.core.data import kb
from lib.core.data import logger
from lib.core.exception import SqlmapUnsupportedFeatureException
from lib.core.settings import LOBLKSIZE
@ -23,6 +24,7 @@ class Filesystem(GenericFilesystem):
GenericFilesystem.__init__(self)
def stackedReadFile(self, remoteFile):
if not kb.bruteMode:
infoMsg = "fetching file: '%s'" % remoteFile
logger.info(infoMsg)

View File

@ -174,6 +174,7 @@ class Filesystem(object):
return True
def askCheckReadFile(self, localFile, remoteFile):
if not kb.bruteMode:
message = "do you want confirmation that the remote file '%s' " % remoteFile
message += "has been successfully downloaded from the back-end "
message += "DBMS file system? [Y/n] "
@ -255,7 +256,7 @@ class Filesystem(object):
if fileContent is not None:
fileContent = decodeDbmsHexValue(fileContent, True)
if fileContent:
if fileContent.strip():
localFilePath = dataToOutFile(remoteFile, fileContent)
if not Backend.isDbms(DBMS.PGSQL):
@ -269,7 +270,7 @@ class Filesystem(object):
localFilePath += " (size differs from remote file)"
localFilePaths.append(localFilePath)
else:
elif not kb.bruteMode:
errMsg = "no data retrieved"
logger.error(errMsg)

View File

@ -572,6 +572,10 @@ commonTables = False
# Valid: True or False
commonColumns = False
# Check existence of common files.
# Valid: True or False
commonFiles = False
# These options can be used to create custom user-defined functions.
[User-defined function]