implementing WordPress phpass hash cracking routine

This commit is contained in:
Miroslav Stampar 2011-11-20 19:10:46 +00:00
parent f1979936c8
commit e1a92d59de
4 changed files with 91 additions and 36 deletions

View File

@ -73,6 +73,7 @@ class HASH:
MD5_GENERIC = r'(?i)\A[0-9a-f]{32}\Z' MD5_GENERIC = r'(?i)\A[0-9a-f]{32}\Z'
SHA1_GENERIC = r'(?i)\A[0-9a-f]{40}\Z' SHA1_GENERIC = r'(?i)\A[0-9a-f]{40}\Z'
CRYPT_GENERIC = r'(?i)\A[./0-9A-Za-z]{13}\Z' CRYPT_GENERIC = r'(?i)\A[./0-9A-Za-z]{13}\Z'
WORDPRESS = r'(?i)\A\$P\$[./0-9A-Za-z]{31}\Z'
# Reference: http://www.zytrax.com/tech/web/mobile_ids.html # Reference: http://www.zytrax.com/tech/web/mobile_ids.html
class MOBILES: class MOBILES:

View File

@ -365,6 +365,9 @@ REFLECTIVE_MISS_THRESHOLD = 20
# Regular expression used for extracting HTML title # Regular expression used for extracting HTML title
HTML_TITLE_REGEX = "<title>(?P<result>[^<]+)</title>" HTML_TITLE_REGEX = "<title>(?P<result>[^<]+)</title>"
# Table used for Base64 conversion in WordPress hash cracking routine
ITOA64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
# Chars used to quickly distinguish if the user provided tainted parameter values # Chars used to quickly distinguish if the user provided tainted parameter values
DUMMY_SQL_INJECTION_CHARS = ";()'" DUMMY_SQL_INJECTION_CHARS = ";()'"
@ -403,3 +406,6 @@ UNION_CHAR_REGEX = r'\A\w+\Z'
# Attribute used for storing original parameter value in special cases (e.g. POST) # Attribute used for storing original parameter value in special cases (e.g. POST)
UNENCODED_ORIGINAL_VALUE = 'original' UNENCODED_ORIGINAL_VALUE = 'original'
# Common column names containing usernames (used for hash cracking in some cases)
COMMON_USER_COLUMNS = ('user', 'username', 'user_name', 'benutzername', 'benutzer', 'utilisateur', 'usager', 'consommateur', 'utente', 'utilizzatore', 'usufrutuario', 'korisnik', 'usuario', 'consumidor')

View File

@ -58,10 +58,12 @@ from lib.core.enums import HASH
from lib.core.exception import sqlmapFilePathException from lib.core.exception import sqlmapFilePathException
from lib.core.exception import sqlmapUserQuitException from lib.core.exception import sqlmapUserQuitException
from lib.core.settings import COMMON_PASSWORD_SUFFIXES from lib.core.settings import COMMON_PASSWORD_SUFFIXES
from lib.core.settings import COMMON_USER_COLUMNS
from lib.core.settings import DUMMY_USER_PREFIX from lib.core.settings import DUMMY_USER_PREFIX
from lib.core.settings import GENERAL_IP_ADDRESS_REGEX from lib.core.settings import GENERAL_IP_ADDRESS_REGEX
from lib.core.settings import HASH_MOD_ITEM_DISPLAY from lib.core.settings import HASH_MOD_ITEM_DISPLAY
from lib.core.settings import IS_WIN from lib.core.settings import IS_WIN
from lib.core.settings import ITOA64
from lib.core.settings import PYVERSION from lib.core.settings import PYVERSION
from lib.core.settings import ML from lib.core.settings import ML
from lib.core.settings import UNICODE_ENCODING from lib.core.settings import UNICODE_ENCODING
@ -214,6 +216,7 @@ def sha1_generic_passwd(password, uppercase=False):
return retVal.upper() if uppercase else retVal.lower() return retVal.upper() if uppercase else retVal.lower()
def crypt_generic_passwd(password, salt, uppercase=False): def crypt_generic_passwd(password, salt, uppercase=False):
""" """
Reference(s): Reference(s):
@ -230,6 +233,60 @@ def crypt_generic_passwd(password, salt, uppercase=False):
return retVal.upper() if uppercase else retVal return retVal.upper() if uppercase else retVal
def wordpress_passwd(password, salt, count, prefix, uppercase=False):
"""
Reference(s):
http://packetstormsecurity.org/files/74448/phpassbrute.py.txt
http://scriptserver.mainframe8.com/wordpress_password_hasher.php
>>> wordpress_passwd(password='testpass', salt='dYPSjeF4', count=2048)
''
"""
def _encode64(input_, count):
output = ''
i = 0
while i < count:
value = ord(input_[i])
i += 1
output = output + ITOA64[value & 0x3f]
if i < count:
value = value | (ord(input_[i]) << 8)
output = output + ITOA64[(value>>6) & 0x3f]
i += 1
if i >= count:
break
if i < count:
value = value | (ord(input_[i]) << 16)
output = output + ITOA64[(value>>12) & 0x3f]
i += 1
if i >= count:
break
output = output + ITOA64[(value>>18) & 0x3f]
return output
cipher = md5(salt)
cipher.update(password)
hash_ = cipher.digest()
for i in xrange(count):
_ = md5(hash_)
_.update(password)
hash_ = _.digest()
retVal = prefix + _encode64(hash_, 16)
return retVal.upper() if uppercase else retVal
__functions__ = { __functions__ = {
HASH.MYSQL: mysql_passwd, HASH.MYSQL: mysql_passwd,
HASH.MYSQL_OLD: mysql_old_passwd, HASH.MYSQL_OLD: mysql_old_passwd,
@ -240,7 +297,8 @@ __functions__ = {
HASH.ORACLE_OLD: oracle_old_passwd, HASH.ORACLE_OLD: oracle_old_passwd,
HASH.MD5_GENERIC: md5_generic_passwd, HASH.MD5_GENERIC: md5_generic_passwd,
HASH.SHA1_GENERIC: sha1_generic_passwd, HASH.SHA1_GENERIC: sha1_generic_passwd,
HASH.CRYPT_GENERIC: crypt_generic_passwd HASH.CRYPT_GENERIC: crypt_generic_passwd,
HASH.WORDPRESS: wordpress_passwd
} }
def attackCachedUsersPasswords(): def attackCachedUsersPasswords():
@ -268,7 +326,7 @@ def attackDumpedTable():
attack_dict = {} attack_dict = {}
for column in columns: for column in columns:
if column and column.lower() in ('user', 'username', 'user_name'): if column and column.lower() in COMMON_USER_COLUMNS:
colUser = column colUser = column
break break
@ -385,7 +443,7 @@ def __bruteProcessVariantA(attack_info, hash_regex, wordlist, suffix, retVal, pr
attack_info.remove(item) attack_info.remove(item)
elif proc_id == 0 and count % HASH_MOD_ITEM_DISPLAY == 0 or hash_regex == HASH.ORACLE_OLD or hash_regex == HASH.CRYPT_GENERIC and IS_WIN: elif (proc_id == 0 or getattr(proc_count, 'value', 0) == 1) and count % HASH_MOD_ITEM_DISPLAY == 0 or hash_regex == HASH.ORACLE_OLD or hash_regex == HASH.CRYPT_GENERIC and IS_WIN:
rotator += 1 rotator += 1
if rotator >= len(ROTATING_CHARS): if rotator >= len(ROTATING_CHARS):
rotator = 0 rotator = 0
@ -404,6 +462,10 @@ def __bruteProcessVariantA(attack_info, hash_regex, wordlist, suffix, retVal, pr
except KeyboardInterrupt: except KeyboardInterrupt:
pass pass
finally:
if hasattr(proc_count, 'value'):
proc_count.value -= 1
def __bruteProcessVariantB(user, hash_, kwargs, hash_regex, wordlist, suffix, retVal, found, proc_id, proc_count): def __bruteProcessVariantB(user, hash_, kwargs, hash_regex, wordlist, suffix, retVal, found, proc_id, proc_count):
count = 0 count = 0
rotator = 0 rotator = 0
@ -441,7 +503,8 @@ def __bruteProcessVariantB(user, hash_, kwargs, hash_regex, wordlist, suffix, re
dataToStdout(infoMsg, True) dataToStdout(infoMsg, True)
found.value = True found.value = True
elif proc_id == 0 and count % HASH_MOD_ITEM_DISPLAY == 0 or hash_regex == HASH.ORACLE_OLD or hash_regex == HASH.CRYPT_GENERIC and IS_WIN:
elif (proc_id == 0 or getattr(proc_count, 'value', 0) == 1) and count % HASH_MOD_ITEM_DISPLAY == 0 or hash_regex == HASH.ORACLE_OLD or hash_regex == HASH.CRYPT_GENERIC and IS_WIN:
rotator += 1 rotator += 1
if rotator >= len(ROTATING_CHARS): if rotator >= len(ROTATING_CHARS):
rotator = 0 rotator = 0
@ -461,6 +524,9 @@ def __bruteProcessVariantB(user, hash_, kwargs, hash_regex, wordlist, suffix, re
except KeyboardInterrupt: except KeyboardInterrupt:
pass pass
finally:
if hasattr(proc_count, 'value'):
proc_count.value -= 1
def dictionaryAttack(attack_dict): def dictionaryAttack(attack_dict):
suffix_list = [""] suffix_list = [""]
@ -491,11 +557,14 @@ def dictionaryAttack(attack_dict):
if not hash_: if not hash_:
continue continue
hash_ = hash_.split()[0].lower() hash_ = hash_.split()[0]
if getCompiledRegex(hash_regex).match(hash_): if getCompiledRegex(hash_regex).match(hash_):
item = None item = None
if hash_regex not in (HASH.CRYPT_GENERIC, HASH.WORDPRESS):
hash_ = hash_.lower()
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):
item = [(user, hash_), {}] item = [(user, hash_), {}]
elif hash_regex in (HASH.ORACLE_OLD, HASH.POSTGRES): elif hash_regex in (HASH.ORACLE_OLD, HASH.POSTGRES):
@ -506,6 +575,8 @@ def dictionaryAttack(attack_dict):
item = [(user, hash_), {'salt': hash_[6:14]}] item = [(user, hash_), {'salt': hash_[6:14]}]
elif hash_regex in (HASH.CRYPT_GENERIC): elif hash_regex in (HASH.CRYPT_GENERIC):
item = [(user, hash_), {'salt': hash_[0:2]}] item = [(user, hash_), {'salt': hash_[0:2]}]
elif hash_regex in (HASH.WORDPRESS):
item = [(user, hash_), {'salt': hash_[4:12], 'count': 1<<ITOA64.index(hash_[3]), 'prefix': hash_[:12]}]
if item and hash_ not in keys: if item and hash_ not in keys:
resumed = conf.hashDB.retrieve(hash_) resumed = conf.hashDB.retrieve(hash_)
@ -545,7 +616,7 @@ def dictionaryAttack(attack_dict):
logger.info("using custom list of dictionaries") logger.info("using custom list of dictionaries")
else: else:
# It is the slowest of all methods hence smaller default dict # It is the slowest of all methods hence smaller default dict
if hash_regex == HASH.ORACLE_OLD: if hash_regex in (HASH.ORACLE_OLD, HASH.WORDPRESS):
dictPaths = [paths.SMALL_DICT] dictPaths = [paths.SMALL_DICT]
else: else:
dictPaths = [paths.WORDLIST] dictPaths = [paths.WORDLIST]
@ -602,8 +673,10 @@ def dictionaryAttack(attack_dict):
singleTimeLogMessage(infoMsg) singleTimeLogMessage(infoMsg)
retVal = _multiprocessing.Queue() retVal = _multiprocessing.Queue()
count = _multiprocessing.Value('i', _multiprocessing.cpu_count())
for i in xrange(_multiprocessing.cpu_count()): for i in xrange(_multiprocessing.cpu_count()):
p = _multiprocessing.Process(target=__bruteProcessVariantA, args=(attack_info, hash_regex, kb.wordlist, suffix, retVal, i, _multiprocessing.cpu_count())) p = _multiprocessing.Process(target=__bruteProcessVariantA, args=(attack_info, hash_regex, kb.wordlist, suffix, retVal, i, count))
processes.append(p) processes.append(p)
for p in processes: for p in processes:
@ -671,9 +744,10 @@ def dictionaryAttack(attack_dict):
retVal = _multiprocessing.Queue() retVal = _multiprocessing.Queue()
found_ = _multiprocessing.Value('i', False) found_ = _multiprocessing.Value('i', False)
count = _multiprocessing.Value('i', _multiprocessing.cpu_count())
for i in xrange(_multiprocessing.cpu_count()): for i in xrange(_multiprocessing.cpu_count()):
p = _multiprocessing.Process(target=__bruteProcessVariantB, args=(user, hash_, kwargs, hash_regex, kb.wordlist, suffix, retVal, found_, i, _multiprocessing.cpu_count())) p = _multiprocessing.Process(target=__bruteProcessVariantB, args=(user, hash_, kwargs, hash_regex, kb.wordlist, suffix, retVal, found_, i, count))
processes.append(p) processes.append(p)
for p in processes: for p in processes:

View File

@ -168,7 +168,6 @@ adi
adidas adidas
adldemo adldemo
admin admin
admin
admin1 admin1
administrator administrator
adrian adrian
@ -402,7 +401,6 @@ bella
belle belle
belmont belmont
ben ben
ben
benjamin benjamin
benji benji
benny benny
@ -634,7 +632,6 @@ castle
cat cat
catalina catalina
catalog catalog
catalog
catch22 catch22
catfish catfish
catherine catherine
@ -929,7 +926,6 @@ delete
deliver deliver
delta delta
demo demo
demo
demo8 demo8
demo9 demo9
demon demon
@ -1143,7 +1139,6 @@ fidel
Figaro Figaro
fii fii
finance finance
finance
finprod finprod
fiona fiona
fire fire
@ -1473,7 +1468,6 @@ hithere
hitler hitler
hlw hlw
hobbes hobbes
hobbes
hobbit hobbit
hockey hockey
hola hola
@ -1685,7 +1679,6 @@ joey
johan johan
johanna1 johanna1
john john
john
john316 john316
johnny johnny
johnson johnson
@ -2015,7 +2008,6 @@ mddemo
mddemo_mgr mddemo_mgr
mdsys mdsys
me me
me
meatloaf meatloaf
mech mech
mechanic mechanic
@ -2081,7 +2073,6 @@ milano
miles miles
millenium millenium
miller miller
miller
millie millie
million million
mimi mimi
@ -2402,13 +2393,11 @@ pascal
pass pass
passion passion
passwd passwd
passwd
passwo1 passwo1
passwo2 passwo2
passwo3 passwo3
passwo4 passwo4
password password
password
Password Password
pat pat
patches patches
@ -2418,7 +2407,6 @@ patriots
patrol patrol
patton patton
paul paul
paul
paula paula
pauline pauline
pavel pavel
@ -2974,7 +2962,6 @@ snow
snowball snowball
snowflake snowflake
snowman snowman
snowman
snowski snowski
snuffy snuffy
sober1 sober1
@ -3036,13 +3023,11 @@ starlight
stars stars
start start
starter starter
starter
startrek startrek
starwars starwars
station station
stealth stealth
steel steel
steel
steele steele
steelers steelers
stella stella
@ -3114,7 +3099,6 @@ Superman
supersecret supersecret
superstar superstar
support support
support
supra supra
surf surf
surfer surfer
@ -3138,7 +3122,6 @@ swimming
switzer switzer
Swoosh Swoosh
swordfish swordfish
swordfish
swpro swpro
swuser swuser
sydney sydney
@ -3153,7 +3136,6 @@ sysman
syspass syspass
sys_stnt sys_stnt
system system
system
system5 system5
systempass systempass
tab tab
@ -3205,7 +3187,6 @@ terminal
terry terry
terry1 terry1
test test
test
test1 test1
test123 test123
test2 test2
@ -3213,6 +3194,7 @@ test3
tester tester
testi testi
testing testing
testpass
testpilot testpilot
testtest testtest
test_user test_user
@ -3243,11 +3225,9 @@ thx1138
tibco tibco
tiffany tiffany
tiger tiger
tiger
tiger2 tiger2
tigers tigers
tigger tigger
tigger
Tigger Tigger
tightend tightend
tigre tigre
@ -3291,7 +3271,6 @@ transport
trapper trapper
trash trash
travel travel
travel
travis travis
tre tre
treasure treasure
@ -3333,7 +3312,6 @@ twins
tyler tyler
tyler1 tyler1
ultimate ultimate
ultimate
um_admin um_admin
um_client um_client
undead undead
@ -3348,7 +3326,6 @@ ursula
user user
user0 user0
user1 user1
user1
user2 user2
user3 user3
user4 user4
@ -3447,7 +3424,6 @@ Webster
wedge wedge
weezer weezer
welcome welcome
welcome
wendy wendy
wendy1 wendy1
wesley wesley
@ -3522,7 +3498,6 @@ wright
wsh wsh
wsm wsm
www www
www
wwwuser wwwuser
xademo xademo
xanadu xanadu
@ -3542,7 +3517,6 @@ xns
xprt xprt
xtr xtr
xxx xxx
xxx
xxx123 xxx123
xxxx xxxx
xxxxxx xxxxxx