MDXfind is a command-line hash-cracking tool for security researchers, pen testers, etc.
MDXfind "runs large numbers of unsolved hashes, using many algorithms, against large numbers of plaintext words, very quickly" (Waffle, primary author).
MDXfind is somewhat different from other hash-cracking software (see below). The learning curve may be a little steep at first, but if you have a pile of hashes of unknown type, or a hundred million hashes that you need to pare down quickly for other attacks ... MDXfind may be the perfect tool for the job.
While MDXfind is CPU based (which may seem odd in the era of GPU cracking), don't worry -- its core features work nicely on CPU!
MDXfind is distributed as standalone binaries for the command line. There is no installer, and there is no GUI.
MDXfind should work "out of the box" on: Windows 32/64, Linux (x86 32/64, ARM6/7/8, and POWER8), FreeBSD 64, macOS (Intel or M), and Linux POWER8. See file(1) output for minimum kernel versions.
MDXfind uses the following libraries. As of version 1.130, these are included directly (no dependencies required).
If you have an update to these requirements, let me know.
This example shows a basic mdxfind run, followed by using mdsplit to remove all discovered solutions from the target.
Note: on the Windows command line, use double quotes instead of single quotes around the regex.
$ cat example-hashes.txt 5a22e6c339c96c9c0513a46e44c39683 5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8 5f4dcc3b5aa765d61d8327deb882cf99 696d29e0940a4957748fe3fc9efd22a3 7b3b4de00794a247cf8df8e6fbfe19bf 8846f7eaee8fb117ad06bdd830b7586c $ cat example-wordlist.dict password # Run the attack. All three requested hash types # are attempted in a single attack run. $ mdxfind -h '^(SHA1|MD5|NTLM)$' \ -i 3 -f example-hashes.txt example-wordlist.dict \ | tee -a example-found.res Iterations set to 3 Working on hash types: MD5 SHA1 NTLM Took 0.00 seconds to read hashes Searching through 6 unique hex hashes from example-hashes.txt Maximum hash chain depth is 1 Minimum hash length is 32 characters Using 2 cores Done - 2 threads caught 1 lines processed in 0 seconds 1.00 lines per second 0.00 seconds hashing, 11 total hash calculations 0.01M hashes per second (approx) 1 total files 1 MD5x01 hashes found 1 MD5x02 hashes found 1 MD5x03 hashes found 1 SHA1x01 hashes found 1 NTLMx01 hashes found 5 Total hashes found MD5x01 5f4dcc3b5aa765d61d8327deb882cf99:password MD5x02 696d29e0940a4957748fe3fc9efd22a3:password MD5x03 5a22e6c339c96c9c0513a46e44c39683:password SHA1x01 5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8:password NTLMx01 8846f7eaee8fb117ad06bdd830b7586c:password $ cat example-found.res MD5x01 5f4dcc3b5aa765d61d8327deb882cf99:password MD5x02 696d29e0940a4957748fe3fc9efd22a3:password MD5x03 5a22e6c339c96c9c0513a46e44c39683:password SHA1x01 5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8:password NTLMx01 8846f7eaee8fb117ad06bdd830b7586c:password # Remove all found hashes from our target file, # distributing hash:found pairs into separate files # per algorithm. # This can be done iteratively (new results will append). # Check how big the target hashfile is. $ wc -l example-hashes.txt 6 example-hashes.txt # Make a backup - we're about to change it. $ cp -p example-hashes.txt example-hashes.txt.orig # Split the founds per type; remove hashes from target. $ mdsplit -f example-found.res example-hashes.txt 5 result lines processed, 5 types found MD5x01 MD5x02 MD5x03 NTLMx01 SHA1x01 example-hashes.txt had 5 hits Total 5 hashes found # Check our split files. $ head -n 1 example-hashes.*x0* ==> example-hashes.MD5x01 <== 5f4dcc3b5aa765d61d8327deb882cf99:password ==> example-hashes.MD5x02 <== 696d29e0940a4957748fe3fc9efd22a3:password ==> example-hashes.MD5x03 <== 5a22e6c339c96c9c0513a46e44c39683:password ==> example-hashes.NTLMx01 <== 8846f7eaee8fb117ad06bdd830b7586c:password ==> example-hashes.SHA1x01 <== 5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8:password # The found hashes have been removed from the target. $ wc -l example-hashes.txt* 1 example-hashes.txt 5 example-hashes.txt.orig
(adapted from @winxp5421's talk, with each line explicated):
# Try all unsalted hash types, up to five iterations.
$ mdxfind -h ALL -h '!salt,!user,!crypt' -i 5 \
-f unknown.txt /dict/top-10k-pass.dict \
| tee -a out.res
# Search every algorithm, except ...
-h ALL
# ... skip salted explicit (!salt), implicit (!crypt)
-h '!salt,!user,!crypt'
# Iterate 1 to 5 times, inclusive.
-i 5
# Specify target hashlist.
-f unknown.txt
# Specify wordlist (input dictionary).
/dict/top-10k.pass.dict
# Append to outfile (where results go).
| tee -a out.res
(Using '| tee -a out.res' lets you watch the output; use ' > out.res' instead if you won't want to watch, or tee isn't available on your system.)
To tell MDXfind which hash types you want to try, you use use regular expressions that matches the hash types. Case-insensitivity is always implied.
To see the list of available hash types, run MDXfind with just -h with no other parameters.
At startup, you can see which hash types were selected (the "Working on hash types:" line). You can use this to check whether your regex is doing what you're expecting.
Tip: if you know you only need one or more specific hash types, your attack will be much faster if you only specify just the relevant types.
Examples:
Note: on the Windows command line, use double quotes instead of single quotes around the regex.
# Match only a single hash type. $ mdxfind -h '^MD5$' [...] # Match all hash types beginning with MD5. # (usually means MD5 was the last step in the algorithm) # Note the difference from above - no '$' at the end. $ mdxfind -h '^MD5' [...] # Only match these three specific hash types. $ mdxfind -h '^(SHA1|MD5|NTLM)$' [...] # Match all hash types beginning with these strings. # Again, note the missing '$'. $ mdxfind -h '^(SHA1|MD5|NTLM)' [...] # Match all hash types *except* any containing these string.. # Exclamation points may have special meaning in your shell, # so quoting may be necessary. mdxfind -h ALL -h '!salt,!user,!crypt,!revp,!leet' [...]
As of version 1.133, the undocumented -m flag, which lets you specify hash types using hashcat mode IDs (one or more separated by commas) now also supported a -m eNN syntax, which lets you specify an mdxfind hash type using its position in -h (1 for MD5, etc.)
By default, MDXfind will politely but thoroughly use all available cores/threads. To override this, you can use -t [N] (where N is the number of threads to use).
You can use mdxfind's -z flag to generate arbitrary hashes:
# Use NUL instead of /dev/null on Windows $ echo password | mdxfind -h "^MD5$" -z -f /dev/null -i 5 stdin 2>/dev/null MD5x01 5f4dcc3b5aa765d61d8327deb882cf99:password MD5x02 696d29e0940a4957748fe3fc9efd22a3:password MD5x03 5a22e6c339c96c9c0513a46e44c39683:password MD5x04 e777a29bee9227c8a6a86e0bad61fc40:password MD5x05 7b3b4de00794a247cf8df8e6fbfe19bf:password
Expanded examples:
$TESTVEC[bytes x count] is a way to create patterns up to 2,000,000 bytes as a candidate for any algorithm. Typically, these are used as ways to create known patterns to test algorithms.
$TESTVEC accepts one or more byte, and repeats them the specified number of times, to a maximum size of 2,000,000 bytes (bytes x repetition size).
Such sequences are also part of the FIPS 140-2 standard:
A file containing the binary-coded form of the ASCII string which consists of 1,000,000 repetitions of the character "a" results in a SHA-1 message digest of 34aa973c d4c4daa4 f61eeb2b dbad2731 6534016f.
# Simple example - a single NUL byte: $ echo '$TESTVEC[00 x 1]' \ | mdxfind -h ^sha1$ -z -f /dev/null stdin 2>/dev/null SHA1x01 5ba93c9db0cff93f52b521d7420e43f6eda2784f:$TESTVEC[00 x 1] # ... which is equivalent to: $ echo -n '\x00' | xxd -p -r | sha1sum 5ba93c9db0cff93f52b521d7420e43f6eda2784f - # The classic FIPS example - byte \x61, repeated 1 million times. $ echo '$TESTVEC[61 x 1000000]' \ | mdxfind -h ^sha1$ -z -f /dev/null stdin 2>/dev/null SHA1x01 34aa973cd4c4daa4f61eeb2bdbad27316534016f:$TESTVEC[61 x 1000000]
$TESTVEC was introduced in MDXfind 1.16 in May 2014.
Cracking output goes straight to stdout (unless redirected). Each hash:solution pair is prefixed with the discovered hash type and iteration (x99 syntax):
MD5x01 5f4dcc3b5aa765d61d8327deb882cf99:password MD5x02 696d29e0940a4957748fe3fc9efd22a3:password MD5x03 5a22e6c339c96c9c0513a46e44c39683:password SHA1x01 5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8:password NTLMx01 8846f7eaee8fb117ad06bdd830b7586c:password
MDXfind will periodically show a status / progress "comfort message" line (every 15 seconds). This status output goes to stderr, so if you are directing output somewhere else, the status line will be the only thing you see while the attack is in progress:
Working on mybigwordlist w=62, line 2771, Found=2809, 0.54Mh/s, 184.73c/s Working on mybigwordlist w=62, line 5542, Found=8532, 0.74Mh/s, 184.73c/s
H/s is hash operations per second (often more than one in each attempt of a hashtype)
c/s is candidates per second (e.g., bcrypt attempts per second)
Note that initial values may take time to populate (the first job has to complete).
Status output and tuning commentary, from Waffle:
The purpose of mdsplit is to remove hashes with solutions from a target hashlist, using MDXfind result files (traditionally named with a .res extension) as a reference, separating them into per-hashtype files with the hash type as the files extension.
Typical usage examples:
# Remove all hashes found in foo.res from unsolved.hashes.txt. mdsplit -f foo.res unsolved-hashes.txt # Do the same for multiple files, from stdin. cat foo.res bar.res | mdsplit unsolved-hashes.txt # Run a cracking job, automatically sending mdxfind output to mdsplit. cat unfound-hashset*.txt | mdxfind -i 3 /tmp/words | mdsplit *.txt
If you are working with a results file that does not contain hash-type prefixes (such as a potfile from hashcat or john), you can use the -t flag to specify a default hash type. (Of course, if your potfile contains more than one "silent" hash type, this won't work.)
When working with large hashlists (millions+), other tools like John the Ripper and hashcat can take non-trivial time to process the hashlist at each startup. (When doing many small attacks, this can sometimes mean that the startup time is higher than the attack time!). So a very common workflow is to use mdxfind to find a lot of hashes early, and then mdsplit to remove the found hashes from your target hashlist. This can reduce your target to a manageable size for other tools. For hashcat users, this is similar to hashcat's --remove, but after the fact.
Here is the full known changelog (text file) for both mdxfind and mdsplit.
The upstream distribution from Waffle just contains binaries. I generate some useful metadata and maintain a changelog here, but my metadata is not in the distribution zipfile.
There is also a little bit of metadata provided to me directly by Waffle (but isn't part of the zipfile):
Posts with more info and examples:
(Moved/broken references, necromancy TBD - see Wayback Machine in the meantime:)
Google search for MDXfind on Hashkiller forums |
usage from hashes.org forums |
thread 1 |
thread 2 |
thread 3
NOTE: MDXfind is distributed as standalone binaries for the command line. There is no installer, and there is no GUI.
In the directory below, you will see two zipfiles:
Note:
MDXfind is not currently open source (no public GitHub, etc.), but it is free to use.
Permission is granted to use, copy, share, and redistribute this software for non-commercial purposes only.
Modifications and derivative works are allowed, provided they are not used or distributed for commercial benefit.
Redistribution of unmodified copies is permitted only for non-commercial purposes.
Any commercial use, sale, or distribution for profit is strictly prohibited.
Name Last modified Size Description
Parent Directory -
archive/ 2025-08-27 02:49 - retired/older files
example-wordlist.dict 2025-08-18 17:34 9 Example wordlist*
example-hashes.txt 2025-08-18 18:49 206 Example hashes*
example-found.res 2025-08-18 22:00 255 Example cracking results*
CHECKSUMS.MD5.txt 2025-11-10 16:38 1.2K MD5 checksums*
CHECKSUMS.SHA256.txt 2025-11-10 16:38 2.1K SHA256 checksums*
mdsplit-old.txt 2019-09-01 22:33 2.5K old Perl version (ref)
usage.txt 2025-11-10 16:38 2.8K mdxfind/mdsplit usage*
file-output.txt 2025-11-10 16:38 4.1K file(1) binary details*
maphashcat.txt 2025-08-26 00:27 8.6K hashcat mode -> algo map**
algorithms.txt 2025-11-10 16:38 10K supported hash types*
CHANGES.txt 2025-11-11 01:05 15K Changelog*
algo_defs.txt 2025-08-26 00:27 22K algo name -> ID map**
mdsplit.arm6 2025-09-12 14:03 74K e.g. Pi v1 x32
mdsplit.arm7 2025-09-12 14:03 74K e.g. Pi early 2 B x32
top-10k-pass.dict 2025-08-18 22:46 80K HashMob's top10k list*
mdsplit-32.exe 2025-09-12 14:16 92K (32-bit)
mdsplit-32 2025-09-12 14:15 94K (32-bit)
mdsplit.arm8 2025-09-12 14:14 131K e.g. Pi 3+ x64
mdsplit.freebsd 2025-09-12 14:09 158K
mdsplit 2025-09-12 14:09 159K
mdsplit.static 2025-09-12 14:09 159K DEPRECATED - use mdsplit
mdsplit.exe 2025-09-12 14:16 166K
mdsplit.power8 2025-09-12 14:07 259K
mdsplit.mac 2025-10-24 14:52 342K
mdsplit.macm1 2025-10-24 14:52 342K DEPRECATED - use mdsplit.mac (universal)
mdxfind.arm8 2025-09-12 14:09 2.1M e.g. Pi 3+ x64
mdxfind.arm6 2025-11-10 16:38 2.4M e.g. Pi v1 x32
mdxfind.arm7 2025-11-10 16:32 2.5M e.g. Pi early 2 B x32
mdxfind.power8 2025-11-10 17:40 3.0M
mdxfind-32 2025-11-10 17:08 3.3M (32-bit)
mdxfind.exe 2025-11-10 19:01 3.8M
mdxfind 2025-11-10 17:45 3.9M
mdxfind.static 2025-11-10 17:45 3.9M DEPRECATED - use mdxfind
mdxfind-32.exe 2025-11-10 20:12 4.0M (32-bit)
mdxfind.freebsd 2025-11-10 16:37 4.1M
mdxfind.mac 2025-11-10 16:27 4.9M
mdxfind.macm1 2025-11-10 16:27 4.9M DEPRECATED - use mdxfind.mac (universal)
mdxfind-1.136.zip 2025-11-11 00:53 16M
mdxfind-latest.zip 2025-11-11 00:53 16M stable symlink to latest
* File not part of the distro -- metadata added by me (errors mine, not Cynosure Prime's)
** File not part of the distro -- metadata added by Waffle
All credit to Waffle and hops of CynoSure Prime for this powerful hash-cracking tool. A serious amount of careful thought and insight has gone into making it reliable and efficient.