Openwall GNU/*/Linux - a small security-enhanced Linux distro for servers
[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Date: Mon, 28 Sep 2015 11:50:56 +0200
From: "Michael Kramer" <michael.kramer@...-konstanz.de>
To: john-dev@...ts.openwall.com
Subject: Kerberoast for John

Hello John Community,

I'm a Information Engineering Student from Germany and I've worked on a John the Ripper format plugin while I was working at SySS GmbH.

I wanted to share my work with the John Community. The work is based on the Kerberoast Python script from Tim Medin and I've ported it from there to C and then into John.

I had to cheat a little since the cracking algorithm of those Kerberos Tickets is not like the usual cracking John does. The first 16 bytes of the file will be used as a checksum, while the rest of the ticket will be hashed a few times with the cleartext password we are trying and then compared to the checksum itself. If this matches, our guessed password was the one the Ticket was encrypted with. Therefore I saved the first 16 bytes inside the salt variable John uses.

I've included the fmt_plug file for John, a testfile with 3 testhashes the module is able to crack, and also part of the python script from Tim Medin to parse kirbi files into the format my John module uses.

I've created the tickets I've tested my module with inside a VM and everything works with them.

Since I'm fairly new to John developement I haven't optimized my code yet.

But I've encountered a strange bug and thought maybe one of you could help me.

If I try to crack my test tickets with the ./john acrackfile call without any arguments (except the file of course) the performance is okay. It's not great, but it has the same performance over a long peroid of time.

But if I try to crack a larger file (let's say 300 tickets/hashes), John behaves strange. The performance starts very good (150000p/s) but then gets worse pretty fast. After 17 hours I had a performance of 500p/s.

Could anyone help me with this behaviour? Or at lest hint me what could cause it? I've already tried to check if there are any memory-leaks, but neither Valgrim nor a check of the used memory has shown anything.

Greetings,
Michael Kramer

# Part of the Kerberoast Scrypt from Tim Medin to extract the Kerberos tickets from a kirbi file. 
# Modification to parse them into the JTR-format by Michael Kramer (SySS GmbH)

from pyasn1.codec.ber import encoder, decoder
from multiprocessing import JoinableQueue, Manager
import glob

if __name__ == '__main__':
	import argparse

	parser = argparse.ArgumentParser(description='Read Mimikatz kerberos ticket then modify it and save it in crack_file')
	parser.add_argument('files', nargs='+', metavar='file.kirbi',
					help='File name to crack. Use asterisk \'*\' for many files.\n Files are exported with mimikatz or from extracttgsrepfrompcap.py')
	
	args = parser.parse_args()

	manager = Manager()
	enctickets = manager.list()

	i = 0
	for path in args.files:
		for f in glob.glob(path):
			with open(f, 'rb') as fd:
				data = fd.read()
			#data = open('f.read()

			if data[0] == '\x76':
				# rem dump 
				enctickets.append((str(decoder.decode(data)[0][2][0][3][2]), i, f))
				i += 1
			elif data[:2] == '6d':
				for ticket in data.strip().split('\n'):
					enctickets.append((str(decoder.decode(ticket.decode('hex'))[0][4][3][2]), i, f))
					i += 1

	out=open("crack_file","wb")	
	for et in enctickets:
		out.write("$krb5tgs$unkown:"+et[0][:16].encode("hex")+"$"+et[0][16:].encode("hex")+"\n")
	out.close
		

//Based on the work by Tim Medin
//Port from his Pythonscript to John by Michael Kramer (SySS GmbH)
//Copyright 2015 Michael Kramer
/*Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

#if FMT_EXTERNS_H
extern struct fmt_main fmt_krb5tgs;
#elif FMT_REGISTERS_H
john_register_one(&fmt_krb5tgs);
#else

#include <stdio.h>
#include <string.h>
#include <openssl/hmac.h>
 #include <openssl/rc4.h>

#include "misc.h"
#include "formats.h"
#include "common.h"


#include "memdbg.h"


#define FORMAT_LABEL                   	"krb5tgs"
#define FORMAT_NAME                    	"Kerberos5 TGS"
#define ALGORITHM_NAME                 	"NTLM/HMAC/RC4Crypt"
#define BENCHMARK_COMMENT              	""
#define BENCHMARK_LENGTH               	-1
#define PLAINTEXT_LENGTH               	32
#define CIPHERTEXT_LENGTH              	1500
#define BINARY_SIZE                   	1500
#define SALT_SIZE                      	16
#define BINARY_ALIGN			1
#define SALT_ALIGN			1
#define MIN_KEYS_PER_CRYPT             	1
#define MAX_KEYS_PER_CRYPT             	1

//Init values
#define INIT_A 0x67452301
#define INIT_B 0xefcdab89
#define INIT_C 0x98badcfe
#define INIT_D 0x10325476
 
#define SQRT_2 0x5a827999
#define SQRT_3 0x6ed9eba1


static struct fmt_tests tests[] = {
};



static unsigned char (*crypt_key)[SALT_SIZE];
static char (*saved_key)[PLAINTEXT_LENGTH + 1];
static unsigned char cursalt[SALT_SIZE];
unsigned char realcipher[BINARY_SIZE];
static int edata2len;

static void init(struct fmt_main *self)
{
	crypt_key = mem_calloc_tiny(
	    (sizeof(*crypt_key) + sizeof(*saved_key)) *
	    self->params.max_keys_per_crypt,
	    MEM_ALIGN_CACHE);
	saved_key = (void *)((char *)crypt_key +
	    sizeof(*crypt_key) * self->params.max_keys_per_crypt);
}

// ntlm_crypt function from the JTR site

static void ntlm_crypt(char *key, unsigned int *output)
{

	
	unsigned int nt_buffer[16];
	int i=0;
	int length=strlen(key);
	memset(nt_buffer,0,16*4);
	//The length of key need to be <= 27
	for(;i<length/2;i++)	
		nt_buffer[i] = key[2*i] | (key[2*i+1]<<16);
 
	//padding
	if(length%2==1)
		nt_buffer[i] = key[length-1] | 0x800000;
	else
		nt_buffer[i]=0x80;
	//put the length
	nt_buffer[14] = length << 4;	


	unsigned int a = INIT_A;
	unsigned int b = INIT_B;
	unsigned int c = INIT_C;
	unsigned int d = INIT_D;
 
	/* Round 1 */
	a += (d ^ (b & (c ^ d)))  +  nt_buffer[0]  ;a = (a << 3 ) | (a >> 29);
	d += (c ^ (a & (b ^ c)))  +  nt_buffer[1]  ;d = (d << 7 ) | (d >> 25);
	c += (b ^ (d & (a ^ b)))  +  nt_buffer[2]  ;c = (c << 11) | (c >> 21);
	b += (a ^ (c & (d ^ a)))  +  nt_buffer[3]  ;b = (b << 19) | (b >> 13);
 
	a += (d ^ (b & (c ^ d)))  +  nt_buffer[4]  ;a = (a << 3 ) | (a >> 29);
	d += (c ^ (a & (b ^ c)))  +  nt_buffer[5]  ;d = (d << 7 ) | (d >> 25);
	c += (b ^ (d & (a ^ b)))  +  nt_buffer[6]  ;c = (c << 11) | (c >> 21);
	b += (a ^ (c & (d ^ a)))  +  nt_buffer[7]  ;b = (b << 19) | (b >> 13);
 
	a += (d ^ (b & (c ^ d)))  +  nt_buffer[8]  ;a = (a << 3 ) | (a >> 29);
	d += (c ^ (a & (b ^ c)))  +  nt_buffer[9]  ;d = (d << 7 ) | (d >> 25);
	c += (b ^ (d & (a ^ b)))  +  nt_buffer[10] ;c = (c << 11) | (c >> 21);
	b += (a ^ (c & (d ^ a)))  +  nt_buffer[11] ;b = (b << 19) | (b >> 13);
 
	a += (d ^ (b & (c ^ d)))  +  nt_buffer[12] ;a = (a << 3 ) | (a >> 29);
	d += (c ^ (a & (b ^ c)))  +  nt_buffer[13] ;d = (d << 7 ) | (d >> 25);
	c += (b ^ (d & (a ^ b)))  +  nt_buffer[14] ;c = (c << 11) | (c >> 21);
	b += (a ^ (c & (d ^ a)))  +  nt_buffer[15] ;b = (b << 19) | (b >> 13);
 
	/* Round 2 */
	a += ((b & (c | d)) | (c & d)) + nt_buffer[0] +SQRT_2; a = (a<<3 ) | (a>>29);
	d += ((a & (b | c)) | (b & c)) + nt_buffer[4] +SQRT_2; d = (d<<5 ) | (d>>27);
	c += ((d & (a | b)) | (a & b)) + nt_buffer[8] +SQRT_2; c = (c<<9 ) | (c>>23);
	b += ((c & (d | a)) | (d & a)) + nt_buffer[12]+SQRT_2; b = (b<<13) | (b>>19);
 
	a += ((b & (c | d)) | (c & d)) + nt_buffer[1] +SQRT_2; a = (a<<3 ) | (a>>29);
	d += ((a & (b | c)) | (b & c)) + nt_buffer[5] +SQRT_2; d = (d<<5 ) | (d>>27);
	c += ((d & (a | b)) | (a & b)) + nt_buffer[9] +SQRT_2; c = (c<<9 ) | (c>>23);
	b += ((c & (d | a)) | (d & a)) + nt_buffer[13]+SQRT_2; b = (b<<13) | (b>>19);
 
	a += ((b & (c | d)) | (c & d)) + nt_buffer[2] +SQRT_2; a = (a<<3 ) | (a>>29);
	d += ((a & (b | c)) | (b & c)) + nt_buffer[6] +SQRT_2; d = (d<<5 ) | (d>>27);
	c += ((d & (a | b)) | (a & b)) + nt_buffer[10]+SQRT_2; c = (c<<9 ) | (c>>23);
	b += ((c & (d | a)) | (d & a)) + nt_buffer[14]+SQRT_2; b = (b<<13) | (b>>19);
 
	a += ((b & (c | d)) | (c & d)) + nt_buffer[3] +SQRT_2; a = (a<<3 ) | (a>>29);
	d += ((a & (b | c)) | (b & c)) + nt_buffer[7] +SQRT_2; d = (d<<5 ) | (d>>27);
	c += ((d & (a | b)) | (a & b)) + nt_buffer[11]+SQRT_2; c = (c<<9 ) | (c>>23);
	b += ((c & (d | a)) | (d & a)) + nt_buffer[15]+SQRT_2; b = (b<<13) | (b>>19);
 
	/* Round 3 */
	a += (d ^ c ^ b) + nt_buffer[0]  +  SQRT_3; a = (a << 3 ) | (a >> 29);
	d += (c ^ b ^ a) + nt_buffer[8]  +  SQRT_3; d = (d << 9 ) | (d >> 23);
	c += (b ^ a ^ d) + nt_buffer[4]  +  SQRT_3; c = (c << 11) | (c >> 21);
	b += (a ^ d ^ c) + nt_buffer[12] +  SQRT_3; b = (b << 15) | (b >> 17);
 
	a += (d ^ c ^ b) + nt_buffer[2]  +  SQRT_3; a = (a << 3 ) | (a >> 29);
	d += (c ^ b ^ a) + nt_buffer[10] +  SQRT_3; d = (d << 9 ) | (d >> 23);
	c += (b ^ a ^ d) + nt_buffer[6]  +  SQRT_3; c = (c << 11) | (c >> 21);
	b += (a ^ d ^ c) + nt_buffer[14] +  SQRT_3; b = (b << 15) | (b >> 17);
 
	a += (d ^ c ^ b) + nt_buffer[1]  +  SQRT_3; a = (a << 3 ) | (a >> 29);
	d += (c ^ b ^ a) + nt_buffer[9]  +  SQRT_3; d = (d << 9 ) | (d >> 23);
	c += (b ^ a ^ d) + nt_buffer[5]  +  SQRT_3; c = (c << 11) | (c >> 21);
	b += (a ^ d ^ c) + nt_buffer[13] +  SQRT_3; b = (b << 15) | (b >> 17);
 
	a += (d ^ c ^ b) + nt_buffer[3]  +  SQRT_3; a = (a << 3 ) | (a >> 29);
	d += (c ^ b ^ a) + nt_buffer[11] +  SQRT_3; d = (d << 9 ) | (d >> 23);
	c += (b ^ a ^ d) + nt_buffer[7]  +  SQRT_3; c = (c << 11) | (c >> 21);
	b += (a ^ d ^ c) + nt_buffer[15] +  SQRT_3; b = (b << 15) | (b >> 17);
 
	output[0] = a + INIT_A;
	output[1] = b + INIT_B;
	output[2] = c + INIT_C;
	output[3] = d + INIT_D;
}

static void * binary (char *ciphertext)
{
	int i;
	edata2len = (strlen(ciphertext)-33)/2;
	for (i=0; i < edata2len; i++){
		realcipher[i] = atoi16[ARCH_INDEX(ciphertext[i*2+33])]*16 + atoi16[ARCH_INDEX(ciphertext[i*2+1+33])];
	}
	return ((void *) realcipher);
}

static void *get_salt(char *ciphertext) {
	int i;
	static unsigned char realsalt[SALT_SIZE];
	for (i=0; i < SALT_SIZE; i++){
		realsalt[i]=atoi16[ARCH_INDEX(ciphertext[i*2])]*16 + atoi16[ARCH_INDEX(ciphertext[i*2+1])];
	}
	return realsalt;
}

static int salt_hash(void *salt){
	return 0;
}

static void set_salt(void *salt){
	memcpy(cursalt, salt, SALT_SIZE);
	int i;
}
	


static int valid (char *ciphertext, struct fmt_main *self)
{
	char h = '$';
	if (ciphertext[32]!=h){
		return 0;
	}
	return 1;
}


static void set_key (char *key, int index)
{
	strnzcpy (saved_key[index], key, PLAINTEXT_LENGTH + 1);
}


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

static int cmp_all (void *binary, int count)
{
	unsigned char K1[SALT_SIZE];
	unsigned char K2[SALT_SIZE];
	unsigned char* K3;
	unsigned char edata1[SALT_SIZE];
	unsigned char edata2[edata2len];
	unsigned char* checksum;
	unsigned char ddata[edata2len];
	int i;

	memcpy(K1, crypt_key[0], SALT_SIZE);
	memcpy(K2, crypt_key[0], SALT_SIZE);
	memcpy(edata1, cursalt, SALT_SIZE);
	memcpy(edata2, binary, edata2len);

	K3 = HMAC(EVP_md5(), K1, 16, edata1, 16, NULL, NULL);

	RC4_KEY key;
	RC4_set_key(&key, 16, K3);
	RC4(&key, edata2len, edata2, ddata);

	checksum = HMAC(EVP_md5(), K2, 16, ddata, edata2len, NULL, NULL);

	if (!memcmp(edata1,checksum,16)){
		return 1;
	}
	
	return 0;
}

static int cmp_one (void *binary, int index)
{
	return 1;
}

static int cmp_exact (char *source, int index)
{
	return 1;
}



static int crypt_all(int *pcount, struct db_salt *salt)
{
	unsigned int output[4];
	ntlm_crypt(saved_key[0], output);
	unsigned char* key = (unsigned char*) output;
	char data[4] = {2,0,0,0};
	unsigned char* K1;
	int i;

	K1 = HMAC(EVP_md5(), key, 16, data, 4, NULL, NULL);

	memcpy(crypt_key[0], K1, SALT_SIZE);

	return 1;
}

static int get_hash1(int index) { return crypt_key[index][0] & 0xf; }
static int get_hash2(int index) { return crypt_key[index][0] & 0xff; }
static int get_hash3(int index) { return crypt_key[index][0] & 0xfff; }
static int get_hash4(int index) { return crypt_key[index][0] & 0xffff; }
static int get_hash5(int index) { return crypt_key[index][0] & 0xfffff; }
static int get_hash6(int index) { return crypt_key[index][0] & 0xffffff; }
static int get_hash7(int index) { return crypt_key[index][0] & 0x7ffffff; }
static int binary_hash1(void * binary) { return *(ARCH_WORD_32 *)binary & 0xf; }
static int binary_hash2(void * binary) { return *(ARCH_WORD_32 *)binary & 0xff; }
static int binary_hash3(void * binary) { return *(ARCH_WORD_32 *)binary & 0xfff; }
static int binary_hash4(void * binary) { return *(ARCH_WORD_32 *)binary & 0xffff; }
static int binary_hash5(void * binary) { return *(ARCH_WORD_32 *)binary & 0xfffff; }
static int binary_hash6(void * binary) { return *(ARCH_WORD_32 *)binary & 0xffffff; }
static int binary_hash7(void * binary) { return *(ARCH_WORD_32 *)binary & 0x7ffffff; }

	


struct fmt_main fmt_krb5tgs = {
	{
		FORMAT_LABEL,
		FORMAT_NAME,
		ALGORITHM_NAME,
		BENCHMARK_COMMENT,
		BENCHMARK_LENGTH,
		PLAINTEXT_LENGTH,
		BINARY_SIZE,
		BINARY_ALIGN,
		SALT_SIZE,
		SALT_ALIGN,
		MIN_KEYS_PER_CRYPT,
		MAX_KEYS_PER_CRYPT,
		FMT_CASE | FMT_8_BIT | FMT_OMP,
#if FMT_MAIN_VERSION > 11
		{ NULL },
#endif
		tests
	}, {
		init,
		fmt_default_done,
		fmt_default_reset,
		fmt_default_prepare,
		valid,
		fmt_default_split,
		binary,
		get_salt,
#if FMT_MAIN_VERSION > 11
		{ NULL },
#endif
		fmt_default_source,
		{
			binary_hash1,
			binary_hash2,
			binary_hash3,
			binary_hash4,
			binary_hash5,
			binary_hash6,
			binary_hash7
		},
		salt_hash,
		set_salt,
		set_key,
		get_key,
		fmt_default_clear_keys,
		crypt_all,
		{
			get_hash1,
			get_hash2,
			get_hash3,
			get_hash4,
			get_hash5,
			get_hash6,
			get_hash7
		},
		cmp_all,
		cmp_one,
		cmp_exact
	}
};



#endif

[ CONTENT OF TYPE application/octet-stream SKIPPED ]

Powered by blists - more mailing lists

Your e-mail address:

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