Skip to content

Commit

Permalink
ssh: support aes256-ctr for private keys encryption
Browse files Browse the repository at this point in the history
Add support for ssh new-style private keys encrypted using `aes256-ctr`.

Fixes #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 authored and magnumripper committed Sep 1, 2020
1 parent 2be323f commit 51f7f3d
Show file tree
Hide file tree
Showing 5 changed files with 85 additions and 10 deletions.
2 changes: 2 additions & 0 deletions doc/NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,8 @@ Major changes from 1.9.0-jumbo-1 (May 2019) in this bleeding-edge version:
the Easter egg option "--subsets=full-unicode" which BTW supersedes the
external modes with far better performance. [magnum; 2020]

- Add support for ssh new-style private keys encrypted using `aes256-ctr`
cipher. [vkhromov; 2020]

Major changes from 1.8.0-jumbo-1 (December 2014) to 1.9.0-jumbo-1 (May 2019):

Expand Down
18 changes: 16 additions & 2 deletions run/ssh2john.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@
#
# Copyright (C) 2011, Jeff Forcier <jeff@bitprophet.org>
#
# This software is Copyright (c) 2020 Valeriy Khromov <valery.khromov at gmail.com>,
# and it is hereby released to the general public under the following terms:
# Redistribution and use in source and binary forms, with or without
# modification, are permitted.
#
# This file is part of ssh.
#
# 'ssh' is free software; you can redistribute it and/or modify it under the
Expand Down Expand Up @@ -38,6 +43,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 +122,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 +186,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
61 changes: 58 additions & 3 deletions src/ssh_fmt_plug.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@
* and it is hereby released to the general public under the following terms:
* Redistribution and use in source and binary forms, with or without modification,
* are permitted.
*
* This software is Copyright (c) 2020 Valeriy Khromov <valery.khromov at gmail.com>,
* and it is hereby released to the general public under the following terms:
* Redistribution and use in source and binary forms, with or without
* modification, are permitted.
*/

#if FMT_EXTERNS_H
Expand All @@ -23,7 +28,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 +279,43 @@ 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 (EVP_DecryptInit_ex(ctx, EVP_aes_256_ctr(), NULL, key, iv) != 1)
handleErrors();

EVP_CIPHER_CTX_set_padding(ctx, 0);

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

if (EVP_DecryptFinal_ex(ctx, plaintext + len, &len) != 1)
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 +355,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 +369,16 @@ 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 +444,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 +488,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
8 changes: 6 additions & 2 deletions src/ssh_variable_code.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,14 @@ static struct fmt_tests ssh_tests[] = {
// RSA key encrypted with 3DES, this caught the incorrect padding check bug
{"$sshng$0$8$F1621D1A561534C3$616$ab1925ec002675445db989f2591a5bf7a31a80e10131b6eebb20bc2d2b70e2a21f431bfc70228f3873b4e0bb902156a1cf829d50fa09bc035d5ddf04f2a403f4fd7bfe32b5219d6c74dd594d0babd07e28075be4eef6f015d1ce5be91fcd81a55f886d867995d4719bd8e0890e8fe4c8abc171d272442e1c6805b29e1cb996a2b2cd3e82e70df0270d98d88c8cd32a1164ebe6e1390e64ce15cc166054281619a125bf4776c7433cf653a87d40d3ae6b494d536c2d2974e697d34b8965239d976e9e1d8a3f1503c7bb6ebacd8f852f65b96e58e5a280411ea7737ba1410ec273722b1b3b91c83eba4c3a0c187be3bdb05d3fe9be55cfbde501adc8ff6ff257ecbd4efceb8d8e7a859af411565b3f3fb0fc3d9df056a265836ec18b234f7b6956a4202ae75e5ed2890d33e9abb355763cc56438509a199c4fe3e48e12fa3f6cc2e55f8f3b134ba2dec87b4d37d6209bbf84826d74cac0d96cf4303654c36476edc38f750d4d7d0a495aac5f6ec8ffc6fcceb482985b81636fb66f05502d00c00e5e8b39a17afe46faf18ac590cb4fd59cf88b62209378c47be74b902956b555bdeaba14f447a8b0e4522ea6d0f492045f3b14a49c3d7d9f6cd3f8782cb1fce3bacd57e71e918726a514a39a474661c6989796a9fab1d8f6cc684b4963ced9982a01ee50e076937dfccc4a1d00870b238f30fc4fa258dd6a62d3c7a79bb9f23b0be25261bf222681859058fc56660d59124d114d7528e98b8c2eb8d465514894a6796b07f244bb8334bb4a440245d5a942a05fd401634cbc6f32ee223b4ec49446fd0fc2b30ed05324837ba8a2415c23bc4fc526ee15766c6a29047ba5bb05f38a122160ed91c769ae", "albert"},
#ifdef CPU_FORMAT
// /ssh-keygen -o -N test12345 -t ecdsa -f test
// /ssh-keygen -o -N test12345 -t ecdsa -Z aes256-cbc -f test
{"$sshng$2$16$6931efeeafd9d3fefc5d3f220d6e32f3$375$6f70656e7373682d6b65792d7631000000000a6165733235362d6362630000000662637279707400000018000000106931efeeafd9d3fefc5d3f220d6e32f30000001000000001000000680000001365636473612d736861322d6e69737470323536000000086e6973747032353600000041043da6ae45fd7e65967e3434e5af68d1f92c08b2dbc837ba50f14f58c3fe9f715062f61d3485d0426dec2b021b69f4a8272bdeaf90d9be5b3bd101f2381e9a1758000000c0d876c4b88fc4b76a43b95813d68e37000e6bea260da8cde01144a8ea052e66e5e42bb488b1c39822541147bc21a16cc6be613fa76d6e524073a68e94d944723abb34cec635dc4e3ffa0411695452467c294b95c78f34466c2154bb97f54d5712b7cc08d2902a0f874543eb6660c4c4adccbf1528cfb5348451d93a70d8318a3716819a624299aa5e9c21ec6526377c7bbc3f30173dd9a9b3bc0ef0193a9a21210db076c93c228fd23eaa83796d4f6a4848760db010054f1b9aed7445061a3512$16$183", "test12345"},
// /ssh-keygen -o -N test12345 -t ed25519 -f test
// /ssh-keygen -o -N test12345 -t ed25519 -Z aes256-cbc -f test
{"$sshng$2$16$a439509f8aefc40a17a504ac81c46601$290$6f70656e7373682d6b65792d7631000000000a6165733235362d636263000000066263727970740000001800000010a439509f8aefc40a17a504ac81c466010000001000000001000000330000000b7373682d65643235353139000000200b31c6439dc6b42c9de146c70c752e33877baa7a5875c37ce092e5689dadadee000000a013bbe4b8cd8e0880a7c5dba953fdc5b0e4380b1904c631cb10c9f19ddadd52341160120f459ea1325681bc8f5c40f45a5ef055bc79ea9a05bc94bf668e2808ea6cf88a5ff3f418c4b13664c02456086671776969ce9cb21699818d16b4deae2dd30f03f0f85fc8dd54901a7ad884c35a2b28bd08b418d15ee7d8ec0332649eeff4fab6299eca59f096c2b56f753de0dcc226c0d8404bf44a73a608de2589545c$16$130", "test12345"},
// /ssh-keygen -o -N test12345 -t ecdsa -Z aes256-ctr -f test
{"$sshng$6$16$220c1cfd4c12255c50e95424071ab0d5$375$6f70656e7373682d6b65792d7631000000000a6165733235362d637472000000066263727970740000001800000010220c1cfd4c12255c50e95424071ab0d50000001000000001000000680000001365636473612d736861322d6e69737470323536000000086e69737470323536000000410405042a41f6a8a5b4fdc926bb60664f4d1bbccfce1ab8e4789b7654874f8fcb3961d30fe86197d4c9a36fceadefbb5652672c640b9a45be9e18aa0b1298504343000000c08d5ad7949499c1282d24a86f820a76c90dbde432ed30aec67578724cdbc067e0d89fa399b5e2e945dc405134597543deea2ff24f39c9c2a54edf1c7c3adfb28f62bfe93d06235b8777857839ef79b996c688c1c473dcacb13159045048595cdbd20b2ba19a684bc42a3d3d62de7fab9ec61d97f6fe1e537a973eebf2ad86db5c8e950d5925e2af159b100dca400f6b1515dffa4278e8cb8c653a82157ff89f69e397e25e7bbd04c4c95a5cc204bda8e1014b4b31316350337d2920c83361fa91$16$183", "test12345"},
// /ssh-keygen -o -N test12345 -t ed25519 -Z aes256-ctr -f test
{"$sshng$6$16$ea185829e22299b73de4f6dc8c22767f$290$6f70656e7373682d6b65792d7631000000000a6165733235362d637472000000066263727970740000001800000010ea185829e22299b73de4f6dc8c22767f0000001000000001000000330000000b7373682d65643235353139000000208fea39c1a1b91bdad8da7215f9d79ad5b417f1c76075886634ede3b124e6134c000000a0880e9636df1be5a6420c09a183dfd5c24aaf6ca0ae7e13a6b8d8f8f3d8692d50211710393319639332bc27c589b2dc6bff83d8a8134657000c88612acc7b88ec57eeca59d222e8656b9508fbe2ba0dc6c966f1ebf8123e4260fbee5bb9ee31816c1031974c3de9c25b0324ad50c4cd91e9d44588cdeb43f00cd3dec6a9b0bb3cb643e979405ae5c4cb51dc6e3ca4b9312d77d65b73188dd276ceb7678fff7f9f$16$130", "test12345"},
#endif
// RSA/DSA key with AES-192-CBC
{"$sshng$4$16$04D2D7882E0C474E07E542FE997D2A49$608$bdf0791ad9b7e27dd2788e8910b8d6886c3a3be8feb4647a59b8b748d7806647d203c3d38de8ba3d51ebf9d18ae7331d9d3724774129c48b6ac4476d2cae86b4121af4a45dddc11b6b21e6cb2c9b1f6142e124d724250505ed9e6fb64a9a70a5441e4572602a13189ff90389c079d9c3a0d3ceafa8192c22f5c4a8cb6a84eb61605e48fcb8a0187012ce366b0fd58400e3322e6f86e711b22084bfecc0407f1d54bce85c8c0ac317ac710e9d2b945017ff51f645372a48ea4554357948b67375d7879e46c043dfb5642fb74040f331f83c26ec36d4d3b416ad5bf38265a5bb72aa474b51340f5ee1ae5fd785ce6318121d9f975592bc88077eefa3a7a6b945b460e8c035c6334c145cf62ae63f16a4ac4db15b36455f92e94d2040ba12a8dc6d59eed43d666e7f311d8d350ef2af4a85d1aa166279dd72180a565fd1ba75e3c6fe064dcff9d3794db98a4047ee952727ad32d3468df87dd4c8009af904987439718db3dac594c27d373e05c154c40750cc7ab5edb7d202761b9df4192d461bea6b1828cfe5c0dcbac1ae53cf72b4826722188d91727464a4ce0e37f4ec83119ebeb9199cd2a8560388fa0205f1ea019136fbf2a681552af5ea12aff617e495cb2306ae6e313e6b2bfc7501784f529ae4f33730df54033fc5eaea228476b334743b4870b4e8f87e4efffdc7750a317377426217f4aa3241f191d6e6202f6a961b219169506efbe16de7e87583ee1b19a32f54da3e05f7e8e72b412a1dcf2581379b529a65f53667ed733ee2c1ce002abaa4767337b0f4b749ccec023316ae346bee262174f41266dc550938c0094de1eec70d020f4053a978619ae8c11fff27a5", "password"},
Expand Down

0 comments on commit 51f7f3d

Please sign in to comment.