few fixes here and there and multi-core processing for dictionary based hash attack

This commit is contained in:
Miroslav Stampar 2011-07-04 19:58:41 +00:00
parent da049110df
commit b8ffcf9495
8 changed files with 225 additions and 101 deletions

View File

@ -33,7 +33,7 @@ if INTP_VER < (2, 2):
import types, warnings
class OrderedDict(dict):
class _OrderedDict(dict):
"""
A class of dictionary that keeps the insertion order of keys.
@ -869,6 +869,11 @@ class OrderedDict(dict):
"""
self._sequence.sort(*args, **kwargs)
if INTP_VER >= (2, 7):
from collections import OrderedDict
else:
OrderedDict = _OrderedDict
class Keys(object):
# FIXME: should this object be a subclass of list?
"""

View File

@ -60,17 +60,22 @@ def __selectInjection():
Selection function for injection place, parameters and type.
"""
points = []
points = {}
for i in xrange(0, len(kb.injections)):
place = kb.injections[i].place
parameter = kb.injections[i].parameter
ptype = kb.injections[i].ptype
for injection in kb.injections:
place = injection.place
parameter = injection.parameter
ptype = injection.ptype
point = (place, parameter, ptype)
if point not in points:
points.append(point)
points[point] = injection
else:
for key in points[point].keys():
if key != 'data':
points[point][key] = points[point][key] or injection[key]
points[point]['data'].update(injection['data'])
if len(points) == 1:
kb.injection = kb.injections[0]
@ -126,19 +131,11 @@ def __formatInjection(inj):
def __showInjections():
header = "sqlmap identified the following injection points with "
header += "a total of %d HTTP(s) requests" % kb.testQueryCount
data = ""
for inj in kb.injections:
data += __formatInjection(inj)
data = data.rstrip("\n")
data = "".join(set(map(lambda x: __formatInjection(x), kb.injections))).rstrip("\n")
conf.dumper.technic(header, data)
if inj.place in (HTTPMETHOD.GET, HTTPMETHOD.POST):
debugMsg = "usage of %s payloads requires manual url-encoding" % inj.place
logger.debug(debugMsg)
if conf.tamper:
infoMsg = "changes made by tampering scripts are not "
infoMsg += "included in shown payload content(s)"

View File

@ -1453,7 +1453,6 @@ def __setKnowledgeBaseAttributes(flushAll=True):
kb.testQueryCount = 0
kb.threadContinue = True
kb.threadException = False
kb.threadData = {}
kb.uChar = "NULL"
kb.xpCmdshellAvailable = False
@ -1650,6 +1649,9 @@ def __mergeOptions(inputOptions, overrideOptions):
conf[key] = value
def __setTrafficOutputFP():
infoMsg = "setting file for logging HTTP traffic"
logger.info(infoMsg)
if conf.trafficFile:
conf.trafficFP = openFile(conf.trafficFile, "w+")

View File

@ -300,7 +300,7 @@ MYSQL_ERROR_CHUNK_LENGTH = 50
MSSQL_ERROR_CHUNK_LENGTH = 100
# Do not unescape the injected statement if it contains any of the following SQL words
EXCLUDE_UNESCAPE = ("WAITFOR DELAY ", " INTO DUMPFILE ", " INTO OUTFILE ", "CREATE ", "BULK ", "EXEC ", "RECONFIGURE ", "DECLARE ", CHAR_INFERENCE_MARK)
EXCLUDE_UNESCAPE = ("WAITFOR DELAY ", " INTO DUMPFILE ", " INTO OUTFILE ", "CREATE ", "BULK ", "EXEC ", "RECONFIGURE ", "DECLARE ", "'%s'" % CHAR_INFERENCE_MARK)
# Mark used for replacement of reflected values
REFLECTED_VALUE_MARKER = '__REFLECTED_VALUE__'
@ -364,3 +364,9 @@ DUMMY_SQL_INJECTION_CHARS = ";()\"'"
# Extensions skipped by crawler
CRAWL_EXCLUDE_EXTENSIONS = ("gif","jpg","jar","tif","bmp","war","ear","mpg","wmv","mpeg","scm","iso","dmp","dll","cab","so","avi","bin","exe","iso","tar","png","pdf","ps","mp3","zip","rar","gz")
# Template used for common table existence check
BRUTE_TABLE_EXISTS_TEMPLATE = "EXISTS(SELECT %d FROM %s)"
# Template used for common column existence check
BRUTE_COLUMN_EXISTS_TEMPLATE = "EXISTS(SELECT %s FROM %s)"

View File

@ -25,7 +25,7 @@ from lib.core.settings import PYVERSION
shared = advancedDict()
class ThreadData():
class _ThreadData(threading.local):
"""
Represents thread independent data
"""
@ -44,6 +44,8 @@ class ThreadData():
self.shared = shared
self.valueStack = []
ThreadData = _ThreadData()
def getCurrentThreadUID():
return hash(threading.currentThread())
@ -52,13 +54,12 @@ def readInput(message, default=None):
def getCurrentThreadData():
"""
Returns current thread's dependent data
Returns current thread's local data
"""
threadUID = getCurrentThreadUID()
if threadUID not in kb.threadData:
kb.threadData[threadUID] = ThreadData()
return kb.threadData[threadUID]
global ThreadData
return ThreadData
def exceptionHandledFunction(threadFunction):
try:

View File

@ -20,6 +20,7 @@ from lib.core.common import getPageWordSet
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 safeStringFormat
from lib.core.common import safeSQLIdentificatorNaming
@ -27,10 +28,13 @@ from lib.core.data import conf
from lib.core.data import kb
from lib.core.data import logger
from lib.core.enums import DBMS
from lib.core.exception import sqlmapDataException
from lib.core.exception import sqlmapMissingMandatoryOptionException
from lib.core.exception import sqlmapThreadException
from lib.core.settings import MAX_NUMBER_OF_THREADS
from lib.core.settings import METADB_SUFFIX
from lib.core.settings import BRUTE_COLUMN_EXISTS_TEMPLATE
from lib.core.settings import BRUTE_TABLE_EXISTS_TEMPLATE
from lib.core.session import safeFormatString
from lib.core.threads import getCurrentThreadData
from lib.core.threads import runThreads
@ -52,6 +56,13 @@ def __addPageTextWords():
return wordsList
def tableExists(tableFile, regex=None):
result = inject.checkBooleanExpression("%s" % safeStringFormat(BRUTE_TABLE_EXISTS_TEMPLATE, (randomInt(1), randomStr())))
if result:
errMsg = "can't use table existence check because of detected invalid results "
errMsg += "(most probably caused by inability of the used injection "
errMsg += "to distinguish errornous results)"
raise sqlmapDataException, errMsg
tables = getFileItems(tableFile, lowercase=Backend.getIdentifiedDbms() in (DBMS.ACCESS), unique=True)
infoMsg = "checking table existence using items from '%s'" % tableFile
@ -84,7 +95,7 @@ def tableExists(tableFile, regex=None):
else:
fullTableName = table
result = inject.checkBooleanExpression("%s" % safeStringFormat("EXISTS(SELECT %d FROM %s)", (randomInt(1), fullTableName)))
result = inject.checkBooleanExpression("%s" % safeStringFormat(BRUTE_TABLE_EXISTS_TEMPLATE, (randomInt(1), fullTableName)))
kb.locks.ioLock.acquire()
@ -135,6 +146,13 @@ def columnExists(columnFile, regex=None):
errMsg = "missing table parameter"
raise sqlmapMissingMandatoryOptionException, errMsg
result = inject.checkBooleanExpression(safeStringFormat(BRUTE_COLUMN_EXISTS_TEMPLATE, (randomStr(), randomStr())))
if result:
errMsg = "can't use column existence check because of detected invalid results "
errMsg += "(most probably caused by inability of the used injection "
errMsg += "to distinguish errornous results)"
raise sqlmapDataException, errMsg
infoMsg = "checking column existence using items from '%s'" % columnFile
logger.info(infoMsg)
@ -169,7 +187,7 @@ def columnExists(columnFile, regex=None):
kb.locks.countLock.release()
break
result = inject.checkBooleanExpression("%s" % safeStringFormat("EXISTS(SELECT %s FROM %s)", (column, table)))
result = inject.checkBooleanExpression(safeStringFormat(BRUTE_COLUMN_EXISTS_TEMPLATE, (column, table)))
kb.locks.ioLock.acquire()

View File

@ -19,6 +19,7 @@ import time
from hashlib import md5
from hashlib import sha1
from Queue import Queue
from zipfile import ZipFile
from extra.pydes.pyDes import des
@ -35,6 +36,7 @@ from lib.core.common import normalizeUnicode
from lib.core.common import paths
from lib.core.common import readInput
from lib.core.common import singleTimeLogMessage
from lib.core.common import singleTimeWarnMessage
from lib.core.common import Wordlist
from lib.core.convert import hexdecode
from lib.core.convert import hexencode
@ -50,9 +52,13 @@ from lib.core.settings import DUMMY_USER_PREFIX
from lib.core.settings import GENERAL_IP_ADDRESS_REGEX
from lib.core.settings import HASH_MOD_ITEM_DISPLAY
from lib.core.settings import IS_WIN
from lib.core.settings import PYVERSION
from lib.core.settings import ML
from lib.core.settings import UNICODE_ENCODING
if PYVERSION >= "2.6":
import multiprocessing
def mysql_passwd(password, uppercase=True):
"""
Reference(s):
@ -320,6 +326,7 @@ def dictionaryAttack(attack_dict):
suffix_list = [""]
hash_regexes = []
results = []
processException = False
for (_, hashes) in attack_dict.items():
for hash_ in hashes:
@ -421,10 +428,8 @@ def dictionaryAttack(attack_dict):
kb.wordlist.append(normalizeUnicode(user))
if hash_regex in (HASH.MYSQL, HASH.MYSQL_OLD, HASH.MD5_GENERIC, HASH.SHA1_GENERIC):
count = 0
for suffix in suffix_list:
if not attack_info:
if not attack_info or processException:
break
if suffix:
@ -434,53 +439,91 @@ def dictionaryAttack(attack_dict):
kb.wordlist.rewind()
for word in kb.wordlist:
if not attack_info:
break
count += 1
if not isinstance(word, basestring):
continue
if suffix:
word = word + suffix
def bruteProcess(attack_info, hash_regex, wordlist, suffix, retVal, proc_id, proc_count):
count = 0
try:
current = __functions__[hash_regex](password = word, uppercase = False)
for word in kb.wordlist:
if not attack_info:
break
for item in attack_info:
((user, hash_), _) = item
count += 1
if hash_ == current:
results.append((user, hash_, word))
clearConsoleLine()
if not isinstance(word, basestring):
continue
infoMsg = "[%s] [INFO] found: '%s'" % (time.strftime("%X"), word)
if suffix:
word = word + suffix
if user and not user.startswith(DUMMY_USER_PREFIX):
infoMsg += " for user '%s'\n" % user
else:
infoMsg += " for hash '%s'\n" % hash_
try:
current = __functions__[hash_regex](password = word, uppercase = False)
dataToStdout(infoMsg, True)
for item in attack_info:
((user, hash_), _) = item
attack_info.remove(item)
if hash_ == current:
retVal.put((user, hash_, word))
elif count % HASH_MOD_ITEM_DISPLAY == 0 or hash_regex in (HASH.ORACLE_OLD) or hash_regex == HASH.CRYPT_GENERIC and IS_WIN:
status = 'current status: %d%s (%s...)' % (kb.wordlist.percentage(), '%', word.ljust(5)[:8])
dataToStdout("\r[%s] [INFO] %s" % (time.strftime("%X"), status))
clearConsoleLine()
infoMsg = "[%s] [INFO] found: '%s'" % (time.strftime("%X"), word)
if user and not user.startswith(DUMMY_USER_PREFIX):
infoMsg += " for user '%s'\n" % user
else:
infoMsg += " for hash '%s'\n" % hash_
dataToStdout(infoMsg, True)
attack_info.remove(item)
elif proc_id == 0 and count % HASH_MOD_ITEM_DISPLAY == 0 or hash_regex in (HASH.ORACLE_OLD) or hash_regex == HASH.CRYPT_GENERIC and IS_WIN:
status = 'current status: %d%s (%s...)' % (proc_count * kb.wordlist.percentage(), '%', word.ljust(5)[:5])
dataToStdout("\r[%s] [INFO] %s" % (time.strftime("%X"), status))
except KeyboardInterrupt:
raise
except:
warnMsg = "there was a problem while hashing entry: %s. " % repr(word)
warnMsg += "Please report by e-mail to %s." % ML
logger.critical(warnMsg)
except KeyboardInterrupt:
print
warnMsg = "user aborted during dictionary attack phase"
logger.warn(warnMsg)
return results
pass
except:
warnMsg = "there was a problem while hashing entry: %s. " % repr(word)
warnMsg += "Please report by e-mail to %s." % ML
logger.critical(warnMsg)
retVal = None
try:
if PYVERSION >= "2.6":
infoMsg = "starting %d hash attack processes " % multiprocessing.cpu_count()
singleTimeLogMessage(infoMsg)
processes = []
retVal = multiprocessing.Queue()
for i in xrange(multiprocessing.cpu_count()):
p = multiprocessing.Process(target=bruteProcess, args=(attack_info, hash_regex, kb.wordlist, suffix, retVal, i, multiprocessing.cpu_count()))
p.start()
processes.append(p)
for p in processes:
p.join()
else:
warnMsg = "multiprocessing not supported on current version of "
warnMsg += "Python (%s < 2.6)" % PYVERSION
singleTimeWarnMessage(warnMsg)
retVal = Queue()
bruteProcess(attack_info, hash_regex, kb.wordlist, suffix, retVal, 0, 1)
except KeyboardInterrupt:
print
processException = True
warnMsg = "user aborted during dictionary attack phase"
logger.warn(warnMsg)
results = [retVal.get() for i in xrange(retVal.qsize())] if retVal else []
clearConsoleLine()
@ -490,7 +533,7 @@ def dictionaryAttack(attack_dict):
found = False
for suffix in suffix_list:
if found:
if found or processException:
break
if suffix:
@ -500,50 +543,102 @@ def dictionaryAttack(attack_dict):
kb.wordlist.rewind()
for word in kb.wordlist:
current = __functions__[hash_regex](password = word, uppercase = False, **kwargs)
count += 1
if not isinstance(word, basestring):
continue
if suffix:
word = word + suffix
def bruteProcess(user, hash_, kwargs, hash_regex, wordlist, suffix, retVal, found, proc_id, proc_count):
count = 0
try:
if hash_ == current:
if regex == HASH.ORACLE_OLD: #only for cosmetic purposes
word = word.upper()
results.append((user, hash_, word))
clearConsoleLine()
for word in kb.wordlist:
infoMsg = "[%s] [INFO] found: '%s'" % (time.strftime("%X"), word)
current = __functions__[hash_regex](password = word, uppercase = False, **kwargs)
count += 1
if user and not user.startswith(DUMMY_USER_PREFIX):
infoMsg += " for user '%s'\n" % user
else:
infoMsg += " for hash '%s'\n" % hash_
if not isinstance(word, basestring):
continue
dataToStdout(infoMsg, True)
if suffix:
word = word + suffix
found = True
break
elif count % HASH_MOD_ITEM_DISPLAY == 0 or hash_regex in (HASH.ORACLE_OLD) or hash_regex == HASH.CRYPT_GENERIC and IS_WIN:
status = 'current status: %d%s (%s...)' % (kb.wordlist.percentage(), '%', word.ljust(5)[:5])
if not user.startswith(DUMMY_USER_PREFIX):
status += ' (user: %s)' % user
dataToStdout("\r[%s] [INFO] %s" % (time.strftime("%X"), status))
try:
if hash_ == current:
if regex == HASH.ORACLE_OLD: #only for cosmetic purposes
word = word.upper()
retVal.put((user, hash_, word))
clearConsoleLine()
infoMsg = "[%s] [INFO] found: '%s'" % (time.strftime("%X"), word)
if user and not user.startswith(DUMMY_USER_PREFIX):
infoMsg += " for user '%s'\n" % user
else:
infoMsg += " for hash '%s'\n" % hash_
dataToStdout(infoMsg, True)
found.value = True
break
elif proc_id == 0 and count % HASH_MOD_ITEM_DISPLAY == 0 or hash_regex in (HASH.ORACLE_OLD) or hash_regex == HASH.CRYPT_GENERIC and IS_WIN:
status = 'current status: %d%s (%s...)' % (proc_count * kb.wordlist.percentage(), '%', word.ljust(5)[:5])
if not user.startswith(DUMMY_USER_PREFIX):
status += ' (user: %s)' % user
dataToStdout("\r[%s] [INFO] %s" % (time.strftime("%X"), status))
except KeyboardInterrupt:
raise
except:
warnMsg = "there was a problem while hashing entry: %s. " % repr(word)
warnMsg += "Please report by e-mail to %s." % ML
logger.critical(warnMsg)
except KeyboardInterrupt:
print
warnMsg = "user aborted during dictionary attack phase"
logger.warn(warnMsg)
return results
pass
except:
warnMsg = "there was a problem while hashing entry: %s. " % repr(word)
warnMsg += "Please report by e-mail to %s." % ML
logger.critical(warnMsg)
retVal = None
try:
if PYVERSION >= "2.6":
infoMsg = "starting %d hash attack processes " % multiprocessing.cpu_count()
singleTimeLogMessage(infoMsg)
processes = []
retVal = multiprocessing.Queue()
found_ = multiprocessing.Value('i', False)
for i in xrange(multiprocessing.cpu_count()):
p = multiprocessing.Process(target=bruteProcess, args=(user, hash_, kwargs, hash_regex, kb.wordlist, suffix, retVal, found_, i, multiprocessing.cpu_count()))
p.start()
processes.append(p)
for p in processes:
p.join()
found = found_.value != 0
else:
warnMsg = "multiprocessing not supported on current version of "
warnMsg += "Python (%s < 2.6)" % PYVERSION
singleTimeWarnMessage(warnMsg)
class Value():
pass
retVal = Queue()
found_ = Value()
found_.value = False
bruteProcess(user, hash_, kwargs, hash_regex, kb.wordlist, suffix, retVal, found_, 0, 1)
found = found_.value
except KeyboardInterrupt:
print
processException = True
warnMsg = "user aborted during dictionary attack phase"
logger.warn(warnMsg)
results = [retVal.get() for i in xrange(retVal.qsize())] if retVal else []
clearConsoleLine()

View File

@ -164,7 +164,7 @@ class Miscellaneous:
if not choice or choice == "1":
choice = "1"
condParam = " LIKE '%%%s%%'"
elif choice.isdigit() and choice == "2":
elif choice == "2":
condParam = "='%s'"
else:
errMsg = "invalid value"