Rules are a small language for modifying password candidates. They can change, remove, or add characters and can use conditions to reject candidates. -r works in every attack mode.
Why use a specialized rule language instead of regular expressions? Rules are designed to transform candidate passwords efficiently inside hashcat's candidate-generation pipeline. Regular expressions are useful for describing text patterns, but they are not a drop-in replacement for hashcat's candidate transformations.
hashcat's rule syntax shares many operations with John the Ripper and PasswordsPro, but compatibility is not guaranteed. The same rule letter can have different behavior or argument handling between tools, so check the documentation before reusing a rule file.
The following operations have corresponding functions in John the Ripper and PasswordsPro. Check each tool's documentation for differences in behavior and arguments before reusing rules.
| Name | Function | Description | Example Rule | Input Word | Output Word | Note |
|---|---|---|---|---|---|---|
| Passthrough | : | No operation, the word is unchanged | : | p@ssW0rd | p@ssW0rd | |
| Lowercase | l | Lowercase all letters | l | p@ssW0rd | p@ssw0rd | |
| Uppercase | u | Uppercase all letters | u | p@ssW0rd | P@SSW0RD | |
| Capitalize | c | Capitalize the first character, lowercase the rest | c | p@ssW0rd | P@ssw0rd | |
| Invert Capitalize | C | Lowercase the first character, uppercase the rest | C | p@ssW0rd | p@SSW0RD | |
| Toggle Case | t | Toggle the case of all characters in word | t | p@ssW0rd | P@SSw0RD | |
| Toggle @ | TN | Toggle the case of characters at position N | T3 | p@ssW0rd | p@sSW0rd | * |
| Reverse | r | Reverse the entire word | r | p@ssW0rd | dr0Wss@p | |
| Duplicate | d | Duplicate entire word | d | p@ssW0rd | p@ssW0rdp@ssW0rd | |
| Duplicate N | pN | Append duplicated word N times | p2 | p@ssW0rd | p@ssW0rdp@ssW0rdp@ssW0rd | * |
| Reflect | f | Duplicate word reversed | f | p@ssW0rd | p@ssW0rddr0Wss@p | |
| Rotate Left | { | Rotate the word left | { | p@ssW0rd | @ssW0rdp | |
| Rotate Right | } | Rotate the word right | } | p@ssW0rd | dp@ssW0r | |
| Append Character | $X | Append character X to end | $1$2 | p@ssW0rd | p@ssW0rd12 | |
| Prepend Character | ^X | Prepend character X to front | ^2^1 | p@ssW0rd | 12p@ssW0rd | |
| Truncate left | [ | Delete first character | [ | p@ssW0rd | @ssW0rd | |
| Truncate right | ] | Delete last character | ] | p@ssW0rd | p@ssW0r | |
| Delete @ N | DN | Delete character at position N | D3 | p@ssW0rd | p@sW0rd | * |
| Extract range | xNM | Extract M characters, starting at position N | x04 | p@ssW0rd | p@ss | * # |
| Omit range | ONM | Delete M characters, starting at position N | O12 | p@ssW0rd | psW0rd | * |
| Insert @ N | iNX | Insert character X at position N | i4! | p@ssW0rd | p@ss!W0rd | * |
| Overwrite @ N | oNX | Overwrite character at position N with X | o3$ | p@ssW0rd | p@s$W0rd | * |
| Truncate @ N | 'N | Truncate word at position N | '6 | p@ssW0rd | p@ssW0 | * |
| Replace | sXY | Replace all instances of X with Y | ss$ | p@ssW0rd | p@$$W0rd | |
| Purge | @X | Purge all instances of X | @s | p@ssW0rd | p@W0rd | |
| Duplicate first N | zN | Duplicate first character N times | z2 | p@ssW0rd | ppp@ssW0rd | * |
| Duplicate last N | ZN | Duplicate last character N times | Z2 | p@ssW0rd | p@ssW0rddd | * |
| Duplicate all | q | Duplicate every character | q | p@ssW0rd | pp@@ssssWW00rrdd | |
| Extract memory | XNMI | Insert substring of length M starting from position N of word saved to memory at position I | lMX428 | p@ssW0rd | p@ssw0rdw0 | + |
| Append memory | 4 | Append the word saved to memory to current word | uMl4 | p@ssW0rd | p@ssw0rdP@SSW0RD | + |
| Prepend memory | 6 | Prepend the word saved to memory to current word | rMr6 | p@ssW0rd | dr0Wss@pp@ssW0rd | + |
| Memorize | M | Memorize current word | lMuX084 | p@ssW0rd | P@SSp@ssw0rdW0RD | + |
-j or -k).NOTE: Reject rules only work either with hashcat-legacy, or when using “-j” or “-k” with hashcat. They will not work as regular rules (in a rule file) with hashcat.
| Name | Function | Description | Example Rule | Note |
|---|---|---|---|---|
| Reject less | <N | Reject plains if their length is greater than N | <G | * |
| Reject greater | >N | Reject plains if their length is less than N | >8 | * |
| Reject equal | _N | Reject plains of length not equal to N | _7 | * |
| Reject contain | !X | Reject plains which contain char X | !z | |
| Reject not contain | /X | Reject plains which do not contain char X | /e | |
| Reject equal first | (X | Reject plains which do not start with X | (h | |
| Reject equal last | )X | Reject plains which do not end with X | )t | |
| Reject equal at | =NX | Reject plains which do not have char X at position N | =1a | * |
| Reject contains | %NX | Reject plains which contain char X less than N times | %2a | * |
| Reject contains | Q | Reject plains where the memory saved matches current word | rMrQ | e.g. for palindrome |
The following functions are not available in John the Ripper and/or PasswordsPro:
| Name | Function | Description | Example Rule | Input Word | Output Word | Note |
|---|---|---|---|---|---|---|
| Swap front | k | Swap first two characters | k | p@ssW0rd | @pssW0rd | |
| Swap back | K | Swap last two characters | K | p@ssW0rd | p@ssW0dr | |
| Swap @ N | *NM | Swap character at position N with character at position M | *34 | p@ssW0rd | p@sWs0rd | * |
| Bitwise shift left | LN | Bitwise shift left character @ N | L2 | p@ssW0rd | p@æsW0rd | * |
| Bitwise shift right | RN | Bitwise shift right character @ N | R2 | p@ssW0rd | p@9sW0rd | * |
| ASCII increment | +N | Increment character @ N by 1 ascii value | +2 | p@ssW0rd | p@tsW0rd | * |
| ASCII decrement | -N | Decrement character @ N by 1 ascii value | -1 | p@ssW0rd | p?ssW0rd | * |
| Replace N + 1 | .N | Replace character @ N with value at @ N plus 1 | .1 | p@ssW0rd | psssW0rd | * |
| Replace N - 1 | ,N | Replace character @ N with value at @ N minus 1 | ,1 | p@ssW0rd | ppssW0rd | * |
| Duplicate block front | yN | Duplicate first N characters | y2 | p@ssW0rd | p@p@ssW0rd | * |
| Duplicate block back | YN | Duplicate last N characters | Y2 | p@ssW0rd | p@ssW0rdrd | * |
| Title | E | Lower case the whole line, then upper case the first letter and every letter after a space | E | p@ssW0rd w0rld | P@ssw0rd W0rld | + |
| Title w/separator | eX | Upper case the first letter and the letter after each run of the separator X, lower case every other letter. X keeps its own case | e- | p@ssW0rd-w0rld | P@ssw0rd-W0rld | + |
| Toggle w/Nth separator | 3NX | Toggle case the letter after the Nth instance of a separator char | 30- | pass-word | pass-Word | * |
You can use '\xNN' syntax to use hex bytes in rules.
$ echo "Penguin" | hashcat -j '$\x64' --stdout Penguind
This is useful for non-printable ASCII characters, multibyte work, etc.
You may need to use double quotes for the rule with some shells (Windows Command Prompt, etc.)
To write useful rules, first identify the patterns you want to test. This usually means analyzing plaintext passwords to find common patterns. For example, people often append a digit to strengthen a password. This gives us two parameters:
If we take a look at the function overview we see that we can append something using the '$' function. So, for example, if we want to add a “1” to our password, we write a rule that looks like this.
$1
To try all numbers 1 - 9, use a Hybrid attack. See that page for details.
Also note:
There are two ways to automatically “generate” rules.
hashcat can generate random rules for the current session. This can be useful after you have tried all your existing rules on all your dictionaries. There are three configuration parameters:
Generates NUM rules to be applied to each attempt:
--generate-rules=NUM
Specifies the number of functions that should be used (minimum to maximum range):
--generate-rules-func-min=NUM --generate-rules-func-max=NUM
You can also specify the pool of functions that the rule engine will select from:
--generate-rules-func-sel=[function list]
… such as:
--generate-rules-func-sel=ioTlc
This number can be unlimited but large numbers are not recommended. When used in conjunction with -g, any rule outside of this setting will be ignored.
For example, it could generate “l r”, “l ^f”, and “sa@”. These are all valid rules. However, “l ^f sa@ r $3” would be ignored because it contains 5 functions. Default: min=1 max=4
This is useful with the rule generator and for statistical analysis of your rulesets.
To save any rule that generated a matched password use these switches:
--debug-mode=1 --debug-file=matched.rule
This will save the matched rule on every match, so the resulting rule file might contain many duplicate rules.
At high cracking speeds, this may slow the attack slightly. At lower speeds, the impact is likely negligible.
To verify that a rule does what you want, use the --stdout switch and omit the hashlist.
Here is an example:
Create a small dictionary:
$ echo WORd > word
Generate a simple rule. The “c” rule capitalizes the first letter and lower-cases the rest.
$ echo c > rule
And that's how we see the generated debug output:
$ ./hashcat -r rule --stdout word Word
This feature also provides a fast password candidate generator. You can feed its output to an external program that reads from stdin.
Character Class Commands were added in 7.0.0, expanding the rule logic and bringing hashcat's behavior more in line with John the Ripper.
Only a limited set of character classes is supported (?l, ?u, ?d, ?h, ?H, ?s).
Syntax is similar to John the Ripper's, using the ~ symbol at the beginning of each command.
Supported on both CPU and GPU:
| Rule | Description |
|---|---|
| ~s?CY | Replace all characters of class C in the word with Y |
| ~@?C | Remove all characters of class C from the word |
| ~e?C | Upper case the first letter and the letter after each run of characters from class C, lower case every other letter. Characters in C keep their own case |
Supported only on CPU:
| Rule | Description |
|---|---|
| ~!?C | Reject the word if it contains any character from class C |
| ~/?C | Reject the word unless it contains at least one character from class C |
| ~(?C | Reject the word unless its first character is in class C |
| ~)?C | Reject the word unless its last character is in class C |
| ~=N?C | Reject the word unless the character at position N is in class C |
| ~%N?C | Reject the word unless the word contains at least N characters from class C |
Both eX and ~e?C leave the separator itself alone and upper case the letter after a whole run of separators rather than after each one. A run counts once, so e- on foo–bar gives Foo–Bar rather than upper casing twice, and a separator that is itself a letter keeps its own case:
$ printf 'foo--bar\nfoobar\n' | hashcat --stdout -j e- Foo--Bar Foobar $ printf 'foo--bar\nfoobar\n' | hashcat --stdout -j eo Foo--bar FooBar
Under eo the separator is o, so in foobar both of them stay lower case and the b after the run of them is upper cased.
Applies to master. The three rule engines disagreed about this until after 8.0.0. The host engine behind -j and -k preserved separators, while the GPU engines lower cased everything including the separator and upper cased the letter after each separator, so eo on foobar gave FooBar on the host and FoOBar on the GPU. For the letter classes ~e?l, ~e?u, ~e?h and ~e?H the host followed the GPU instead. The optimized engine also skipped a class rule when the word held no character of that class, so ~e?s left foobar unchanged rather than producing Foobar. All three now behave as described above. Results differ from 8.0.0 only where a separator is a letter, since digits, symbols and spaces have no case, and in that optimized-engine case.
NOTE: rules that use the 'p' character position code work only with the '-j' or '-k' command-line options.
The 'p' character position code (see Github here) memorizes the position of the Nth instance of a character, and makes that position available for use in rules that work with positions within the string. 'p' becomes available automatically when using '%' (reject plains unless they have at least X occurrences of a specific character) and '/' (reject plains that do not contain a specific character).
To operate only on the first instance of a character, use %1s or '/':
$ echo -n 'Odessa77' | hashcat --stdout -j '%1s Dp ip$' Ode$sa77 $ echo -n 'Odessa77' | hashcat --stdout -j '/s Dp ip$' Ode$sa77
If you only want to operate on the second instance:
$ echo -n 'Odessa77' | hashcat --stdout -j '%2s Dp ip$' Odes$a77
… etc. Unlike the 's' rule, which replaces all instances of a character, this allows selection of which instance of a character to replace.
You can also use this functionality to replace one character with multiple characters:
$ echo -n 'p@ssW0rd' | hashcat --stdout -j '%1W DpM ip/ ip\ ip/ ip\' p@ss\/\/0rd
In the examples below, the '%2s' portion of the rule means “reject plains unless they have at least two 's' characters”. The 'p' then refers to the position of the second 's' character.
| Name | Function | Description | Example Rule | Input Word | Output Word | Note |
|---|---|---|---|---|---|---|
| Toggle @ | Tp | Toggle the case of characters at position p | %2s Tp | p@s.sW0rd | p@s.SW0rd | |
| Delete @ p | Dp | Deletes character at position p | %2s Dp | p@s.sW0rd | p@s.W0rd | |
| Extract range | xpM | Extracts M characters, starting at position p | %2s xp4 | p@s.sW0rd | sW0r | |
| Omit range | OpM | Deletes M characters, starting at position p | %2s Op2 | p@s.sW0rd | p@s.0rd | |
| Insert @ p | ipX | Inserts character X at position p | %2s ip! | p@s.sW0rd | p@s.!sW0rd | |
| Overwrite @ p | opX | Overwrites character at position p with X | %2s op$ | p@s.sW0rd | p@s.$W0rd | |
| Truncate @ p | 'p | Truncate word at position p | %2s 'p | p@s.sW0rd | p@s. | |
| Extract memory | XpMI | Insert substring of length M starting from position p of word saved to memory at position I | %2s lMXp28 | p@s.sW0rd | p@s.sw0rswd | |
| Swap @ p | *pM | Swaps character at position p with character at position M | %2s *p4 | p@s.sW0rd | p@s.sW0rd | |
| Bitwise shift left | Lp | Bitwise shift left character @ p | %2s Lp | p@s.sW0rd | p@s.▒W0rd | |
| Bitwise shift right | Rp | Bitwise shift right character @ p | %2s Rp | p@s.sW0rd | p@s.9W0rd | |
| ASCII increment | +p | Increment character @ p by 1 ASCII value | %2s +p | p@s.sW0rd | p@s.tW0rd | |
| ASCII decrement | -p | Decrement character @ p by 1 ASCII value | %2s -p | p@s.sW0rd | p@s.rW0rd | |
| Replace p + 1 | .p | Replaces character @ p with value at @ p plus 1 | %2s .p | p@s.sW0rd | p@s.WW0rd | |
| Replace p - 1 | ,p | Replaces character @ p with value at @ p minus 1 | %2s ,p | p@s.sW0rd | p@s..W0rd |
See rules/ folder in hashcat-legacy or hashcat for examples.
The release of oclHashcat-plus v0.07 introduced a new feature for rule-based cracking.
Instead of just giving one -r parameter and a file, you can now add as many -r's as you want.
They are not executed in a sequence!
Every rule from each rule file is combined with every rule from the other rule files. This lets you build custom combinations of rules.
$ cat 123.rule $1 $2 $3 $ cat abc.rule $a $b $c $ hashcat --stdout -r 123.rule -r abc.rule wordlist hashcat1a hashcat2a hashcat3a hashcat1b hashcat2b hashcat3b hashcat1c hashcat2c hashcat3c
Because the total number of generated rules is the product of all lists, stacking multiple large lists can quickly exceed available memory. But a few well-chosen rules can be stacked to great effect.
This attack is currently supported by:
With hashcat, both the number of functions in a single rule line and the total number of functions in combined rules are limited to 31.