Follow @Openwall on Twitter for new release announcements and other news
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <BAY120-W382189CE653935529FAD63B0B70@phx.gbl>
Date: Mon, 16 Feb 2009 11:08:08 +0000
From: P PO1434 <p2409@...mail.com>
To: <john-users@...ts.openwall.com>
Subject: RE: 2 known letters + wordlist word --> is new format
 definition best way?


Hi Guys

OK - I've made some headway in a format module that does an MD5 of two known chars, followed by plaintext. It's messy due to my naivety, but it's almost there (I think ;)
The two chars at the start of the password change for every password provided. I've called it osc-md5 because it's used
by OSCommerce, but the code is trying to be more general.

The tricky part writing this format was knowing when to save the known characters, and when to add them back on in the different format functions.

The code works for the test conditions provided in the format (inline). It doesn't work for the file input. (Not sure why this is the case. I'm struggling with the john internals knowing
what gets called when. Someone who knows john better can probably pick up where I'm going wrong straight away). I've sprinkled comments and printf's throughout - just my
ramblings in trying to understand, probably not effcient.



Any comments/pointers appreciated. My hope is that it can become the basis of a more general 

MD5((x * known characters) +  password) format.

(To run: john --format=OSC-md5 --wordlist=password.lst --rules ../keys/osc.txt)

To test, add this source file to your john project, and update john.c to:

1. Add the format struct around line 75
extern struct fmt_main fmt_OSC; 

AND

2.In john_register_all, around line 130, register the new format:

john_register_one(&fmt_OSC);


// START OF CODE
/*
 * MD5 where first 2 character are known and supplied
 * by Pete. Test version only - handles the test conditions fine
 * NOT HANDLING FILE INPUT AT THIS STAGE!
 *
 * This algorithm is often used in PHP - sort of a fake
 * salt + plaintext which is MD5'd
 *
 * This code is really just a mod of raw_md5 which is
 *
 * Copyright (c) 2004 bartavelle
 * bartavelle at bandecon.com
 *
 * by Justin
 *
 * Minor changes by David Luyer <david at luyer.net> to
 * use a modified (faster) version of Solar Designer's
 * md5 implementation.
 *
 * More improvement by
 * Bal�zs Bucsay - earthquake at rycon.hu - http://www.rycon.hu/
 * (2times faster, but it's only works up to 54characters)
 *
 * Credits to john!
 */

#include <string.h>

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

#if ARCH_LITTLE_ENDIAN
#define MD5_out MD5_out_eq
#else
#define MD5_out MD5_bitswapped_out_eq
#endif


#define FORMAT_LABEL            "osc-md5"
#define FORMAT_NAME            "osc md5"
#define ALGORITHM_NAME            "osc-md5"

#define BENCHMARK_COMMENT        ""
#define BENCHMARK_LENGTH        -1

#define PLAINTEXT_LENGTH        32

#define BINARY_SIZE            16
#define SALT_SIZE            0

#define MIN_KEYS_PER_CRYPT        1
#define MAX_KEYS_PER_CRYPT        64

#define MD5_BINARY_SIZE        16
#define MD5_HEX_SIZE        (MD5_BINARY_SIZE * 2)

#define CIPHERTEXT_LENGTH    (1 + 3 + 1 + 2 + 1 + MD5_HEX_SIZE)


// Our 'salt' - actually just 2 characters we know that
// are the password prefix.
static char OSCSalt[2+1];

extern ARCH_WORD_32 MD5_out[MAX_KEYS_PER_CRYPT];
extern char MD5_tmp[MAX_KEYS_PER_CRYPT][MD5_HEX_SIZE + 1];

typedef unsigned int MD5_u32plus;
static char saved_key[MAX_KEYS_PER_CRYPT][PLAINTEXT_LENGTH + 1 + 128 /* MD5 scratch space */];
int saved_key_len[MAX_KEYS_PER_CRYPT];
// Validate a line - straightforw
extern void MD5_Go_eq(unsigned char *data, unsigned int len, int index);
extern void MD5_Go2_eq(unsigned char *data, unsigned int len, int index);

// This struct primes up the fmt variable as an extern in john.c line 40
// the password in this case is 03ericsson - 03 provided, ericsson guessed AND
// a2justin - a2 supplied, justin is decode
// {"$OSC$a2$14f194bdf3a1c50d61e3427d0f77544c","justin"},
        
static struct fmt_tests oscmd5_tests[] = {
    {"$OSC$03$066ab82156b450503861270cf0c0aa68", "ericsson"},
    {"$OSC$a2$14f194bdf3a1c50d61e3427d0f77544c","justin"},
    {NULL}
};
// No set up goes on here, just a check
static int oscmd5_valid(char *ciphertext)
{
    //  0123456789012345678901234567890123456789
    //  $OSC$03$066ab82156b450503861270cf0c0aa68
    //  printf("%s %s%s","We are in OSC. Ciphertext is:",ciphertext,"\n");
    if (!ciphertext)
        return 0;

        if (strncmp(ciphertext, "$OSC$", 5) != 0) {
                printf("%s %s %s","Initial 5 characters of ciphertext line was not $OSC$ but rather\n",ciphertext,"\n");
                //return 0;
        }

    if (strlen(ciphertext) != CIPHERTEXT_LENGTH) {
            printf ("%s %u %s %s", "OSC ciphertext length invalid\n",strlen(ciphertext),ciphertext,"\n");
            return 0;
        }

     if (ciphertext[7] != '$') {
            printf("%s","Character 7 must be a $ to split the 'salt' and the ciphertext\n");
            return 0;
        }
     if (strspn(ciphertext+8, itoa16) != MD5_HEX_SIZE) {
            printf("%s %u %s","OSC ciphertext key not valid size: must be",(unsigned int)MD5_HEX_SIZE,"\n");
                return 0;
        }

        int i;
        for (i = 8; i < CIPHERTEXT_LENGTH; i++){
        if (!(  (('0' <= ciphertext[i])&&(ciphertext[i] <= '9')) ||
                    (('a' <= ciphertext[i])&&(ciphertext[i] <= 'f'))  )) {
                    printf("%s","OSC non-hex characters found in key\n");
                    return 0;
                }
    }
        // All checks passed...continue
         printf("%s %s %s","Found valid osc-md5 password",ciphertext,"\n");
    return 1;
}

// Can use an init function. Gets called once at fmt setup time.
// Not used here - just for interst purposes.
static void oscmd5_init() {
    //printf("%s","INIT WAS CALLED\n");
}

// Split the ciphertext at position 8 to get the password, and get the "salt"
// which is actually just the first two characters of the password ie. known
// eg.
// $OSC$03$066ab82156b450503861270cf0c0aa68
// becomes 066ab82156b450503861270cf0c0aa68
// The 'salt' is stored in OSCSalt

static char* oscmd5_split(char *ciphertext,int index) {
// Get the salt out
// Move on 8 characters to pick out just the ciphertext eg. 05aa..etc
    strncpy(OSCSalt,ciphertext+5,2);
    printf("%s %s %s","OSCSalt is ",OSCSalt,"\n");
    OSCSalt[2]=0;
    ciphertext+=8;
    printf("%s %s %s","Split ciphertext in oscmd5 is",ciphertext,"\n");
    return ciphertext;
}
static void oscmd5_set_salt(void *ciphertext) {
// Nothing in here - our 'salt' is not really a salt - just 2 known characters
}

// Set the suggested key up in saved_key by prefixing with the 2 known characters
static void oscmd5_set_key(char *key, int index) {
    char x[PLAINTEXT_LENGTH];
    strnzcpy(x,OSCSalt,PLAINTEXT_LENGTH+1);
    strcat(x,key);
    strnzcpy(saved_key[index], x, PLAINTEXT_LENGTH+1);
    saved_key_len[index] = strlen(saved_key[index]);
   // printf("%s %s %s","Suggested key",saved_key[index],"\n");
}

// Get the key stored in the saved_key array eg. ericsson from 03ericsson
// Strip off the first 2 characters.
static char *oscmd5_get_key(int index) {
    saved_key[index][saved_key_len[index]] = '\0';
    // Chop off first 2 characters of saved_key to compare with the plaintext.
    return saved_key[index]+2;
}

static int oscmd5_cmp_one(void *binary, int index)
{
        return (!(*((unsigned int*)binary) - (unsigned int)MD5_out[index]));
}
// Do a binary (number) check of the ciphertext eg. 06 6a b8 ... aa 68. Quit as soon as MD5 of password and key don't match
static int oscmd5_cmp_all(void *binary, int count)
{
    unsigned int i;

    for (i = 0; i < count; i++)
    {
        if (!(*((unsigned int*)binary) - *((unsigned int*)&MD5_out[i]))) return 1;
    }
    return 0;
}
// Compare ASCII hex keys
static int oscmd5_cmp_exact(char *source, int index)
{
    // Creates a hash and compares to key
    MD5_Go2_eq((unsigned char *)saved_key[index], saved_key_len[index], index);
    // Do a straight byte for byte memory compare of the ascii hex variable to test, vs the MD5 calced one
    printf("%s %s %s %s","Comparing ", source, MD5_tmp[index],"\n");
    return !memcmp(source,MD5_tmp[index], MD5_HEX_SIZE);
}

static void oscmd5_crypt_all(int count)
{
    unsigned int i;

    for (i = 0; i < count; i++)
    {
              MD5_Go_eq((unsigned char *)saved_key[i], saved_key_len[i], i);
        }
}

int oscmd5_binary_hash_0(void *binary)
{
    return *(ARCH_WORD_32 *)binary & 0xF;
}

int oscmd5_binary_hash_1(void *binary)
{
    return *(ARCH_WORD_32 *)binary & 0xFF;
}

int oscmd5_binary_hash_2(void *binary)
{
    return *(ARCH_WORD_32 *)binary & 0xFFF;
}

int oscmd5_get_hash_0(int index)
{
        return MD5_out[index] & 0xF;
}

int oscmd5_get_hash_1(int index)
{
    return MD5_out[index] & 0xFF;
}

int oscmd5_get_hash_2(int index)
{
    return MD5_out[index] & 0xFFF;
}
// Convert the ASCII Hex representation to a real number
static void *oscmd5_binary(char *ciphertext)
{
    static char realcipher[BINARY_SIZE];
    int i;
    for(i=0;i<BINARY_SIZE;i++)
    {
        realcipher[i] = atoi16[ARCH_INDEX(ciphertext[i*2])]*16 + atoi16[ARCH_INDEX(ciphertext[i*2+1])];
    }
        // realcipher must be binary value of actual password ie. 8 char after start.
    return (void *)realcipher;
}

// Define our format - identifying the 'override' functions we will use 
struct fmt_main fmt_OSC =
{
    {
        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,
        oscmd5_tests
    }, {
        fmt_default_init,
        oscmd5_valid,
        oscmd5_split,
        oscmd5_binary,
        oscmd5_set_salt,
        {
            oscmd5_binary_hash_0,
            oscmd5_binary_hash_1,
            oscmd5_binary_hash_2
                },
        fmt_default_salt_hash,
        oscmd5_set_salt,
        oscmd5_set_key,
        oscmd5_get_key,
        fmt_default_clear_keys,
        oscmd5_crypt_all,
        {
            oscmd5_get_hash_0,
            oscmd5_get_hash_1,
            oscmd5_get_hash_2
                },
        oscmd5_cmp_all,
        oscmd5_cmp_one,
        oscmd5_cmp_exact
    }
};


// END OF CODE






> From: p2409@...mail.com
> To: john-users@...ts.openwall.com
> Date: Tue, 10 Feb 2009 00:03:10 +0000
> Subject: RE: [john-users] 2 known letters + wordlist word --> is new format definition best way?
> 
> 
> 
> 
> > Date: Mon, 9 Feb 2009 23:32:53 +0300
> > From: solar@...nwall.com
> > To: john-users@...ts.openwall.com
> > Subject: Re: [john-users] 2 known letters + wordlist word --> is new format definition best way?
> > 
> > On Mon, Feb 09, 2009 at 07:20:23AM +0000, P PO1434 wrote:
> > > I have a question regarding a long list of partially known passwords that use a simple MD5 hash. In my case, I know the first 2 characters (digits), however I would like john to use the wordlist for the remaining characters.
> > 
> > Are those two known characters the same for all partially known passwords
> > on your list or do they differ per password?
> 
> No they differ - http://www.openwall.com/lists/john-users/2009/01/21/5 is exactly the idea ie.
> a format module.
> 
> I'm trying to write the fmt.c now, and will post it if it works . (It ain't right now). 
> Thanks for the reply.
> 
> 
> 
> _________________________________________________________________
> Want to marry your mail? Combine your email accounts here!
> http://livelife.ninemsn.com.au/article.aspx?id=633386

_________________________________________________________________
Get the most out of your life online! Click here for the latest news and tips.
http://livelife.ninemsn.com.au/

Powered by blists - more mailing lists

Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.