In your way through penetration testing, you will find yourself in situations where passwords or critical information are encoded. When this occurs, you first need to find in which format that hash is and then, try to decode it. For these cases, I like to use John the Ripper, one of the most popular password crackers around. So, let’s begin!
Formats
A format is just the kind of encoding that you’re trying to use. Let’s check how many formats john has by typing john --list=formats
. You should see something like this:
j2rgez@myPC:~$ john --list=formats
descrypt, bsdicrypt, md5crypt, md5crypt-long, bcrypt, scrypt, LM, AFS,
tripcode, AndroidBackup, adxcrypt, agilekeychain, aix-ssha1, aix-ssha256,
aix-ssha512, andOTP, ansible, argon2, as400-des, as400-ssha1, asa-md5,
AxCrypt, AzureAD, BestCrypt, bfegg, Bitcoin, BitLocker, bitshares, Bitwarden,
BKS, Blackberry-ES10, WoWSRP, Blockchain, chap, Clipperz, cloudkeychain,
dynamic_n, cq, CRC32, sha1crypt, sha256crypt, sha512crypt, Citrix_NS10,
dahua, dashlane, diskcryptor, Django, django-scrypt, dmd5, dmg, dominosec,
dominosec8, DPAPImk, dragonfly3-32, dragonfly3-64, dragonfly4-32,
dragonfly4-64, Drupal7, eCryptfs, eigrp, electrum, EncFS, enpass, EPI,
EPiServer, ethereum, fde, Fortigate256, Fortigate, FormSpring, FVDE, geli,
gost, gpg, HAVAL-128-4, HAVAL-256-3, hdaa, hMailServer, hsrp, IKE, ipb2,
itunes-backup, iwork, KeePass, keychain, keyring, keystore, known_hosts,
krb4, krb5, krb5asrep, krb5pa-sha1, krb5tgs, krb5-17, krb5-18, krb5-3,
kwallet, lp, lpcli, leet, lotus5, lotus85, LUKS, MD2, mdc2, MediaWiki,
monero, money, MongoDB, scram, Mozilla, mscash, mscash2, MSCHAPv2,
mschapv2-naive, krb5pa-md5, mssql, mssql05, mssql12, multibit, mysqlna,
mysql-sha1, mysql, net-ah, nethalflm, netlm, netlmv2, net-md5, netntlmv2,
netntlm, netntlm-naive, net-sha1, nk, notes, md5ns, nsec3, NT, o10glogon,
o3logon, o5logon, ODF, Office, oldoffice, OpenBSD-SoftRAID, openssl-enc,
oracle, oracle11, Oracle12C, osc, ospf, Padlock, Palshop, Panama,
PBKDF2-HMAC-MD4, PBKDF2-HMAC-MD5, PBKDF2-HMAC-SHA1, PBKDF2-HMAC-SHA256,
PBKDF2-HMAC-SHA512, PDF, PEM, pfx, pgpdisk, pgpsda, pgpwde, phpass, PHPS,
PHPS2, pix-md5, PKZIP, po, postgres, PST, PuTTY, pwsafe, qnx, RACF,
RACF-KDFAES, radius, RAdmin, RAKP, rar, RAR5, Raw-SHA512, Raw-Blake2,
Raw-Keccak, Raw-Keccak-256, Raw-MD4, Raw-MD5, Raw-MD5u, Raw-SHA1,
Raw-SHA1-AxCrypt, Raw-SHA1-Linkedin, Raw-SHA224, Raw-SHA256, Raw-SHA3,
Raw-SHA384, ripemd-128, ripemd-160, rsvp, Siemens-S7, Salted-SHA1, SSHA512,
sapb, sapg, saph, sappse, securezip, 7z, Signal, SIP, skein-256, skein-512,
skey, SL3, Snefru-128, Snefru-256, LastPass, SNMP, solarwinds, SSH, sspr,
Stribog-256, Stribog-512, STRIP, SunMD5, SybaseASE, Sybase-PROP, tacacs-plus,
tcp-md5, telegram, tezos, Tiger, tc_aes_xts, tc_ripemd160, tc_ripemd160boot,
tc_sha512, tc_whirlpool, vdi, OpenVMS, vmx, VNC, vtp, wbb3, whirlpool,
whirlpool0, whirlpool1, wpapsk, wpapsk-pmk, xmpp-scram, xsha, xsha512, ZIP,
ZipMonster, plaintext, has-160, HMAC-MD5, HMAC-SHA1, HMAC-SHA224,
HMAC-SHA256, HMAC-SHA384, HMAC-SHA512, dummy, crypt
Wow, that’s a lot, right? Well, names and/or well-known formats (like MD5 which starts with ), could lead you to know which format you’re going to use. So let’s create a new file called my_hash.txt and insert the following hash (in user:password format): root:6ff3402b$2w6aUd7n//XodMXDt84BE1.
Now, to select the decoded format, we type john my_hash.txt --format=md5crypt
and check the results:
j2rgez@myPC:~$ john my_hash.txt --format=md5crypt
Using default input encoding: UTF-8
Loaded 1 password hash (md5crypt, crypt(3) $1$ (and variants) [MD5 256/256AVX28x3])
Will run 12 OpenMP threads
Proceeding with single, rules:Single
Press 'q' or Ctrl-C to abort, almost any other key for status
Almost done: Processing the remaining buffered candidate passwords, if any.
Warning: Only 248 candidates buffered for the current salt, minimum 288 needed for performance.
Proceeding with wordlist:/usr/share/john/password.lst, rules:Wordlist
admin (root)
1g 0:00:00:00 DONE 2/3 (2020-04-02 20:38) 20.00g/s 90560p/s 90560c/s 90560C/s chacha..OU812
Use the "--show" option to display all of the cracked passwords reliably
Session completed
As you can see, john tried to find a match in its default wordlist and found it! The decoded value is “admin” (let’s keep things fast).
There are tons of wordlists around the internet (or you can create your own ones). You can check for example the ones from SecLists repo, and simply type john my_hash.txt --format=md5crypt --wordlist=my_wordlist.txt
.
Automating all of this
So, last weekend I was completing some exercises from Pentesterlab when I found this sentence in one of the exercises: Use John by trial and error until you find the format. From the exercise context, you could imagine which format it was, but I was thinking: what if they ask me to do this in the future with a not so obvious format?
So I began my way of automating pentesting processes and created a new repo. Yes, I’m 90% sure that there is a better way (or an existing tool) to automate John the Ripper (or password cracking in general), but I find that it’s also a good way to practice some new programming languages or at least the ones that I don’t use on a day to day basis. So here is the first version of my john_auto_decypher.py script:
import subprocess
import argparse
def decrypt(formats, inputFile):
keep_string = "Press 'q' or Ctrl-C to abort, almost any other key for status"
format = ""
for f in formats:
try:
decode_process = subprocess.check_output(["john", inputFile, "--format=" + f], stdout=None)
if keep_string in decode_process:
format = f
except: subprocess.CalledProcessError
if format:
print "Found a hash for " + format + " format"
else:
print "Couldn't decode, checking for already saved hashes..."
print subprocess.check_output(["john", "--show", inputFile])
def getArgs():
parser = argparse.ArgumentParser("john_auto_decypher.py")
parser.add_argument("file", help="File with encoded passwords to be cracked", type=str)
return parser.parse_args()
def main():
inputFile = getArgs().file
formats = map(lambda str: str.strip(), subprocess.check_output(["john", "--list=formats"]).split(","))
decrypt(formats, inputFile)
main()
Simple but effective, now by just typing python john_auto_decypher.py <filename>
on my CLI, this script will check all formats for me and/or already saved hashes. Of course, there is still a lot to improve, like adding parameters for wordlists and custom rules.