doc/Auditing-Kerio-Connect.md
This document describes the process of auditing Kerio Connect hashes with JtR.
On Linux, Kerio Connect stores hashes in the following files,
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 name="Password">\([^<]*\)<\/variable>/\1/p' | tr -d " "
D3S:d1ca0cee090ba26a64869078fcc95e46a74b66447f662068
D3S:77b27f3742be6de9db01f240928f714f19470b4f1ff8f4cf
These are the scrambled password strings.
$ cat users.cfg | sed -n 's/<variable name="PasswordHistory">\([^<]*\)<\/variable>/\1/p' | tr -d " "
SHA:161c91ace6162991d4d73f24921c560622d6f0230c111ef27f4188cf
SHA:e2e9aa4757186ed5e8fdce538ce77b759298e2224f07c43f1d499533
These are the hashed passwords. Hashing is done using PKBDF2-HMAC-SHA1 with 10000 iterations.
The following script can be used to reverse these "D3S" scrambled password strings.
#!/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,
#!/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])
The native PBKDF2-HMAC-SHA1 hashes look like,
SHA:161c91ace6162991d4d73f24921c560622d6f0230c111ef27f4188cf
SHA:e2e9aa4757186ed5e8fdce538ce77b759298e2224f07c43f1d499533
These can be converted into JtR format using the following script,
#!/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)
$ 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