diff --git a/dshashes.py b/dshashes.py
new file mode 100644
index 0000000..1c32d90
--- /dev/null
+++ b/dshashes.py
@@ -0,0 +1,147 @@
+# This file was derived from dsusers.py, which is is part of ntdsxtract.
+#
+# ntdsxtract is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# ntdsxtract is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with ntdsxtract. If not, see .
+
+'''
+@editor: LaNMaSteR53,woanware
+@author: Csaba Barta
+@license: GNU General Public License 2.0 or later
+@contact: csaba.barta@gmail.com
+'''
+
+from ntds.dsdatabase import *
+from ntds.dsrecord import *
+from ntds.dslink import *
+from ntds.dstime import *
+from ntds.dsobjects import *
+
+def usage():
+ print "DSHashes"
+ print "Extracts user hashes in a user-friendly format\n"
+ print "usage: %s [options]" % sys.argv[0]
+ print " options:"
+ print " --rid "
+ print " List user identified by RID"
+ print " --name "
+ print " List user identified by Name"
+ print " --passwordhashes "
+ print " Extract password hashes"
+ print " --passwordhistory "
+ print " Extract password history"
+ print " --exclude-disabled"
+ print " Exclude disabled accounts from output"
+
+if len(sys.argv) < 4:
+ usage()
+ sys.exit(1)
+
+rid = -1
+name = ""
+syshive = ""
+pwdump = False
+pwhdump = False
+optid = 0
+excl_dsbl = False
+print "Running with options:"
+for opt in sys.argv:
+ if opt == "--rid":
+ if len(sys.argv) < 5:
+ usage()
+ sys.exit(1)
+ rid = int(sys.argv[optid + 1])
+ print "\tUser RID: %d" % rid
+ if opt == "--name":
+ if len(sys.argv) < 5:
+ usage()
+ sys.exit(1)
+ name = sys.argv[optid + 1]
+ print "\tUser name: %s" % name
+ if opt == "--passwordhashes":
+ if len(sys.argv) < 5:
+ usage()
+ sys.exit(1)
+ syshive = sys.argv[optid + 1]
+ pwdump = True
+ print "\tExtracting password hashes"
+ if opt == "--passwordhistory":
+ if len(sys.argv) < 5:
+ usage()
+ sys.exit(1)
+ syshive = sys.argv[optid + 1]
+ pwhdump = True
+ print "\tExtracting password history"
+ if '--exclude-disabled' in sys.argv:
+ excl_dsbl = True
+ optid += 1
+
+print 'after'
+
+db = dsInitDatabase(sys.argv[1], sys.argv[3])
+dl = dsInitLinks(sys.argv[2], sys.argv[3])
+
+if pwdump or pwhdump:
+ dsInitEncryption(syshive)
+
+utype = -1
+utype = dsGetTypeIdByTypeName(db, "Person")
+if utype == -1:
+ print "Unable to get type id for Person"
+ sys.exit()
+
+print "\nList of hashes:"
+print "=============="
+for recordid in dsMapLineIdByRecordId:
+ if int(dsGetRecordType(db, recordid)) == utype:
+ user = dsUser(db, recordid)
+ if rid != -1 and user.SID.RID != rid:
+ continue
+ if name != "" and user.Name != name:
+ continue
+ if excl_dsbl:
+ user_disabled = False
+ for uac in user.getUserAccountControl():
+ if uac == 'Disabled': user_disabled = True
+ if user_disabled: continue
+
+ if pwdump == True:
+ nthash = ''
+ lmhash = 'aad3b435b51404eeaad3b435b51404ee'
+ (lm, nt) = user.getPasswordHashes()
+ if nt != '':
+ nthash = nt
+ if lm != '':
+ lmhash = lm
+ hash = "%s:%s:%s:%s:::" % (user.SAMAccountName, user.SID.RID, lmhash, nthash)
+ if nt != '':
+ print hash
+
+ if pwhdump == True:
+ lmhistory = None
+ nthistory = None
+ (lmhistory, nthistory) = user.getPasswordHistory()
+ if nthistory != None:
+ hashid = 0
+ for nthash in nthistory:
+ print "%s_nthistory%d:%s:E52CAC67419A9A224A3B108F3FA6CB6D:%s:::" % (user.SAMAccountName, hashid, user.SID.RID, nthash)
+ hashid += 1
+ if lmhistory != None:
+ hashid = 0
+ for lmhash in lmhistory:
+ print "%s_lmhistory%d:%s:%s:8846F7EAEE8FB117AD06BDD830B7586C:::" % (user.SAMAccountName, hashid, user.SID.RID, lmhash)
+ hashid += 1
+
+if pwhdump == True:
+ print "\n[*] NOTE: NT and LM hashes are shown on individual lines with the respective hash of 'password' in the opposing position."
+ print "This is done in order to make sure the output plays nice with various hash cracking tools. Account for this when cracking historical hashes.\n"
+