first working version of dictionary attack

This commit is contained in:
Miroslav Stampar 2010-11-23 13:24:02 +00:00
parent c471b815cc
commit ba4ea32603
6 changed files with 278109 additions and 3 deletions

View File

@ -662,6 +662,7 @@ def setPaths():
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")
paths.WORDLIST_TXT = os.path.join(paths.SQLMAP_TXT_PATH, "wordlist.txt")
paths.PHPIDS_RULES_XML = os.path.join(paths.SQLMAP_XML_PATH, "phpids_rules.xml")
paths.ERRORS_XML = os.path.join(paths.SQLMAP_XML_PATH, "errors.xml")
paths.INJECTIONS_XML = os.path.join(paths.SQLMAP_XML_PATH, "injections.xml")
@ -1325,11 +1326,15 @@ def initCommonOutputs():
cfile.close()
def getFileItems(filename, commentPrefix='#'):
def getFileItems(filename, commentPrefix='#', unicode_=True):
retVal = []
checkFile(filename)
ifile = codecs.open(filename, 'r', conf.dataEncoding)
if unicode_:
ifile = codecs.open(filename, 'r', conf.dataEncoding)
else:
ifile = open(filename, 'r')
for line in ifile.readlines(): # xreadlines doesn't return unicode strings when codec.open() is used
if commentPrefix:

View File

@ -42,3 +42,15 @@ class HTTPMETHOD:
class NULLCONNECTION:
HEAD = "HEAD"
RANGE = "Range"
class HASH:
MYSQL = r'(?i)\A\*[0-9a-f]{40}\Z'
MYSQL_OLD = r'(?i)\A[0-9a-f]{16}\Z'
POSTGRES = r'(?i)\Amd5[0-9a-f]{32}\Z'
MSSQL = r'(?i)\A0x0100[0-9a-f]{8}[0-9a-f]{40}\Z'
MSSQL_OLD = r'(?i)\A0x0100[0-9a-f]{8}[0-9a-f]{80}\Z'
ORACLE = r'(?i)\As:[0-9a-f]{60}\Z'
ORACLE_OLD = r'(?i)\A[0-9a-f]{16}\Z'
MD5_GENERIC = r'(?i)\A[0-9a-f]{32}\Z'
SHA1_GENERIC = r'(?i)\A[0-9a-f]{40}\Z'
__all__ = (MYSQL, MYSQL_OLD, POSTGRES, MSSQL, MSSQL_OLD, ORACLE, ORACLE_OLD, MD5_GENERIC, SHA1_GENERIC)

View File

@ -7,13 +7,26 @@ Copyright (c) 2006-2010 sqlmap developers (http://sqlmap.sourceforge.net/)
See the file 'doc/COPYING' for copying permission
"""
import re
import time
from hashlib import md5
from hashlib import sha1
from zipfile import ZipFile
from extra.pydes.pyDes import des
from extra.pydes.pyDes import CBC
from lib.core.common import conf
from lib.core.common import dataToStdout
from lib.core.common import getFileItems
from lib.core.common import paths
from lib.core.common import readInput
from lib.core.convert import hexdecode
from lib.core.convert import hexencode
from lib.core.data import kb
from lib.core.data import logger
from lib.core.enums import DBMS
from lib.core.enums import HASH
def mysql_passwd(password, uppercase=True):
"""
@ -126,8 +139,8 @@ def oracle_old_passwd(password, username, uppercase=True): # prior to version '1
>>> oracle_old_passwd(password='tiger', username='scott', uppercase=True)
'F894844C34402B67'
"""
IV, pad = "\0"*8, "\0"
username = unicode.encode(username, conf.dataEncoding) #pyDes has issues with unicode strings
unistr = "".join("\0%s" % c for c in (username + password).upper())
cipher = des(hexdecode("0123456789ABCDEF"), CBC, IV, pad)
@ -138,3 +151,115 @@ def oracle_old_passwd(password, username, uppercase=True): # prior to version '1
retVal = hexencode(encrypted[-8:])
return retVal.upper() if uppercase else retVal.lower()
def md5_generic_passwd(password, uppercase=False):
"""
>>> md5_generic_passwd(password='testpass', uppercase=False)
'179ad45c6ce2cb97cf1029e212046e81'
"""
retVal = md5(password).hexdigest()
return retVal.upper() if uppercase else retVal.lower()
def sha1_generic_passwd(password, uppercase=False):
"""
>>> sha1_generic_passwd(password='testpass', uppercase=False)
'206c80413b9a96c1312cc346b7d2517b84463edd'
"""
retVal = sha1(password).hexdigest()
return retVal.upper() if uppercase else retVal.lower()
__functions__ = {
HASH.MYSQL: mysql_passwd, HASH.MYSQL_OLD: mysql_old_passwd, HASH.POSTGRES: postgres_passwd,
HASH.MSSQL: mssql_passwd, HASH.MSSQL_OLD: mssql_old_passwd, HASH.ORACLE: oracle_passwd,
HASH.ORACLE_OLD: oracle_old_passwd, HASH.MD5_GENERIC: md5_generic_passwd, HASH.SHA1_GENERIC: sha1_generic_passwd
}
def dictionaryAttack():
rehash = None
attack_info = []
results = []
for (_, hashes) in kb.data.cachedUsersPasswords.items():
for hash_ in hashes:
if not hash_:
continue
hash_ = hash_.split()[0]
for regex in HASH.__all__:
if re.match(regex, hash_):
rehash = regex
break
if rehash:
for (user, hashes) in kb.data.cachedUsersPasswords.items():
for hash_ in hashes:
if not hash_:
continue
hash_ = hash_.split()[0]
if re.match(rehash, hash_):
hash_ = hash_.lower()
if rehash in (HASH.MYSQL, HASH.MYSQL_OLD, HASH.MD5_GENERIC, HASH.SHA1_GENERIC) and kb.dbms != DBMS.ORACLE:
attack_info.append([(user, hash_), {}])
elif rehash in (HASH.ORACLE_OLD, HASH.POSTGRES):
attack_info.append([(user, hash_), {'username': user}])
elif rehash in (HASH.ORACLE):
attack_info.append([(user, hash_), {'salt': hash_[-20:]}])
elif rehash in (HASH.MSSQL, HASH.MSSQL_OLD):
attack_info.append([(user, hash_), {'salt': hash_[6:14]}])
infoMsg = "loading dictionary from: '%s'" % paths.WORDLIST_TXT
logger.info(infoMsg)
wordlist = getFileItems(paths.WORDLIST_TXT, None, False)
infoMsg = "running dictionary attack"
logger.info(infoMsg)
length = len(wordlist)
if rehash in (HASH.MYSQL, HASH.MYSQL_OLD, HASH.MD5_GENERIC, HASH.SHA1_GENERIC) and kb.dbms != DBMS.ORACLE:
count = 0
for word in wordlist:
count += 1
current = __functions__[rehash](password = word, uppercase = False)
for item in attack_info:
((user, hash_), _) = item
if count % 1117 == 0 or count == length:
status = '%d/%d words (%d%s)' % (count, length, round(100.0*count/length), '%')
dataToStdout("\r[%s] [INFO] %s" % (time.strftime("%X"), status), True)
if hash_ == current:
results.append((user, hash_, word))
#dataToStdout("\r[%s] [INFO] found: %s:%s\n" % (time.strftime("%X"), user, word), True)
attack_info.remove(item)
else:
for ((user, hash_), kwargs) in attack_info:
count = 0
for word in wordlist:
current = __functions__[rehash](password = word, uppercase = False, **kwargs)
count += 1
if count % 1117 == 0 or count == length:
status = '%d/%d words (%d%s)' % (count, length, round(100.0*count/length), '%')
dataToStdout("\r[%s] [INFO] %s" % (time.strftime("%X"), status), True)
if hash_ == current:
results.append((user, hash_, word))
#dataToStdout("\r[%s] [INFO] found: %s:%s\n" % (time.strftime("%X"), user, word), True)
break
dataToStdout("\n", True)
blank = " "
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%spassword: %s" % ('\n' if kb.data.cachedUsersPasswords[user][i][-1] != '\n' else '', blank, password)

View File

@ -47,6 +47,7 @@ from lib.techniques.brute.use import tableExists
from lib.techniques.error.test import errorTest
from lib.techniques.inband.union.test import unionTest
from lib.techniques.outband.stacked import stackedTest
from lib.utils.hash import dictionaryAttack
class Enumeration:
"""
@ -329,6 +330,16 @@ class Enumeration:
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:
dictionaryAttack()
return kb.data.cachedUsersPasswords
def __isAdminFromPrivileges(self, privileges):

277953
txt/wordlist.txt Normal file

File diff suppressed because it is too large Load Diff

Binary file not shown.