pretty important commit (now dumped tables are prone to dictionary attack)

This commit is contained in:
Miroslav Stampar 2010-12-27 10:56:28 +00:00
parent c8d5a6b980
commit 51a492e17d
4 changed files with 116 additions and 36 deletions

View File

@ -1203,6 +1203,7 @@ def __setKnowledgeBaseAttributes(flushAll=True):
kb.targetUrls = set() kb.targetUrls = set()
kb.testedParams = set() kb.testedParams = set()
kb.userAgents = None kb.userAgents = None
kb.wordlist = None
def __saveCmdline(): def __saveCmdline():
""" """

View File

@ -74,6 +74,9 @@ UPPER_RATIO_BOUND = 0.98
# dynamicity mark length used in dynamicity removal engine # dynamicity mark length used in dynamicity removal engine
DYNAMICITY_MARK_LENGTH = 32 DYNAMICITY_MARK_LENGTH = 32
# dummy user prefix used in dictionary attack
DUMMY_USER_PREFIX = '__dummy__'
# System variables # System variables
IS_WIN = subprocess.mswindows IS_WIN = subprocess.mswindows
# The name of the operating system dependent module imported. The following # The name of the operating system dependent module imported. The following

View File

@ -20,6 +20,7 @@ from lib.core.common import checkFile
from lib.core.common import conf from lib.core.common import conf
from lib.core.common import clearConsoleLine from lib.core.common import clearConsoleLine
from lib.core.common import dataToStdout from lib.core.common import dataToStdout
from lib.core.common import getCompiledRegex
from lib.core.common import getFileItems from lib.core.common import getFileItems
from lib.core.common import getPublicTypeMembers from lib.core.common import getPublicTypeMembers
from lib.core.common import paths from lib.core.common import paths
@ -30,6 +31,8 @@ from lib.core.data import kb
from lib.core.data import logger from lib.core.data import logger
from lib.core.enums import DBMS from lib.core.enums import DBMS
from lib.core.enums import HASH from lib.core.enums import HASH
from lib.core.exception import sqlmapUserQuitException
from lib.core.settings import DUMMY_USER_PREFIX
def mysql_passwd(password, uppercase=True): def mysql_passwd(password, uppercase=True):
""" """
@ -190,37 +193,96 @@ __functions__ = {
HASH.SHA1_GENERIC: sha1_generic_passwd HASH.SHA1_GENERIC: sha1_generic_passwd
} }
def dictionaryAttack(): def attackCachedUsersPasswords():
hash_regexes = [] if kb.data.cachedUsersPasswords:
wordlist = [] results = dictionaryAttack(kb.data.cachedUsersPasswords)
for (user, hash_, password) in results:
for i in xrange(len(kb.data.cachedUsersPasswords[user])):
if kb.data.cachedUsersPasswords[user][i] and hash_.lower() in kb.data.cachedUsersPasswords[user][i].lower():
kb.data.cachedUsersPasswords[user][i] += "%s clear-text password: %s" % ('\n' if kb.data.cachedUsersPasswords[user][i][-1] != '\n' else '', password)
for (_, hashes) in kb.data.cachedUsersPasswords.items(): def attackDumpedTable():
if kb.data.dumpedTable:
table = kb.data.dumpedTable
columns = table.keys()
count = table["__infos__"]["count"]
colUser = ''
attack_dict = {}
for column in columns:
if column and column.lower() in ('user', 'username', 'user_name'):
colUser = column
break
for i in range(count):
for column in columns:
if column == colUser or column == '__infos__':
continue
value = table[column]['values'][i]
if hashRecognition(value):
if colUser:
if table[colUser]['values'][i] not in attack_dict:
attack_dict[table[colUser]['values'][i]] = []
attack_dict[table[colUser]['values'][i]].append(value)
else:
attack_dict['%s%d' % (DUMMY_USER_PREFIX, i)] = [value]
if attack_dict:
message = "recognized possible password hash values. "
message += "do you want to use dictionary attack on retrieved table items? [Y/n/q]"
test = readInput(message, default="Y")
if test[0] in ("n", "N"):
return
elif test[0] in ("q", "Q"):
raise sqlmapUserQuitException
results = dictionaryAttack(attack_dict)
for (user, hash_, password) in results:
for i in range(count):
for column in columns:
if column == colUser or column == '__infos__':
continue
value = table[column]['values'][i]
if value.lower() == hash_.lower():
table[column]['values'][i] += " (%s)" % password
def hashRecognition(value):
retVal = None
if value:
for name, regex in getPublicTypeMembers(HASH):
#hashes for Oracle and old MySQL look the same hence these checks
if kb.dbms == DBMS.ORACLE and regex == HASH.MYSQL_OLD:
continue
elif kb.dbms == DBMS.MYSQL and regex == HASH.ORACLE_OLD:
continue
elif getCompiledRegex(regex).match(value):
retVal = regex
break
return retVal
def dictionaryAttack(attack_dict):
hash_regexes = []
results = []
for (_, hashes) in attack_dict.items():
for hash_ in hashes: for hash_ in hashes:
if not hash_: if not hash_:
continue continue
hash_ = hash_.split()[0] hash_ = hash_.split()[0]
regex = hashRecognition(hash_)
for name, regex in getPublicTypeMembers(HASH): if regex not in hash_regexes:
#hashes for Oracle and old MySQL look the same hence these checks hash_regexes.append(regex)
if kb.dbms == DBMS.ORACLE and regex == HASH.MYSQL_OLD: infoMsg = "using hash method: '%s'" % __functions__[regex].func_name
continue logger.info(infoMsg)
elif kb.dbms == DBMS.MYSQL and regex == HASH.ORACLE_OLD:
continue
elif re.match(regex, hash_):
if regex not in hash_regexes:
hash_regexes.append(regex)
infoMsg = "using hash method: '%s'" % __functions__[regex].func_name
logger.info(infoMsg)
for hash_regex in hash_regexes: for hash_regex in hash_regexes:
attack_info = [] attack_info = []
results = []
for (user, hashes) in kb.data.cachedUsersPasswords.items(): for (user, hashes) in attack_dict.items():
for hash_ in hashes: for hash_ in hashes:
if not hash_: if not hash_:
continue continue
@ -242,7 +304,7 @@ def dictionaryAttack():
elif hash_regex in (HASH.MSSQL, HASH.MSSQL_OLD): elif hash_regex in (HASH.MSSQL, HASH.MSSQL_OLD):
attack_info.append([(user, hash_), {'salt': hash_[6:14]}]) attack_info.append([(user, hash_), {'salt': hash_[6:14]}])
if not wordlist: if not kb.wordlist:
if hash_regex == HASH.ORACLE_OLD: #it's the slowest of all methods hence smaller default dict if hash_regex == HASH.ORACLE_OLD: #it's the slowest of all methods hence smaller default dict
message = "what's the dictionary's location? [%s]" % paths.ORACLE_DEFAULT_PASSWD message = "what's the dictionary's location? [%s]" % paths.ORACLE_DEFAULT_PASSWD
dictpath = readInput(message, default=paths.ORACLE_DEFAULT_PASSWD) dictpath = readInput(message, default=paths.ORACLE_DEFAULT_PASSWD)
@ -255,9 +317,9 @@ def dictionaryAttack():
infoMsg = "loading dictionary from: '%s'" % dictpath infoMsg = "loading dictionary from: '%s'" % dictpath
logger.info(infoMsg) logger.info(infoMsg)
wordlist = getFileItems(dictpath, None, False) kb.wordlist = getFileItems(dictpath, None, False)
length = len(wordlist) length = len(kb.wordlist)
infoMsg = "starting dictionary attack (%s)" % __functions__[hash_regex].func_name infoMsg = "starting dictionary attack (%s)" % __functions__[hash_regex].func_name
logger.info(infoMsg) logger.info(infoMsg)
@ -265,7 +327,7 @@ def dictionaryAttack():
if hash_regex in (HASH.MYSQL, HASH.MYSQL_OLD, HASH.MD5_GENERIC, HASH.SHA1_GENERIC): if hash_regex in (HASH.MYSQL, HASH.MYSQL_OLD, HASH.MD5_GENERIC, HASH.SHA1_GENERIC):
count = 0 count = 0
for word in wordlist: for word in kb.wordlist:
count += 1 count += 1
current = __functions__[hash_regex](password = word, uppercase = False) current = __functions__[hash_regex](password = word, uppercase = False)
@ -275,7 +337,14 @@ def dictionaryAttack():
if hash_ == current: if hash_ == current:
results.append((user, hash_, word)) results.append((user, hash_, word))
clearConsoleLine() clearConsoleLine()
dataToStdout("[%s] [INFO] found: '%s' for user: '%s'\n" % (time.strftime("%X"), word, user), True)
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) attack_info.remove(item)
elif count % 1117 == 0 or count == length or hash_regex in (HASH.ORACLE_OLD): elif count % 1117 == 0 or count == length or hash_regex in (HASH.ORACLE_OLD):
@ -288,7 +357,7 @@ def dictionaryAttack():
for ((user, hash_), kwargs) in attack_info: for ((user, hash_), kwargs) in attack_info:
count = 0 count = 0
for word in wordlist: for word in kb.wordlist:
current = __functions__[hash_regex](password = word, uppercase = False, **kwargs) current = __functions__[hash_regex](password = word, uppercase = False, **kwargs)
count += 1 count += 1
@ -297,7 +366,14 @@ def dictionaryAttack():
word = word.upper() word = word.upper()
results.append((user, hash_, word)) results.append((user, hash_, word))
clearConsoleLine() clearConsoleLine()
dataToStdout("[%s] [INFO] found: '%s' for user: '%s'\n" % (time.strftime("%X"), word, user), True)
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)
break break
elif count % 1117 == 0 or count == length or hash_regex in (HASH.ORACLE_OLD): elif count % 1117 == 0 or count == length or hash_regex in (HASH.ORACLE_OLD):
@ -306,12 +382,9 @@ def dictionaryAttack():
clearConsoleLine() clearConsoleLine()
for (user, hash_, password) in results:
for i in xrange(len(kb.data.cachedUsersPasswords[user])):
if kb.data.cachedUsersPasswords[user][i] and hash_.lower() in kb.data.cachedUsersPasswords[user][i].lower():
kb.data.cachedUsersPasswords[user][i] += "%s clear-text password: %s" % ('\n' if kb.data.cachedUsersPasswords[user][i][-1] != '\n' else '', password)
if len(hash_regexes) == 0: if len(hash_regexes) == 0:
warnMsg = "unknown hash format. " warnMsg = "unknown hash format. "
warnMsg += "Please report by e-mail to sqlmap-users@lists.sourceforge.net." warnMsg += "Please report by e-mail to sqlmap-users@lists.sourceforge.net."
logger.warn(warnMsg) logger.warn(warnMsg)
return results

View File

@ -51,7 +51,8 @@ from lib.request.connect import Connect as Request
from lib.techniques.brute.use import columnExists from lib.techniques.brute.use import columnExists
from lib.techniques.brute.use import tableExists from lib.techniques.brute.use import tableExists
from lib.techniques.inband.union.test import unionTest from lib.techniques.inband.union.test import unionTest
from lib.utils.hash import dictionaryAttack from lib.utils.hash import attackDumpedTable
from lib.utils.hash import attackCachedUsersPasswords
class Enumeration: class Enumeration:
""" """
@ -345,7 +346,7 @@ class Enumeration:
elif test[0] in ("q", "Q"): elif test[0] in ("q", "Q"):
raise sqlmapUserQuitException raise sqlmapUserQuitException
else: else:
dictionaryAttack() attackCachedUsersPasswords()
return kb.data.cachedUsersPasswords return kb.data.cachedUsersPasswords
@ -1315,6 +1316,8 @@ class Enumeration:
return None return None
attackDumpedTable()
return kb.data.dumpedTable return kb.data.dumpedTable
def dumpAll(self): def dumpAll(self):