mirror of
https://github.com/sqlmapproject/sqlmap.git
synced 2025-02-13 10:00:36 +03:00
first working version of dictionary attack
This commit is contained in:
parent
c471b815cc
commit
ba4ea32603
|
@ -662,6 +662,7 @@ def setPaths():
|
||||||
paths.COMMON_TABLES = os.path.join(paths.SQLMAP_TXT_PATH, "common-tables.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.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.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.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.ERRORS_XML = os.path.join(paths.SQLMAP_XML_PATH, "errors.xml")
|
||||||
paths.INJECTIONS_XML = os.path.join(paths.SQLMAP_XML_PATH, "injections.xml")
|
paths.INJECTIONS_XML = os.path.join(paths.SQLMAP_XML_PATH, "injections.xml")
|
||||||
|
@ -1325,11 +1326,15 @@ def initCommonOutputs():
|
||||||
|
|
||||||
cfile.close()
|
cfile.close()
|
||||||
|
|
||||||
def getFileItems(filename, commentPrefix='#'):
|
def getFileItems(filename, commentPrefix='#', unicode_=True):
|
||||||
retVal = []
|
retVal = []
|
||||||
|
|
||||||
checkFile(filename)
|
checkFile(filename)
|
||||||
|
|
||||||
|
if unicode_:
|
||||||
ifile = codecs.open(filename, 'r', conf.dataEncoding)
|
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
|
for line in ifile.readlines(): # xreadlines doesn't return unicode strings when codec.open() is used
|
||||||
if commentPrefix:
|
if commentPrefix:
|
||||||
|
|
|
@ -42,3 +42,15 @@ class HTTPMETHOD:
|
||||||
class NULLCONNECTION:
|
class NULLCONNECTION:
|
||||||
HEAD = "HEAD"
|
HEAD = "HEAD"
|
||||||
RANGE = "Range"
|
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)
|
||||||
|
|
|
@ -7,13 +7,26 @@ Copyright (c) 2006-2010 sqlmap developers (http://sqlmap.sourceforge.net/)
|
||||||
See the file 'doc/COPYING' for copying permission
|
See the file 'doc/COPYING' for copying permission
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import re
|
||||||
|
import time
|
||||||
|
|
||||||
from hashlib import md5
|
from hashlib import md5
|
||||||
from hashlib import sha1
|
from hashlib import sha1
|
||||||
|
from zipfile import ZipFile
|
||||||
|
|
||||||
from extra.pydes.pyDes import des
|
from extra.pydes.pyDes import des
|
||||||
from extra.pydes.pyDes import CBC
|
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 hexdecode
|
||||||
from lib.core.convert import hexencode
|
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):
|
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)
|
>>> oracle_old_passwd(password='tiger', username='scott', uppercase=True)
|
||||||
'F894844C34402B67'
|
'F894844C34402B67'
|
||||||
"""
|
"""
|
||||||
|
|
||||||
IV, pad = "\0"*8, "\0"
|
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())
|
unistr = "".join("\0%s" % c for c in (username + password).upper())
|
||||||
|
|
||||||
cipher = des(hexdecode("0123456789ABCDEF"), CBC, IV, pad)
|
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:])
|
retVal = hexencode(encrypted[-8:])
|
||||||
|
|
||||||
return retVal.upper() if uppercase else retVal.lower()
|
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)
|
||||||
|
|
|
@ -47,6 +47,7 @@ from lib.techniques.brute.use import tableExists
|
||||||
from lib.techniques.error.test import errorTest
|
from lib.techniques.error.test import errorTest
|
||||||
from lib.techniques.inband.union.test import unionTest
|
from lib.techniques.inband.union.test import unionTest
|
||||||
from lib.techniques.outband.stacked import stackedTest
|
from lib.techniques.outband.stacked import stackedTest
|
||||||
|
from lib.utils.hash import dictionaryAttack
|
||||||
|
|
||||||
class Enumeration:
|
class Enumeration:
|
||||||
"""
|
"""
|
||||||
|
@ -329,6 +330,16 @@ class Enumeration:
|
||||||
errMsg += "hashes for the database users"
|
errMsg += "hashes for the database users"
|
||||||
raise sqlmapNoneDataException, errMsg
|
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
|
return kb.data.cachedUsersPasswords
|
||||||
|
|
||||||
def __isAdminFromPrivileges(self, privileges):
|
def __isAdminFromPrivileges(self, privileges):
|
||||||
|
|
277953
txt/wordlist.txt
Normal file
277953
txt/wordlist.txt
Normal file
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Loading…
Reference in New Issue
Block a user