Skip to content

Commit

Permalink
ssh: support aes256-ctr for private key passphrase
Browse files Browse the repository at this point in the history
Add support for ssh private key passphrase encrypted using `aes256-ctr`.

Fixes openwall#4069

Test
```bash
$ ssh-keygen -t rsa -b 4096 -f id_rsa-aes256-cbc -Z aes256-cbc -N TestPassword
$ ssh-keygen -t rsa -b 4096 -f id_rsa-aes256-ctr -Z aes256-ctr -N TestPassword
$ echo TestPassword >passwords.lst
$ ./ssh2john.py id_rsa-aes256-cbc id_rsa-aes256-ctr >id_rsa.hash
$ ./john --wordlist=passwords.lst id_rsa.hash
...
TestPassword     (id_rsa-aes256-ctr)
TestPassword     (id_rsa-aes256-cbc)
```
  • Loading branch information
vkhromov committed Aug 31, 2020
1 parent 2be323f commit da479a6
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 8 deletions.
13 changes: 11 additions & 2 deletions run/ssh2john.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
'DES-EDE3-CBC': {'cipher': DES3, 'keysize': 24, 'blocksize': 8, 'mode': "DES3.MODE_CBC"},
'AES-256-CBC': {'cipher': AES_256, 'keysize': 32, 'blocksize': 16, 'mode': "AES.MODE_CBC"},
'AES-192-CBC': {'cipher': AES, 'keysize': 24, 'blocksize': 16, 'mode': "AES.MODE_CBC"},
'AES-256-CTR': {'cipher': AES_256, 'keysize': 32, 'blocksize': 16, 'mode': "AES.MODE_CTR"},
}


Expand Down Expand Up @@ -116,7 +117,12 @@ def read_private_key(filename):
if ktype != 2:
raise Exception('Can\'t parse DEK-info in private key file')
else:
encryption_type = "AES-256-CBC"
if b'aes256-cbc' in data:
encryption_type = "AES-256-CBC"
elif b'aes256-ctr' in data:
encryption_type = "AES-256-CTR"
else:
raise Exception('Unknown encryption type')
saltstr = "fefe" # dummy value, not used
if encryption_type not in CIPHER_TABLE:
raise Exception('Unknown private key cipher "%s"' % encryption_type)
Expand Down Expand Up @@ -175,9 +181,12 @@ def read_private_key(filename):
elif keysize == 16 and ktype == 3: # EC keys using AES-128
hashline = "%s:$sshng$%s$%s$%s$%s$%s" % (f.name, 3, len(saltstr) // 2,
saltstr, len(data) // 2, data)
elif keysize == 32 and ktype == 2: # bcrypt pbkdf + aes-256-cbc
elif keysize == 32 and encryption_type == "AES-256-CBC" and ktype == 2: # bcrypt pbkdf + aes-256-cbc
hashline = "%s:$sshng$%s$%s$%s$%s$%s$%d$%d" % (f.name, 2, len(saltstr) // 2,
saltstr, len(data) // 2, data, rounds, ciphertext_begin_offset)
elif keysize == 32 and encryption_type == "AES-256-CTR" and ktype == 2: # bcrypt pbkdf + aes-256-ctr
hashline = "%s:$sshng$%s$%s$%s$%s$%s$%d$%d" % (f.name, 6, len(saltstr) // 2,
saltstr, len(data) // 2, data, rounds, ciphertext_begin_offset)
else:
sys.stderr.write("%s uses unsupported cipher, please file a bug!\n" % f.name)
return None
Expand Down
6 changes: 3 additions & 3 deletions src/ssh_common_plug.c
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ int ssh_valid(char *ciphertext, struct fmt_main *self)
goto err;
if (hexlen(p, &extra) / 2 != len || extra)
goto err;
if (cipher == 2) {
if (cipher == 2 || cipher == 6) {
if ((p = strtokm(NULL, "$")) == NULL) /* rounds */
goto err;
if (!isdec(p))
Expand All @@ -55,7 +55,7 @@ int ssh_valid(char *ciphertext, struct fmt_main *self)
goto err;
}

if (cipher < 0 || cipher > 5) {
if (cipher < 0 || cipher > 6) {
fprintf(stderr, "[%s] cipher value of %d is not supported!\n",
self->params.label, cipher);
goto err;
Expand Down Expand Up @@ -94,7 +94,7 @@ void *ssh_get_salt(char *ciphertext)
for (i = 0; i < cs.ctl; i++)
cs.ct[i] = atoi16[ARCH_INDEX(p[i * 2])] * 16
+ atoi16[ARCH_INDEX(p[i * 2 + 1])];
if (cs.cipher == 2) {
if (cs.cipher == 2 || cs.cipher == 6) {
p = strtokm(NULL, "$");
cs.rounds = atoi(p);
p = strtokm(NULL, "$");
Expand Down
53 changes: 50 additions & 3 deletions src/ssh_fmt_plug.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,10 @@ john_register_one(&fmt_ssh);

#include <string.h>
#include <stdint.h>
#include <openssl/conf.h>
#include <openssl/des.h>
#include <openssl/err.h>
#include <openssl/evp.h>

#ifdef _OPENMP
#include <omp.h>
Expand Down Expand Up @@ -271,6 +274,42 @@ inline static int check_padding_and_structure(unsigned char *out, int length, in
return -1;
}

inline static void handleErrors(void)
{
ERR_print_errors_fp(stderr);
abort();
}

inline static int AES_ctr_decrypt(unsigned char *ciphertext, int ciphertext_len, unsigned char *key,
unsigned char *iv, unsigned char *plaintext)
{
EVP_CIPHER_CTX *ctx;

int len;

int plaintext_len;

if(!(ctx = EVP_CIPHER_CTX_new()))
handleErrors();

if(1 != EVP_DecryptInit_ex(ctx, EVP_aes_256_ctr(), NULL, key, iv))
handleErrors();

EVP_CIPHER_CTX_set_padding(ctx, 0);

if(1 != EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, ciphertext_len))
handleErrors();
plaintext_len = len;

if(1 != EVP_DecryptFinal_ex(ctx, plaintext + len, &len))
handleErrors();
plaintext_len += len;

EVP_CIPHER_CTX_free(ctx);

return plaintext_len;
}

static void common_crypt_code(char *password, unsigned char *out, int full_decrypt)
{
if (cur_salt->cipher == 0) {
Expand Down Expand Up @@ -310,7 +349,7 @@ static void common_crypt_code(char *password, unsigned char *out, int full_decry
memcpy(iv, cur_salt->ct + cur_salt->ctl - 32, 16);
AES_cbc_encrypt(cur_salt->ct + cur_salt->ctl - 16, out + cur_salt->ctl - 16, 16, &akey, iv, AES_DECRYPT);
}
} else if (cur_salt->cipher == 2) { /* new ssh key format handling */
} else if (cur_salt->cipher == 2) { /* new ssh key format handling with aes256-cbc */
unsigned char key[32 + 16];
AES_KEY akey;
unsigned char iv[16];
Expand All @@ -324,6 +363,14 @@ static void common_crypt_code(char *password, unsigned char *out, int full_decry
// Padding check is unreliable for this type
// memcpy(iv, cur_salt->ct + cur_salt->ctl - 32, 16);
// AES_cbc_encrypt(cur_salt->ct + cur_salt->ctl - 16, out + cur_salt->ctl - 16, 16, &akey, iv, AES_DECRYPT);
} else if (cur_salt->cipher == 6) { /* new ssh key format handling with aes256-ctr */
unsigned char key[32 + 16];
unsigned char iv[16];

// derive (key length + iv length) bytes
bcrypt_pbkdf(password, strlen((const char*)password), cur_salt->salt, 16, key, 32 + 16, cur_salt->rounds);
memcpy(iv, key + 32, 16);
AES_ctr_decrypt(cur_salt->ct + cur_salt->ciphertext_begin_offset, 16, key, iv, out);
} else if (cur_salt->cipher == 3) { // EC keys with AES-128
unsigned char key[16];
AES_KEY akey;
Expand Down Expand Up @@ -389,7 +436,7 @@ static int crypt_all(int *pcount, struct db_salt *salt)
} else if (cur_salt->cipher == 1) {
cracked[index] =
!check_padding_and_structure(out, cur_salt->ctl, 0, 16);
} else if (cur_salt->cipher == 2) { // new ssh key format handling
} else if (cur_salt->cipher == 2 || cur_salt->cipher == 6) { // new ssh key format handling
cracked[index] =
!check_structure_bcrypt(out, cur_salt->ctl);
} else if (cur_salt->cipher == 3) { // EC keys
Expand Down Expand Up @@ -433,7 +480,7 @@ static int cmp_exact(char *source, int index)
return !check_padding_and_structure(out, cur_salt->ctl, 1, 8);
} else if (cur_salt->cipher == 1) {
return !check_padding_and_structure(out, cur_salt->ctl, 1, 16);
} else if (cur_salt->cipher == 2) { /* new ssh key format handling */
} else if (cur_salt->cipher == 2 || cur_salt->cipher == 6) { /* new ssh key format handling */
return 1; // XXX add more checks!
} else if (cur_salt->cipher == 3) { // EC keys
return 1;
Expand Down

0 comments on commit da479a6

Please sign in to comment.