sqlmap/lib/utils/hash.py

313 lines
11 KiB
Python
Raw Normal View History

#!/usr/bin/env python
"""
$Id$
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
2010-11-23 17:50:47 +03:00
from lib.core.common import checkFile
from lib.core.common import conf
from lib.core.common import dataToStdout
2010-11-23 22:10:34 +03:00
from lib.core.common import getConsoleWidth
from lib.core.common import getFileItems
from lib.core.common import getPublicTypeMembers
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):
"""
Reference(s):
http://csl.sublevel3.org/mysql-password-function/
>>> mysql_passwd(password='testpass', uppercase=True)
'*00E247AC5F9AF26AE0194B41E1E769DEE1429A29'
"""
retVal = "*%s" % sha1(sha1(password).digest()).hexdigest()
return retVal.upper() if uppercase else retVal.lower()
def mysql_old_passwd(password, uppercase=True): # prior to version '4.1'
"""
Reference(s):
http://www.sfr-fresh.com/unix/privat/tpop3d-1.5.5.tar.gz:a/tpop3d-1.5.5/password.c
http://voidnetwork.org/5ynL0rd/darkc0de/python_script/darkMySQLi.html
>>> mysql_old_passwd(password='testpass', uppercase=True)
'7DCDA0D57290B453'
"""
a, b, c = 1345345333, 7, 0x12345671
for d in password:
if d == ' ' or d == '\t':
continue
e = ord(d)
a ^= (((a & 63) + b) * e) + (a << 8)
c += (c << 8) ^ a
b += e
retVal = "%08lx%08lx" % (a & ((1 << 31) - 1), c & ((1 << 31) - 1))
return retVal.upper() if uppercase else retVal.lower()
def postgres_passwd(password, username, uppercase=False):
"""
Reference(s):
http://pentestmonkey.net/blog/cracking-postgres-hashes/
>>> postgres_passwd(password='testpass', username='testuser', uppercase=False)
'md599e5ea7a6f7c3269995cba3927fd0093'
"""
retVal = "md5%s" % md5(password + username).hexdigest()
return retVal.upper() if uppercase else retVal.lower()
def mssql_passwd(password, salt, uppercase=False):
"""
Reference(s):
http://www.leidecker.info/projects/phrasendrescher/mssql.c
https://www.evilfingers.com/tools/GSAuditor.php
>>> mssql_passwd(password='testpass', salt='4086ceb6', uppercase=False)
'0x01004086ceb60c90646a8ab9889fe3ed8e5c150b5460ece8425a'
"""
binsalt = hexdecode(salt)
unistr = "".join("%s\0" % c for c in password)
retVal = "0100%s%s" % (salt, sha1(unistr + binsalt).hexdigest())
return "0x%s" % (retVal.upper() if uppercase else retVal.lower())
def mssql_old_passwd(password, salt, uppercase=True): # prior to version '2005'
"""
Reference(s):
www.exploit-db.com/download_pdf/15537/
http://www.leidecker.info/projects/phrasendrescher/mssql.c
https://www.evilfingers.com/tools/GSAuditor.php
>>> mssql_old_passwd(password='testpass', salt='4086ceb6', uppercase=True)
'0x01004086CEB60C90646A8AB9889FE3ED8E5C150B5460ECE8425AC7BB7255C0C81D79AA5D0E93D4BB077FB9A51DA0'
"""
binsalt = hexdecode(salt)
unistr = "".join("%s\0" % c for c in password)
retVal = "0100%s%s%s" % (salt, sha1(unistr + binsalt).hexdigest(), sha1(unistr.upper() + binsalt).hexdigest())
return "0x%s" % (retVal.upper() if uppercase else retVal.lower())
def oracle_passwd(password, salt, uppercase=True):
"""
Reference(s):
https://www.evilfingers.com/tools/GSAuditor.php
http://www.notesbit.com/index.php/scripts-oracle/oracle-11g-new-password-algorithm-is-revealed-by-seclistsorg/
http://seclists.org/bugtraq/2007/Sep/304
>>> oracle_passwd(password='SHAlala', salt='1B7B5F82B7235E9E182C', uppercase=True)
'S:2BFCFDF5895014EE9BB2B9BA067B01E0389BB5711B7B5F82B7235E9E182C'
"""
binsalt = hexdecode(salt)
retVal="s:%s%s" % (sha1(password + binsalt).hexdigest(), salt)
return retVal.upper() if uppercase else retVal.lower()
def oracle_old_passwd(password, username, uppercase=True): # prior to version '11g'
"""
Reference(s):
http://www.notesbit.com/index.php/scripts-oracle/oracle-11g-new-password-algorithm-is-revealed-by-seclistsorg/
>>> 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)
encrypted = cipher.encrypt(unistr)
cipher = des(encrypted[-8:], CBC, IV, pad)
encrypted = cipher.encrypt(unistr)
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__ = {
2010-11-23 22:21:30 +03:00
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]
2010-11-23 17:50:47 +03:00
for name, regex in getPublicTypeMembers(HASH):
2010-11-23 22:21:30 +03:00
#hashes for Oracle and old MySQL look the same hence these checks
2010-11-23 17:50:47 +03:00
if kb.dbms == DBMS.ORACLE and regex == HASH.MYSQL_OLD:
continue
2010-11-23 22:21:30 +03:00
2010-11-23 17:50:47 +03:00
elif kb.dbms == DBMS.MYSQL and regex == HASH.ORACLE_OLD:
continue
2010-11-23 22:21:30 +03:00
2010-11-23 17:50:47 +03:00
elif re.match(regex, hash_):
rehash = regex
2010-11-23 17:50:47 +03:00
infoMsg = "using hash method: '%s'" % name
logger.info(infoMsg)
break
2010-11-23 17:50:47 +03:00
if rehash:
break
if rehash:
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()
2010-11-23 17:50:47 +03:00
if rehash in (HASH.MYSQL, HASH.MYSQL_OLD, HASH.MD5_GENERIC, HASH.SHA1_GENERIC):
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]}])
2010-11-23 17:50:47 +03:00
if rehash == 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
dictpath = readInput(message, default=paths.ORACLE_DEFAULT_PASSWD)
else:
message = "what's the dictionary's location? [%s]" % paths.WORDLIST
dictpath = readInput(message, default=paths.WORDLIST)
checkFile(dictpath)
infoMsg = "loading dictionary from: '%s'" % dictpath
logger.info(infoMsg)
2010-11-23 17:50:47 +03:00
wordlist = getFileItems(dictpath, None, False)
infoMsg = "starting dictionary attack"
logger.info(infoMsg)
length = len(wordlist)
2010-11-23 17:50:47 +03:00
if rehash in (HASH.MYSQL, HASH.MYSQL_OLD, HASH.MD5_GENERIC, HASH.SHA1_GENERIC):
count = 0
for word in wordlist:
count += 1
current = __functions__[rehash](password = word, uppercase = False)
for item in attack_info:
((user, hash_), _) = item
if hash_ == current:
results.append((user, hash_, word))
dataToStdout("\r[%s] [INFO] found: %s%s\n" % (time.strftime("%X"), word, 40*' '), True)
attack_info.remove(item)
elif count % 1117 == 0 or count == length or rehash in (HASH.ORACLE_OLD):
status = '%d/%d words (%d%s)' % (count, length, round(100.0*count/length), '%')
2010-11-23 22:10:34 +03:00
dataToStdout("\r[%s] [INFO] %s" % (time.strftime("%X"), status))
2010-11-23 22:10:34 +03:00
dataToStdout("\r%s\r" % (" "*(getConsoleWidth()-1)))
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 hash_ == current:
results.append((user, hash_, word))
2010-11-23 22:10:34 +03:00
dataToStdout("\r[%s] [INFO] found: %s%s\n" % (time.strftime("%X"), word, 40*' '), True)
break
elif count % 1117 == 0 or count == length or rehash in (HASH.ORACLE_OLD):
status = '%d/%d words (%d%s) (user: %s)' % (count, length, round(100.0*count/length), '%', user)
2010-11-23 22:10:34 +03:00
dataToStdout("\r[%s] [INFO] %s" % (time.strftime("%X"), status))
2010-11-23 22:10:34 +03:00
dataToStdout("\r%s\r" % (" "*(getConsoleWidth()-1)))
2010-11-23 17:50:47 +03:00
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 password: %s" % ('\n' if kb.data.cachedUsersPasswords[user][i][-1] != '\n' else '', password)
2010-11-23 17:50:47 +03:00
else:
2010-11-23 17:57:36 +03:00
warnMsg = "unknown hash format. "
warnMsg += "Please report by e-mail to sqlmap-users@lists.sourceforge.net."
logger.warn(warnMsg)