Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Add support for cracking LastPass's offline database

  • Loading branch information...
commit ac82ff5c14da242e00ee025ad1597be8ceb8d692 1 parent d76468e
@kholia kholia authored
View
32 run/lastpass2john.py
@@ -0,0 +1,32 @@
+#!/usr/bin/env python
+
+"""lastpass2john.py converts input LastPass data into a format suitable
+for use with JtR.
+
+Output Format:filename:$lp$email(salt)$hash"""
+
+import sys
+import binascii
+import base64
+
+
+def process_file(email, filename):
+ try:
+ f = open(filename)
+ except Exception, e:
+ print >> sys.stderr, "%s : %s" % (filename, str(e))
+ return 2
+
+ f.readline() # ignore first line
+ data = f.readline()
+ if not data:
+ print >> sys.stderr, "%s : %s" % (filename, "Unable to parse data. Are you sure this is LastPass data?")
+
+ print "%s:$lp$%s:%s" % (filename, email, binascii.hexlify(base64.decodestring(data)))
+
+if __name__ == "__main__":
+ if len(sys.argv) < 3:
+ print >> sys.stderr, "Usage: %s <email address> <LastPass *._lpall.slps file>" % sys.argv[0]
+ sys.exit(-1)
+
+ process_file(sys.argv[1], sys.argv[2])
View
4 src/django_fmt.c
@@ -177,7 +177,7 @@ static void set_salt(void *salt)
#define SHA256_DIGEST_LENGTH 32
#endif
-void pbkdf2(unsigned char *K, int KL, unsigned char *S, int SL, int R, ARCH_WORD_32 *dgst)
+void pbkdf2_sha256(unsigned char *K, int KL, unsigned char *S, int SL, int R, ARCH_WORD_32 *dgst)
{
@magnumripper Owner

This should be static. Is it used by others? Then you should move it out of django!

@kholia Collaborator
kholia added a note

Yes, 3 formats are using this function (hence it is not static).

I will move it out to somewhere else (suggestions?).

@magnumripper Owner
@kholia Collaborator
kholia added a note

Sounds good. I will open a pull request so that it gets reviewed properly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
SHA256_CTX ctx, tmp_ctx1, tmp_ctx2;
unsigned char ipad[SHA256_CBLOCK], opad[SHA256_CBLOCK], tmp_hash[SHA256_DIGEST_LENGTH];
@@ -242,7 +242,7 @@ static void crypt_all(int count)
// cur_salt->salt, strlen((char*)cur_salt->salt),
// cur_salt->iterations, EVP_sha256(), 32, (unsigned char*)crypt_out[index]);
- pbkdf2((unsigned char *)saved_key[index], strlen(saved_key[index]),
+ pbkdf2_sha256((unsigned char *)saved_key[index], strlen(saved_key[index]),
cur_salt->salt, strlen((char*)cur_salt->salt),
cur_salt->iterations, crypt_out[index]);
}
View
253 src/lastpass_fmt_plug.c
@@ -0,0 +1,253 @@
+/* LastPass offline cracker patch for JtR. Hacked together during January of 2013 by
+ * Dhiru Kholia <dhiru.kholia at gmail.com>.
+ *
+ * All the hard work was done by Milen (author of hashkill).
+ *
+ * This software is Copyright (c) 2012, Dhiru Kholia <dhiru.kholia 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. */
+
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+#include "arch.h"
+#include "misc.h"
+#include "common.h"
+#include "formats.h"
+#include "params.h"
+#include "options.h"
+#include <openssl/aes.h>
+#ifdef _OPENMP
+#include <omp.h>
+#define OMP_SCALE 64
+#endif
+
+#define FORMAT_LABEL "lp"
+#define FORMAT_NAME "LastPass offline PBKDF2 SHA1"
+#define ALGORITHM_NAME "32/" ARCH_BITS_STR
+#define BENCHMARK_COMMENT ""
+#define BENCHMARK_LENGTH -1
+#define PLAINTEXT_LENGTH 64
+#define BINARY_SIZE 16
+#define SALT_SIZE sizeof(struct custom_salt)
+#define MIN_KEYS_PER_CRYPT 1
+#define MAX_KEYS_PER_CRYPT 1
+
+static struct fmt_tests lastpass_tests[] = {
+ {"$lp$hackme@mailinator.com$6f5d8cec3615fc9ac7ba2e0569bce4f5", "strongpassword"},
+ {NULL}
+};
+
+#if defined (_OPENMP)
+static int omp_t = 1;
+#endif
+static char (*saved_key)[PLAINTEXT_LENGTH + 1];
+static ARCH_WORD_32 (*crypt_out)[32 / sizeof(ARCH_WORD_32)];
+
+static struct custom_salt {
+ int iterations;
+ int salt_length;
+ unsigned char salt[32];
+} *cur_salt;
+
+static void init(struct fmt_main *self)
+{
+#if defined (_OPENMP)
+ omp_t = omp_get_max_threads();
+ self->params.min_keys_per_crypt *= omp_t;
+ omp_t *= OMP_SCALE;
+ self->params.max_keys_per_crypt *= omp_t;
+#endif
+ saved_key = mem_calloc_tiny(sizeof(*saved_key) *
+ self->params.max_keys_per_crypt, MEM_ALIGN_WORD);
+ crypt_out = mem_calloc_tiny(sizeof(*crypt_out) * self->params.max_keys_per_crypt, MEM_ALIGN_WORD);
+}
+
+static int ishex(char *q)
+{
+ while (atoi16[ARCH_INDEX(*q)] != 0x7F)
+ q++;
+ return !*q;
+}
+
+static int valid(char *ciphertext, struct fmt_main *self)
+{
+ char *ctcopy;
+ char *keeptr;
+ char *p;
+ if (strncmp(ciphertext, "$lp$", 4))
+ return 0;
+ ctcopy = strdup(ciphertext);
+ keeptr = ctcopy;
+ ctcopy += 4;
+ if ((p = strtok(ctcopy, "$")) == NULL) /* email */
+ goto err;
+ if ((p = strtok(NULL, "*")) == NULL) /* hash */
+ if (strlen(p) != 32)
+ goto err;
+ if (!ishex(p))
+ goto err;
+
+ MEM_FREE(keeptr);
+ return 1;
+
+err:
+ MEM_FREE(keeptr);
+ return 0;
+}
+
+static void *get_salt(char *ciphertext)
+{
+ char *ctcopy = strdup(ciphertext);
+ char *keeptr = ctcopy;
+ char *p;
+ static struct custom_salt cs;
+ ctcopy += 4; /* skip over "$lp$" */
+ p = strtok(ctcopy, "$");
+ strncpy((char*)cs.salt, p, 32);
+ cs.salt_length = strlen((char*)p);
+ MEM_FREE(keeptr);
+ return (void *)&cs;
+}
+
+static void *get_binary(char *ciphertext)
+{
+ static union {
+ unsigned char c[BINARY_SIZE+1];
+ ARCH_WORD dummy;
+ } buf;
+ unsigned char *out = buf.c;
+ char *p;
+ int i;
+ p = strrchr(ciphertext, '$') + 1;
+ for (i = 0; i < BINARY_SIZE; i++) {
+ out[i] = (atoi16[ARCH_INDEX(*p)] << 4) |
+ atoi16[ARCH_INDEX(p[1])];
+ p += 2;
+ }
+ return out;
+}
+
+static int binary_hash_0(void *binary) { return *(ARCH_WORD_32 *)binary & 0xf; }
+static int binary_hash_1(void *binary) { return *(ARCH_WORD_32 *)binary & 0xff; }
+static int binary_hash_2(void *binary) { return *(ARCH_WORD_32 *)binary & 0xfff; }
+static int binary_hash_3(void *binary) { return *(ARCH_WORD_32 *)binary & 0xffff; }
+static int binary_hash_4(void *binary) { return *(ARCH_WORD_32 *)binary & 0xfffff; }
+static int binary_hash_5(void *binary) { return *(ARCH_WORD_32 *)binary & 0xffffff; }
+static int binary_hash_6(void *binary) { return *(ARCH_WORD_32 *)binary & 0x7ffffff; }
+
+static int get_hash_0(int index) { return crypt_out[index][0] & 0xf; }
+static int get_hash_1(int index) { return crypt_out[index][0] & 0xff; }
+static int get_hash_2(int index) { return crypt_out[index][0] & 0xfff; }
+static int get_hash_3(int index) { return crypt_out[index][0] & 0xffff; }
+static int get_hash_4(int index) { return crypt_out[index][0] & 0xfffff; }
+static int get_hash_5(int index) { return crypt_out[index][0] & 0xffffff; }
+static int get_hash_6(int index) { return crypt_out[index][0] & 0x7ffffff; }
+
+static void set_salt(void *salt)
+{
+ cur_salt = (struct custom_salt *)salt;
+}
+
+void pbkdf2_sha256(unsigned char *K, int KL, unsigned char *S, int SL, int R, ARCH_WORD_32 *dgst);
+static void crypt_all(int count)
+{
+ int index = 0;
+#ifdef _OPENMP
+#pragma omp parallel for
+ for (index = 0; index < count; index++)
+#endif
+ {
+ unsigned char key[32];
+ AES_KEY akey;
+ pbkdf2_sha256((unsigned char*)saved_key[index], strlen(saved_key[index]), cur_salt->salt, cur_salt->salt_length, 500, (ARCH_WORD_32 *)key);
+ memset(&akey, 0, sizeof(AES_KEY));
+ AES_set_encrypt_key((unsigned char*)key, 256, &akey);
+ AES_ecb_encrypt((unsigned char*)"lastpass rocks\x02\x02", (unsigned char*)crypt_out[index], &akey, AES_ENCRYPT);
+ }
+}
+
+static int cmp_all(void *binary, int count)
+{
+ int index = 0;
+ for (; index < count; index++)
+ if (!memcmp(binary, crypt_out[index], BINARY_SIZE))
+ return 1;
+ return 0;
+}
+
+static int cmp_one(void *binary, int index)
+{
+ return !memcmp(binary, crypt_out[index], BINARY_SIZE);
+}
+
+static int cmp_exact(char *source, int index)
+{
+ return 1;
+}
+
+static void lastpass_set_key(char *key, int index)
+{
+ int saved_key_length = strlen(key);
+ if (saved_key_length > PLAINTEXT_LENGTH)
+ saved_key_length = PLAINTEXT_LENGTH;
+ memcpy(saved_key[index], key, saved_key_length);
+ saved_key[index][saved_key_length] = 0;
+}
+
+static char *get_key(int index)
+{
+ return saved_key[index];
+}
+
+struct fmt_main fmt_lastpass = {
+ {
+ FORMAT_LABEL,
+ FORMAT_NAME,
+ ALGORITHM_NAME,
+ BENCHMARK_COMMENT,
+ BENCHMARK_LENGTH,
+ PLAINTEXT_LENGTH,
+ BINARY_SIZE,
+ SALT_SIZE,
+ MIN_KEYS_PER_CRYPT,
+ MAX_KEYS_PER_CRYPT,
+ FMT_CASE | FMT_8_BIT | FMT_OMP,
+ lastpass_tests
+ }, {
+ init,
+ fmt_default_prepare,
+ valid,
+ fmt_default_split,
+ get_binary,
+ get_salt,
+ {
+ binary_hash_0,
+ binary_hash_1,
+ binary_hash_2,
+ binary_hash_3,
+ binary_hash_4,
+ binary_hash_5,
+ binary_hash_6
+ },
+ fmt_default_salt_hash,
+ set_salt,
+ lastpass_set_key,
+ get_key,
+ fmt_default_clear_keys,
+ crypt_all,
+ {
+ get_hash_0,
+ get_hash_1,
+ get_hash_2,
+ get_hash_3,
+ get_hash_4,
+ get_hash_5,
+ get_hash_6
+ },
+ cmp_all,
+ cmp_one,
+ cmp_exact
+ }
+};
View
4 src/lastpass_sniffed_fmt_plug.c
@@ -68,7 +68,7 @@ static struct custom_salt {
unsigned char length;
} *cur_salt;
-void pbkdf2(unsigned char *K, int KL, unsigned char *S, int SL, int R, ARCH_WORD_32 *dgst);
+void pbkdf2_sha256(unsigned char *K, int KL, unsigned char *S, int SL, int R, ARCH_WORD_32 *dgst);
static void init(struct fmt_main *self)
{
@@ -159,7 +159,7 @@ static void crypt_all(int count)
unsigned char iv[16] = { 0 };
AES_KEY akey;
// PKCS5_PBKDF2_HMAC(saved_key[index], strlen(saved_key[index]), (unsigned char*)cur_salt->username, strlen((char*)cur_salt->username), cur_salt->iterations, EVP_sha256(), 32, key);
- pbkdf2((unsigned char*)saved_key[index], strlen(saved_key[index]), (unsigned char*)cur_salt->username, strlen((char*)cur_salt->username), cur_salt->iterations, key);
+ pbkdf2_sha256((unsigned char*)saved_key[index], strlen(saved_key[index]), (unsigned char*)cur_salt->username, strlen((char*)cur_salt->username), cur_salt->iterations, key);
if(AES_set_decrypt_key((const unsigned char *)key, 256, &akey) < 0) {
fprintf(stderr, "AES_set_derypt_key failed in crypt!\n");
Please sign in to comment.
Something went wrong with that request. Please try again.