### Auditing Kerio Connect hashes with JtR This document describes the process of auditing Kerio Connect hashes with JtR. #### Extract the hashes from the database On Linux, Kerio Connect stores hashes in the following files, * /opt/kerio/mailserver/users.cfg * /opt/kerio/mailserver/mailserver.cfg Passwords can be stored in scrambled form or they can be stored in hashed form. These encrypted / hashed passwords strings can be extracted using the following commands, ``` $ cat users.cfg | sed -n 's/\([^<]*\)<\/variable>/\1/p' | tr -d " " D3S:d1ca0cee090ba26a64869078fcc95e46a74b66447f662068 D3S:77b27f3742be6de9db01f240928f714f19470b4f1ff8f4cf ``` These are the scrambled password strings. ``` $ cat users.cfg | sed -n 's/\([^<]*\)<\/variable>/\1/p' | tr -d " " SHA:161c91ace6162991d4d73f24921c560622d6f0230c111ef27f4188cf SHA:e2e9aa4757186ed5e8fdce538ce77b759298e2224f07c43f1d499533 ``` These are the hashed passwords. Hashing is done using PKBDF2-HMAC-SHA1 with 10000 iterations. #### A note about recovering scrambled password strings The following script can be used to reverse these "D3S" scrambled password strings. ```python #!/usr/bin/env python import sys from Crypto.Cipher import DES3 # pip install --user pycryptodome # Password unscrambler for Kerio Connect. # # Tested with kerio-connect-9.2.7-3949-linux-amd64.deb on Ubuntu 18.04 LTS 64-bit. # # root@xubuntu:/opt/kerio/mailserver# ls *.cfg # cluster.cfg mailserver.cfg users.cfg # # The "users.cfg" file has the "D3S" scrambled password strings. # # Sample scrambled password strings with password 123456, # # D3S:3795d2bfad3b1a2abb53f8d6efdafc9ef0cdb947f0ff2757 # D3S:3ad3d43e853c12453c39d1f2cdfaf835f0cdb947f0ff2757 # D3S:3aec30b049f5ceddc7bdbbd8895dcaecf0cdb947f0ff2757 # D3S:32fc936ccfab2e3fc7bdbbd8895dcaecf0cdb947f0ff2757 # D3S:c36e4be2280fa230aa5d4a9de5aef11af0cdb947f0ff2757 # # Password -> 1234567 # # D3S:10b2f2db66669ef03c39d1f2cdfaf835824e3e787e1f3307 # D3S:7214614a6b3fc1932fd98c8404118268824e3e787e1f3307 # # Password -> AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA (32x A) # # D3S:8742ddf7db1906c5d54ec948c500e587adfa4447eff8dc42adfa4447eff8dc42adfa4447eff8dc4298558bad76110b35 # D3S:2962f547d408925a8948beccd54c660aadfa4447eff8dc42adfa4447eff8dc42adfa4447eff8dc4298558bad76110b35 # # Password -> openwall@123 # # D3S:77b27f3742be6de9db01f240928f714f19470b4f1ff8f4cf # # Password -> openwall # # D3S:d1ca0cee090ba26a64869078fcc95e46a74b66447f662068 """ Attach gdb to "mailserver" process. (gdb) rbreak DES* Thread 52 "mailserver" hit Breakpoint 66, 0x00007ffab7bdb2f0 in DES_set_key_unchecked () from /opt/kerio/mailserver/libktcrypto.so.1.0.0 (gdb) bt #0 0x00007ffab7bdb2f0 in DES_set_key_unchecked () from /opt/kerio/mailserver/libktcrypto.so.1.0.0 #1 0x00007ffab7c54160 in des_ede3_init_key () from /opt/kerio/mailserver/libktcrypto.so.1.0.0 #2 0x00007ffab7c52c10 in EVP_CipherInit_ex () from /opt/kerio/mailserver/libktcrypto.so.1.0.0 #3 0x000000000153a735 in kerio::crypto::StreamCrypto::init() () #4 0x000000000153b5db in kerio::crypto::StreamCrypto::StreamCrypto(unsigned char*, int, unsigned char*, int, kerio::crypto::StreamCrypto::Cipher, kerio::crypto::StreamCrypto::CipherMode, kerio::crypto::StreamCrypto::EncDec, bool) () #5 0x000000000269a075 in kerio::tinydb::decryptPassword(std::string&, char const*, std::string const&, std::string const&) () #6 0x000000000269a543 in kerio::tinydb::unscramblePassword(std::string&, bool&, char const*, std::string const&, bool) () #7 0x000000000144abbd in kerio::mailserver::dataSwitch::TinydbUserVariablePassword::fillFromResult(kerio::mailserver::dataSwitch::User&, kerio::mailserver::dataSwitch::ColumnsHolder const&, tinydb_result_row const*, kerio::mailserver::dataSwitch::ResultCache&) const () Thread 44 "mailserver" hit Breakpoint 74, 0x00007ffab7bdbd60 in DES_ecb3_encrypt () from /opt/kerio/mailserver/libktcrypto.so.1.0.0 ... This call is important. It has the raw encrypted password data from the file. Thread 46 "mailserver" hit Breakpoint 21, 0x000000000153a1e0 in kerio::crypto::StreamCrypto::getDES3Cipher() () (gdb) (gdb) bt #0 0x00007ffab7c54140 in des_ede3_init_key () from /opt/kerio/mailserver/libktcrypto.so.1.0.0 #1 0x00007ffab7c52c10 in EVP_CipherInit_ex () from /opt/kerio/mailserver/libktcrypto.so.1.0.0 #2 0x000000000153a735 in kerio::crypto::StreamCrypto::init() () #3 0x000000000153b5db in kerio::crypto::StreamCrypto::StreamCrypto(unsigned char*, int, unsigned char*, int, kerio::crypto::StreamCrypto::Cipher, kerio::crypto::StreamCrypto::CipherMode, kerio::crypto::StreamCrypto::EncDec, bool) () #4 0x000000000269a075 in kerio::tinydb::decryptPassword(std::string&, char const*, std::string const&, std::string const&) () #5 0x000000000269a543 in kerio::tinydb::unscramblePassword(std::string&, bool&, char const*, std::string const&, bool) () ... (gdb) x/24bx $rsi 0x7ffa736f5db0: 0x61 0xd0 0xe5 0x49 0x54 0x73 0x3b 0x80 0x7ffa736f5db8: 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x7ffa736f5dc0: 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 Function prototype is static int des_ede3_init_key(EVP_CIPHER_CTX *ctx, const unsigned char *key, const unsigned char *iv, int enc) man syscall $ cat samples D3S:3795d2bfad3b1a2abb53f8d6efdafc9ef0cdb947f0ff2757 D3S:3ad3d43e853c12453c39d1f2cdfaf835f0cdb947f0ff2757 D3S:3aec30b049f5ceddc7bdbbd8895dcaecf0cdb947f0ff2757 D3S:32fc936ccfab2e3fc7bdbbd8895dcaecf0cdb947f0ff2757 D3S:c36e4be2280fa230aa5d4a9de5aef11af0cdb947f0ff2757 D3S:10b2f2db66669ef03c39d1f2cdfaf835824e3e787e1f3307 D3S:7214614a6b3fc1932fd98c8404118268824e3e787e1f3307 D3S:8742ddf7db1906c5d54ec948c500e587adfa4447eff8dc42adfa4447eff8dc42adfa4447eff8dc4298558bad76110b35 D3S:2962f547d408925a8948beccd54c660aadfa4447eff8dc42adfa4447eff8dc42adfa4447eff8dc4298558bad76110b35 D3S:77b27f3742be6de9db01f240928f714f19470b4f1ff8f4cf D3S:d1ca0cee090ba26a64869078fcc95e46a74b66447f662068 $ cat samples | python hack.py 123456 123456 123456 123456 123456 1234567 1234567 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA openwall@123 openwall """ key = "\x61\xd0\xe5\x49\x54\x73\x3b\x80" + "\x80\x80\x80\x80\x80\x80\x80\x80" + "\x80\x80\x80\x80\x80\x80\x80\x80" des3 = DES3.new(key, DES3.MODE_ECB) # Expected input line format -> D3S:d1ca0cee090ba26a64869078fcc95e46a74b66447f662068 for line in sys.stdin.readlines(): out = "" line = line.rstrip() tag, data = line.split(":") if tag != "D3S": continue data = data.decode("hex") for i in range(1, len(data) // 8): chunk = data[8*i:8*(i+1)] output = des3.decrypt(chunk) if i == 1: out = out + output[2:] else: out = out + output if out: print(out) ``` Since the last two DES keys are the same, this 3DES EDE operation is equivalent to a single DES encryption operation. We can simplify the decryption script as follows, ```python #!/usr/bin/env python import sys from Crypto.Cipher import DES # pip install --user pycryptodome # Password unscrambler for Kerio Connect. key = "\x61\xd0\xe5\x49\x54\x73\x3b\x80" des = DES.new(key, DES.MODE_ECB) # Expected input line format -> D3S:d1ca0cee090ba26a64869078fcc95e46a74b66447f662068 for line in sys.stdin.readlines(): out = "" line = line.rstrip() tag, data = line.split(":") if tag != "D3S": continue data = data.decode("hex") for i in range(1, len(data) // 8): chunk = data[8*i:8*(i+1)] output = des.decrypt(chunk) if i == 1: out = out + output[2:] else: out = out + output if out: padding_length = ord(out[-1]) if padding_length > 8: padding_length = 0 print(out[:-padding_length]) ``` #### Format the hashes The native PBKDF2-HMAC-SHA1 hashes look like, ``` SHA:161c91ace6162991d4d73f24921c560622d6f0230c111ef27f4188cf SHA:e2e9aa4757186ed5e8fdce538ce77b759298e2224f07c43f1d499533 ``` These can be converted into JtR format using the following script, ```python #!/usr/bin/env python import sys for line in sys.stdin: line = line.rstrip() tag, data = line.split(":") if tag != "SHA": continue salt = data[:16] hsh = data[16:] print "$pbkdf2-hmac-sha1$10000$%s$%s" % (salt, hsh) ``` #### Crack the hashes with JtR ``` $ cat hashes $pbkdf2-hmac-sha1$10000$161c91ace6162991$d4d73f24921c560622d6f0230c111ef27f4188cf $ ../run/john hashes Using default input encoding: UTF-8 Loaded 1 password hash (PBKDF2-HMAC-SHA1 [PBKDF2-SHA1 256/256 AVX2 8x]) Cost 1 (iteration count) is 10000 for all loaded hashes Will run 8 OpenMP threads Press 'q' or Ctrl-C to abort, almost any other key for status openwall (?) 1g 0:00:00:00 DONE (2018-06-15 11:18) 100.0g/s 700.0p/s 700.0c/s 700.0C/s found..åbc Use the "--show" option to display all of the cracked passwords reliably Session complete ```