Openwall GNU/*/Linux - a small security-enhanced Linux distro for servers
[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Date: Tue, 23 Oct 2012 20:59:07 +0530
From: Dhiru Kholia <dhiru.kholia@...il.com>
To: john-dev@...ts.openwall.com
Subject: Cracking PPTP MSCHAPv2 with JtR

Hi,

pptp_fmt_plug.c (attached) is quite similar to MSCHAPv2_fmt_plug.c. It
is a separate plug-in since I didn't want to complicate existing code
(it is already complex enough!).

It supports both hash formats which are produced by Ettercap (after it
is patched).

✗ ../run/john -fo:pptp -t
Benchmarking: PPTP MSCHAPv2 MD4 DES [32/64]... DONE
Raw:	2599K c/s real, 2599K c/s virtual

$ src/ettercap -Tq -r ~/asleap-2.2/data/pptp.dump
ettercap 0.7.5 copyright 2001-2012 Ettercap Development Team
...
Tunnel PPTP: 192.168.1.3 -> 192.168.1.1
PPP*MS-CHAP Password*scott:$pptp$0$7C00A1A403CA7DF5$565FE2492FD5FB88EDAEC934C00D282C046227406C31609B$scott

This plug-in essentially obsoletes asleap-2.2 software (and various
scripts around it) since this plug-in is multi-core capable. Please
review.

-- 
Cheers,
Dhiru

/* Offline cracker for PPTP MSCHAP v2 software VPN. Hacked together during
 * October of 2012 by Dhiru Kholia <dhiru.kholia at gmail.com>.
 *
 * 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.
 *
 * Input Format : username:$pptp$type$challenge$response$username */

#include "sha.h"
#include <openssl/des.h>
#include <string.h>
#include "arch.h"
#include "misc.h"
#include "common.h"
#include "formats.h"
#include "params.h"
#include "options.h"
#ifdef _OPENMP
static int omp_t = 1;
#include <omp.h>
#define OMP_SCALE               64
#endif
#ifndef uchar
#define uchar unsigned char
#endif
#define FORMAT_LABEL		"pptp"
#define FORMAT_NAME		"PPTP MSCHAPv2 MD4 DES"
#define ALGORITHM_NAME		"32/" ARCH_BITS_STR
#define BENCHMARK_COMMENT	""
#define BENCHMARK_LENGTH	-1
#define PLAINTEXT_LENGTH	32
#define BINARY_SIZE		24
#define PARTIAL_BINARY_SIZE  8
#define SALT_SIZE		16+2
#define MIN_KEYS_PER_CRYPT	1
#define MAX_KEYS_PER_CRYPT	1

static struct fmt_tests pptp_tests[] = {
	{"$pptp$1$e3a5d0775370bda51e16219a06b0278f$84c4b33e00d9231645598acf91c384800000000000000000565fe2492fd5fb88edaec934c00d282c046227406c31609b00$scott", "turquoise"},
	/* Ettercap generated test vectors below */
	{"$pptp$1$E3A5D0775370BDA51E16219A06B0278F$84C4B33E00D9231645598ACF91C384800000000000000000565FE2492FD5FB88EDAEC934C00D282C046227406C31609B00$scott", "turquoise"},
	{"$pptp$0$7C00A1A403CA7DF5$565FE2492FD5FB88EDAEC934C00D282C046227406C31609B$scott", "turquoise"},
	{NULL}
};

static uchar (*saved_plain)[PLAINTEXT_LENGTH + 1];
static int (*saved_len);
static uchar (*saved_key)[21];
static uchar (*output)[PARTIAL_BINARY_SIZE];
static uchar *challenge;
static int keys_prepared;

#include "unicode.h"

static void init(struct fmt_main *self)
{
#ifdef _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_plain = mem_calloc_tiny(sizeof(*saved_plain) * self->params.max_keys_per_crypt, MEM_ALIGN_NONE);
	saved_len = mem_calloc_tiny(sizeof(*saved_len) * self->params.max_keys_per_crypt, MEM_ALIGN_WORD);
	saved_key = mem_calloc_tiny(sizeof(*saved_key) * self->params.max_keys_per_crypt, MEM_ALIGN_NONE);
	output = mem_alloc_tiny(sizeof(*output) * self->params.max_keys_per_crypt, MEM_ALIGN_WORD);
}

static int valid(char *ciphertext, struct fmt_main *self)
{
	return !strncmp(ciphertext, "$pptp$", 6);
}

static inline void setup_des_key(unsigned char key_56[], DES_key_schedule *ks)
{
	DES_cblock key;

	key[0] = key_56[0];
	key[1] = (key_56[0] << 7) | (key_56[1] >> 1);
	key[2] = (key_56[1] << 6) | (key_56[2] >> 2);
	key[3] = (key_56[2] << 5) | (key_56[3] >> 3);
	key[4] = (key_56[3] << 4) | (key_56[4] >> 4);
	key[5] = (key_56[4] << 3) | (key_56[5] >> 5);
	key[6] = (key_56[5] << 2) | (key_56[6] >> 6);
	key[7] = (key_56[6] << 1);
	DES_set_key(&key, ks);
}

int gethashlast2(unsigned char* challenge, unsigned char* response, unsigned char*lasthash)
{

	int i;
	unsigned char zpwhash[7] = { 0, 0, 0, 0, 0, 0, 0 };
	unsigned char cipher[8];
	DES_key_schedule ks;

	for (i = 0; i <= 0xffff; i++) {
		zpwhash[0] = i >> 8;
		zpwhash[1] = i & 0xff;

		setup_des_key(zpwhash, &ks);
		DES_ecb_encrypt((DES_cblock*)challenge, (DES_cblock*)cipher, &ks, DES_ENCRYPT);

		if (memcmp(cipher, response + 16, 8) == 0) {
			/* Success in calculating the last 2 of the hash */
			lasthash[0]= zpwhash[0];
			lasthash[1]= zpwhash[1];
			return 0;
		}
	}

	return (1);
}
static void *get_salt(char *ciphertext)
{
	SHA_CTX ctx;
	static unsigned char binary_salt[16 + 2];
	unsigned char auth_challenge[16];
	unsigned char peer_challenge[16];
	unsigned char response[24];
	int type;
	char *ctcopy = strdup(ciphertext);
	char *keeptr = ctcopy;
	char *p;
	int i;
	ctcopy += 6; /* skip over "$pptp$" */
	p = strtok(ctcopy, "$");
	type = atoi(p);
	p = strtok(NULL, "$");
	if(type == 1) { /* user has given us raw packet values */
		for (i = 0; i < 16; i++)
			auth_challenge[i] = atoi16[ARCH_INDEX(p[i * 2])] * 16
				+ atoi16[ARCH_INDEX(p[i * 2 + 1])];
		p = strtok(NULL, "$");
		for (i = 0; i < 16; i++)
			peer_challenge[i] = atoi16[ARCH_INDEX(p[i * 2])] * 16
				+ atoi16[ARCH_INDEX(p[i * 2 + 1])];
	}
	else { /* user has given us processed values */
		for (i = 0; i < 8; i++)
			auth_challenge[i] = atoi16[ARCH_INDEX(p[i * 2])] * 16
				+ atoi16[ARCH_INDEX(p[i * 2 + 1])];
	}

	if(type == 1) { /* user has given us raw packet values */
		p = strrchr(ciphertext, '$') + 1; /* get username */
		SHA1_Init(&ctx);
		SHA1_Update(&ctx, peer_challenge, 16);
		SHA1_Update(&ctx, auth_challenge, 16);
		SHA1_Update(&ctx, p, strlen(p));
		SHA1_Final(binary_salt, &ctx);
		p = ciphertext + 6 + 2 + 32 + 1 + 48;
		for (i = 0; i < BINARY_SIZE; i++)
			response[i] = atoi16[ARCH_INDEX(p[i * 2])] * 16
				+ atoi16[ARCH_INDEX(p[i * 2 + 1])];
	}
	else {
		p = strtok(NULL, "$");
		for (i = 0; i < BINARY_SIZE; i++)
			response[i] = atoi16[ARCH_INDEX(p[i * 2])] * 16
				+ atoi16[ARCH_INDEX(p[i * 2 + 1])];
	}
	MEM_FREE(keeptr);
	/* Attempting to recover last 2 of hash */
	if(gethashlast2(binary_salt, response, &binary_salt[16]))
		printf("\tCould not recover last 2 bytes of hash from the challenge/response.  Sorry it didn't work out for %s\n", ciphertext);
	return (void *)binary_salt;
}

static void *get_binary(char *ciphertext)
{
	static union {
		unsigned char c[BINARY_SIZE];
		ARCH_WORD dummy;
	} buf;
	unsigned char *out = buf.c;
	char *p;
	int i;
	int type;
	char *ctcopy = strdup(ciphertext);
	char *keeptr = ctcopy;
	ctcopy += 6; /* skip over "$pptp$" */
	p = strtok(ctcopy, "$");
	type = atoi(p);
	if (type == 1)
		p = ciphertext + 6 + 2 + 32 + 1 + 48;
	else
		p = ciphertext + 6 + 2 + 16 + 1;
	
	for (i = 0; i < BINARY_SIZE; i++)
		out[i] = atoi16[ARCH_INDEX(p[i * 2])] * 16
			+ atoi16[ARCH_INDEX(p[i * 2 + 1])];

	MEM_FREE(keeptr);
	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 get_hash_0(int index)
{
	return *(ARCH_WORD_32 *)output[index] & 0xF;
}

static int get_hash_1(int index)
{
	return *(ARCH_WORD_32 *)output[index] & 0xFF;
}

static int get_hash_2(int index)
{
	return *(ARCH_WORD_32 *)output[index] & 0xFFF;
}

static int get_hash_3(int index)
{
	return *(ARCH_WORD_32 *)output[index] & 0xFFFF;
}

static int get_hash_4(int index)
{
	return *(ARCH_WORD_32 *)output[index] & 0xFFFFF;
}

static void set_salt(void *salt)
{
	challenge = salt;
}

static void crypt_all(int count)
{
	DES_key_schedule ks;
	int i;

	if (!keys_prepared) {
#ifdef _OPENMP
#pragma omp parallel for
#endif
		for(i=0; i<count; i++) {
			int len;
			/* Generate 16-byte NTLM hash */
			len = E_md4hash((uchar *) saved_plain[i], saved_len[i], saved_key[i]);

			if (len <= 0)
				saved_plain[i][-len] = 0; // match if it was truncated

			/* NULL-padding the 16-byte hash to 21-bytes is made in cmp_exact if needed */
		}
		keys_prepared = 1;
	}

#ifdef _OPENMP
#pragma omp parallel for default(none) private(i, ks) shared(count, output, challenge, saved_key)
#endif
	for(i=0; i<count; i++) {
/*		if (saved_key[i][14] != challenge[16] || saved_key[i][15] != challenge[17])
			continue; */

		/* Just do first DES for a partial binary */
		setup_des_key(saved_key[i], &ks);
		DES_ecb_encrypt((DES_cblock*)challenge, (DES_cblock*)output[i], &ks, DES_ENCRYPT);
	}
}

static int cmp_all(void *binary, int count)
{
	int index = 0;
	for(; index<count; index++)
		if (!memcmp(output[index], binary, PARTIAL_BINARY_SIZE))
			return 1;
	return 0;
}

static int cmp_one(void *binary, int index)
{
	return (!memcmp(output[index], binary, PARTIAL_BINARY_SIZE));
}

static int cmp_exact(char *source, int index)
{
	DES_key_schedule ks;
	uchar binary[24];

	/* NULL-pad 16-byte NTLM hash to 21-bytes (postponed until now) */
	memset(&saved_key[index][16], 0, 5);

	/* Split resultant value into three 7-byte thirds
	   DES-encrypt challenge using each third as a key
	   Concatenate three 8-byte resulting values to form 24-byte LM response
	*/
	setup_des_key(saved_key[index], &ks);
	DES_ecb_encrypt((DES_cblock*)challenge, (DES_cblock*)binary, &ks, DES_ENCRYPT);
	setup_des_key(&saved_key[index][7], &ks);
	DES_ecb_encrypt((DES_cblock*)challenge, (DES_cblock*)&binary[8], &ks, DES_ENCRYPT);
	setup_des_key(&saved_key[index][14], &ks);
	DES_ecb_encrypt((DES_cblock*)challenge, (DES_cblock*)&binary[16], &ks, DES_ENCRYPT);

	return !memcmp(binary, get_binary(source), BINARY_SIZE);
}

static void pptp_set_key(char *key, int index)
{
	saved_len[index] = strlen(key);
	memcpy(saved_plain[index], key, saved_len[index] + 1);
	keys_prepared = 0;
}

static char *get_key(int index)
{
	return (char *)saved_plain[index];
}

struct fmt_main pptp_fmt = {
	{
		FORMAT_LABEL,
		FORMAT_NAME,
		ALGORITHM_NAME,
		BENCHMARK_COMMENT,
		BENCHMARK_LENGTH,
		PLAINTEXT_LENGTH,
		BINARY_SIZE,
#if FMT_MAIN_VERSION > 9
		DEFAULT_ALIGN,
#endif
		SALT_SIZE,
#if FMT_MAIN_VERSION > 9
		DEFAULT_ALIGN,
#endif
		MIN_KEYS_PER_CRYPT,
		MAX_KEYS_PER_CRYPT,
		FMT_CASE | FMT_8_BIT | FMT_SPLIT_UNIFIES_CASE | FMT_OMP | FMT_UNICODE | FMT_UTF8,
		pptp_tests
	}, {
		init,
		fmt_default_prepare,
		valid,
		fmt_default_split,
		get_binary,
		get_salt,
#if FMT_MAIN_VERSION > 9
		fmt_default_source,
#endif
		{
			binary_hash_0,
			binary_hash_1,
			binary_hash_2,
			binary_hash_3,
			binary_hash_4
		},
		fmt_default_salt_hash,
		set_salt,
		pptp_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
		},
		cmp_all,
		cmp_one,
		cmp_exact
	}
};

Powered by blists - more mailing lists

Your e-mail address:

Powered by Openwall GNU/*/Linux - Powered by OpenVZ