Phashcat is a small Python helper for the hashcat command line. You chain readable methods, and it hands you a checked argument list you can pass to subprocess. It does not ship a hashcat binary and it does not recover passwords by itself.
- Repository: https://github.com/avitwil/Phashcat
- Author: Avi Twil
- License: MIT for this package. Hashcat is a separate project with its own license.
Use hashcat only on hashes and systems you are allowed to test. Unauthorized password recovery is illegal. Follow local law, your organization's policy, and responsible disclosure.
The walkthrough below uses one public test vector on files you create yourself: the MD5 of the word password, which is 5f4dcc3b5aa765d61d8327deb882cf99. It does not use a real credential, a real host, or a downloaded wordlist.
- Fluent commands such as
.hash_type(0).attack_mode(0).outfile("cracked.txt"). - Immutable builders. Each call returns a new builder, so presets stay unchanged.
- Composable presets.
.mappend(other)merges two builders. Later values win. - Light checks. Values are checked against
flags.VALUE_KINDbefore you run anything. - Safe to execute.
.value()is a list forsubprocess, not a shell string. - One import.
from Phashcat import hashcat, flags.
hashcat(...) finds the hashcat binary and returns a HashcatBuilder.
- An explicit
binary=argument is used if you pass one. - Otherwise
HASHCAT_BINARYis used when it points at a real program. - Otherwise a
hashcatbinary placed next to the package is used, if you put one there. None is shipped. - Otherwise the first
hashcatonPATHis used.
Each method (.hash_type, .attack_mode, .outfile, and the rest) copies the builder and sets one flag. .value() sorts those flags and appends your hash file and wordlist or mask. .run() sends that list to subprocess.run. .cmdline() is the same list, quoted for display.
Hash mode 0 is MD5. Attack mode 0 is a straight wordlist. Those two numbers are hashcat's own -m and -a values.
Clone the repository and install it locally:
git clone https://github.com/avitwil/Phashcat.git
cd Phashcat
pip install .Optional tests:
pip install -e ".[test]"
pytestInstall hashcat itself from hashcat.net or your OS packages, then put hashcat on PATH, set HASHCAT_BINARY, or pass binary=.
The old GitHub release v0.1.1 is a wheel that vendored hashcat. Do not install it.
Create two local files. The hash file holds the well-known MD5 test vector. The wordlist holds the matching test word.
printf '%s\n' '5f4dcc3b5aa765d61d8327deb882cf99' > md5.hash
printf '%s\n' 'password' > wordlist.txtfrom Phashcat import hashcat, flags
print("Attack modes:")
for mode, name in sorted(flags.ATTACK_MODES.items()):
print(f" {mode}: {name}")
cmd = (
hashcat("md5.hash", "wordlist.txt")
.hash_type(0) # -m 0 MD5
.attack_mode(0) # -a 0 straight wordlist
.outfile("cracked.txt")
.potfile_path("lab.pot")
.value()
)
print()
print("argv passed to subprocess:")
print(cmd)Expected shape (the first item is whatever hashcat resolved to on that machine):
Attack modes:
0: Straight
1: Combination
3: Brute-force
6: Hybrid Wordlist + Mask
7: Hybrid Mask + Wordlist
9: Association
argv passed to subprocess:
['/usr/bin/hashcat', '--attack-mode', '0', '--hash-type', '0', '--outfile', 'cracked.txt', '--potfile-path', 'lab.pot', 'md5.hash', 'wordlist.txt']
from Phashcat import hashcat
cmd = (
hashcat("md5.hash", "wordlist.txt")
.hash_type(0)
.attack_mode(0)
.outfile("cracked.txt")
.potfile_path("lab.pot")
)
print(cmd.cmdline())
cmd.run(check=False)Hashcat prints a device banner, then a status block. On this local file the run ends as cracked, and the only candidate is the test word:
Status...........: Cracked
Hash.Mode........: 0 (MD5)
Hash.Target......: 5f4dcc3b5aa765d61d8327deb882cf99
Recovered........: 1/1 (100.00%) Digests
Candidates.#1....: password -> password
Startup time and speed depend on the machine. A one-word list is only there so the demo finishes immediately.
hashcat -m 0 md5.hash --show --potfile-path lab.pot
cat cracked.txtBoth print the same line:
5f4dcc3b5aa765d61d8327deb882cf99:password
You can build that --show command the same way:
from Phashcat import hashcat
print(hashcat("md5.hash").hash_type(0).potfile_path("lab.pot").show(True).cmdline())These only build argument lists. They are not extra cracks. Swap in the same local md5.hash when you want to run one.
Wordlist plus a rules file:
from Phashcat import hashcat
cmd = (
hashcat("md5.hash", "wordlist.txt")
.hash_type(0).attack_mode(0)
.rules_file("rules/best64.rule")
.outfile("out_rules.txt")
.value()
)Mask attack (-a 3) with a custom charset. ?1 is defined by .cs1("?l?d"):
cmd = (
hashcat("md5.hash", "?1?1?1?1")
.hash_type(0).attack_mode(3)
.cs1("?l?d")
.increment(True).increment_min(4).increment_max(4)
.outfile("out_mask.txt")
.value()
)Hybrid wordlist plus a two-digit mask (-a 6):
cmd = (
hashcat("md5.hash", "wordlist.txt", "?d?d")
.hash_type(0).attack_mode(6)
.outfile("out_hybrid.txt")
.value()
)Reusable presets, merged with .mappend(...). The right-hand builder wins when both set the same flag:
preset_md5 = hashcat().hash_type(0)
preset_wordlist = hashcat().attack_mode(0).workload_profile(2)
cmd = (
preset_md5
.mappend(preset_wordlist)
.outfile("cracked.txt")
.potfile_path("lab.pot")
.arg("md5.hash", "wordlist.txt")
.value()
)Flag tables live on flags (ATTACK_MODES, WORKLOAD_PROFILES, OUTFILE_FORMATS, VALUE_KIND). Longer notes are in MANUAL.md.
Phashcat is MIT licensed. See LICENSE.
Hashcat is not included in this repository. Install and license it separately: hashcat.net.


