Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit ba4ea32

Browse files
committed
first working version of dictionary attack
1 parent c471b81 commit ba4ea32

6 files changed

Lines changed: 278109 additions & 3 deletions

File tree

lib/core/common.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -662,6 +662,7 @@ def setPaths():
662662
paths.COMMON_TABLES = os.path.join(paths.SQLMAP_TXT_PATH, "common-tables.txt")
663663
paths.COMMON_OUTPUTS = os.path.join(paths.SQLMAP_TXT_PATH, 'common-outputs.txt')
664664
paths.SQL_KEYWORDS = os.path.join(paths.SQLMAP_TXT_PATH, "keywords.txt")
665+
paths.WORDLIST_TXT = os.path.join(paths.SQLMAP_TXT_PATH, "wordlist.txt")
665666
paths.PHPIDS_RULES_XML = os.path.join(paths.SQLMAP_XML_PATH, "phpids_rules.xml")
666667
paths.ERRORS_XML = os.path.join(paths.SQLMAP_XML_PATH, "errors.xml")
667668
paths.INJECTIONS_XML = os.path.join(paths.SQLMAP_XML_PATH, "injections.xml")
@@ -1325,11 +1326,15 @@ def initCommonOutputs():
13251326

13261327
cfile.close()
13271328

1328-
def getFileItems(filename, commentPrefix='#'):
1329+
def getFileItems(filename, commentPrefix='#', unicode_=True):
13291330
retVal = []
13301331

13311332
checkFile(filename)
1332-
ifile = codecs.open(filename, 'r', conf.dataEncoding)
1333+
1334+
if unicode_:
1335+
ifile = codecs.open(filename, 'r', conf.dataEncoding)
1336+
else:
1337+
ifile = open(filename, 'r')
13331338

13341339
for line in ifile.readlines(): # xreadlines doesn't return unicode strings when codec.open() is used
13351340
if commentPrefix:

lib/core/enums.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,3 +42,15 @@ class HTTPMETHOD:
4242
class NULLCONNECTION:
4343
HEAD = "HEAD"
4444
RANGE = "Range"
45+
46+
class HASH:
47+
MYSQL = r'(?i)\A\*[0-9a-f]{40}\Z'
48+
MYSQL_OLD = r'(?i)\A[0-9a-f]{16}\Z'
49+
POSTGRES = r'(?i)\Amd5[0-9a-f]{32}\Z'
50+
MSSQL = r'(?i)\A0x0100[0-9a-f]{8}[0-9a-f]{40}\Z'
51+
MSSQL_OLD = r'(?i)\A0x0100[0-9a-f]{8}[0-9a-f]{80}\Z'
52+
ORACLE = r'(?i)\As:[0-9a-f]{60}\Z'
53+
ORACLE_OLD = r'(?i)\A[0-9a-f]{16}\Z'
54+
MD5_GENERIC = r'(?i)\A[0-9a-f]{32}\Z'
55+
SHA1_GENERIC = r'(?i)\A[0-9a-f]{40}\Z'
56+
__all__ = (MYSQL, MYSQL_OLD, POSTGRES, MSSQL, MSSQL_OLD, ORACLE, ORACLE_OLD, MD5_GENERIC, SHA1_GENERIC)

lib/utils/hash.py

Lines changed: 126 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,26 @@
77
See the file 'doc/COPYING' for copying permission
88
"""
99

10+
import re
11+
import time
12+
1013
from hashlib import md5
1114
from hashlib import sha1
15+
from zipfile import ZipFile
1216

1317
from extra.pydes.pyDes import des
1418
from extra.pydes.pyDes import CBC
19+
from lib.core.common import conf
20+
from lib.core.common import dataToStdout
21+
from lib.core.common import getFileItems
22+
from lib.core.common import paths
23+
from lib.core.common import readInput
1524
from lib.core.convert import hexdecode
1625
from lib.core.convert import hexencode
26+
from lib.core.data import kb
27+
from lib.core.data import logger
28+
from lib.core.enums import DBMS
29+
from lib.core.enums import HASH
1730

1831
def mysql_passwd(password, uppercase=True):
1932
"""
@@ -126,8 +139,8 @@ def oracle_old_passwd(password, username, uppercase=True): # prior to version '1
126139
>>> oracle_old_passwd(password='tiger', username='scott', uppercase=True)
127140
'F894844C34402B67'
128141
"""
129-
130142
IV, pad = "\0"*8, "\0"
143+
username = unicode.encode(username, conf.dataEncoding) #pyDes has issues with unicode strings
131144
unistr = "".join("\0%s" % c for c in (username + password).upper())
132145

133146
cipher = des(hexdecode("0123456789ABCDEF"), CBC, IV, pad)
@@ -138,3 +151,115 @@ def oracle_old_passwd(password, username, uppercase=True): # prior to version '1
138151
retVal = hexencode(encrypted[-8:])
139152

140153
return retVal.upper() if uppercase else retVal.lower()
154+
155+
def md5_generic_passwd(password, uppercase=False):
156+
"""
157+
>>> md5_generic_passwd(password='testpass', uppercase=False)
158+
'179ad45c6ce2cb97cf1029e212046e81'
159+
"""
160+
161+
retVal = md5(password).hexdigest()
162+
163+
return retVal.upper() if uppercase else retVal.lower()
164+
165+
def sha1_generic_passwd(password, uppercase=False):
166+
"""
167+
>>> sha1_generic_passwd(password='testpass', uppercase=False)
168+
'206c80413b9a96c1312cc346b7d2517b84463edd'
169+
"""
170+
171+
retVal = sha1(password).hexdigest()
172+
173+
return retVal.upper() if uppercase else retVal.lower()
174+
175+
__functions__ = {
176+
HASH.MYSQL: mysql_passwd, HASH.MYSQL_OLD: mysql_old_passwd, HASH.POSTGRES: postgres_passwd,
177+
HASH.MSSQL: mssql_passwd, HASH.MSSQL_OLD: mssql_old_passwd, HASH.ORACLE: oracle_passwd,
178+
HASH.ORACLE_OLD: oracle_old_passwd, HASH.MD5_GENERIC: md5_generic_passwd, HASH.SHA1_GENERIC: sha1_generic_passwd
179+
}
180+
181+
def dictionaryAttack():
182+
rehash = None
183+
attack_info = []
184+
results = []
185+
186+
for (_, hashes) in kb.data.cachedUsersPasswords.items():
187+
for hash_ in hashes:
188+
if not hash_:
189+
continue
190+
191+
hash_ = hash_.split()[0]
192+
193+
for regex in HASH.__all__:
194+
if re.match(regex, hash_):
195+
rehash = regex
196+
break
197+
198+
if rehash:
199+
for (user, hashes) in kb.data.cachedUsersPasswords.items():
200+
for hash_ in hashes:
201+
if not hash_:
202+
continue
203+
204+
hash_ = hash_.split()[0]
205+
206+
if re.match(rehash, hash_):
207+
hash_ = hash_.lower()
208+
209+
if rehash in (HASH.MYSQL, HASH.MYSQL_OLD, HASH.MD5_GENERIC, HASH.SHA1_GENERIC) and kb.dbms != DBMS.ORACLE:
210+
attack_info.append([(user, hash_), {}])
211+
elif rehash in (HASH.ORACLE_OLD, HASH.POSTGRES):
212+
attack_info.append([(user, hash_), {'username': user}])
213+
elif rehash in (HASH.ORACLE):
214+
attack_info.append([(user, hash_), {'salt': hash_[-20:]}])
215+
elif rehash in (HASH.MSSQL, HASH.MSSQL_OLD):
216+
attack_info.append([(user, hash_), {'salt': hash_[6:14]}])
217+
218+
infoMsg = "loading dictionary from: '%s'" % paths.WORDLIST_TXT
219+
logger.info(infoMsg)
220+
wordlist = getFileItems(paths.WORDLIST_TXT, None, False)
221+
222+
infoMsg = "running dictionary attack"
223+
logger.info(infoMsg)
224+
225+
length = len(wordlist)
226+
227+
if rehash in (HASH.MYSQL, HASH.MYSQL_OLD, HASH.MD5_GENERIC, HASH.SHA1_GENERIC) and kb.dbms != DBMS.ORACLE:
228+
count = 0
229+
for word in wordlist:
230+
count += 1
231+
current = __functions__[rehash](password = word, uppercase = False)
232+
for item in attack_info:
233+
((user, hash_), _) = item
234+
235+
if count % 1117 == 0 or count == length:
236+
status = '%d/%d words (%d%s)' % (count, length, round(100.0*count/length), '%')
237+
dataToStdout("\r[%s] [INFO] %s" % (time.strftime("%X"), status), True)
238+
239+
if hash_ == current:
240+
results.append((user, hash_, word))
241+
#dataToStdout("\r[%s] [INFO] found: %s:%s\n" % (time.strftime("%X"), user, word), True)
242+
attack_info.remove(item)
243+
244+
else:
245+
for ((user, hash_), kwargs) in attack_info:
246+
count = 0
247+
for word in wordlist:
248+
current = __functions__[rehash](password = word, uppercase = False, **kwargs)
249+
250+
count += 1
251+
if count % 1117 == 0 or count == length:
252+
status = '%d/%d words (%d%s)' % (count, length, round(100.0*count/length), '%')
253+
dataToStdout("\r[%s] [INFO] %s" % (time.strftime("%X"), status), True)
254+
255+
if hash_ == current:
256+
results.append((user, hash_, word))
257+
#dataToStdout("\r[%s] [INFO] found: %s:%s\n" % (time.strftime("%X"), user, word), True)
258+
break
259+
260+
dataToStdout("\n", True)
261+
blank = " "
262+
for (user, hash_, password) in results:
263+
for i in xrange(len(kb.data.cachedUsersPasswords[user])):
264+
if kb.data.cachedUsersPasswords[user][i] and hash_.lower() in kb.data.cachedUsersPasswords[user][i].lower():
265+
kb.data.cachedUsersPasswords[user][i] += "%s%spassword: %s" % ('\n' if kb.data.cachedUsersPasswords[user][i][-1] != '\n' else '', blank, password)

plugins/generic/enumeration.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
from lib.techniques.error.test import errorTest
4848
from lib.techniques.inband.union.test import unionTest
4949
from lib.techniques.outband.stacked import stackedTest
50+
from lib.utils.hash import dictionaryAttack
5051

5152
class Enumeration:
5253
"""
@@ -329,6 +330,16 @@ def getPasswordHashes(self):
329330
errMsg += "hashes for the database users"
330331
raise sqlmapNoneDataException, errMsg
331332

333+
message = "do you want to use dictionary attack on retrieved password hashes? [Y/n/q]"
334+
test = readInput(message, default="Y")
335+
336+
if test[0] in ("n", "N"):
337+
pass
338+
elif test[0] in ("q", "Q"):
339+
raise sqlmapUserQuitException
340+
else:
341+
dictionaryAttack()
342+
332343
return kb.data.cachedUsersPasswords
333344

334345
def __isAdminFromPrivileges(self, privileges):

0 commit comments

Comments
 (0)