Follow @Openwall on Twitter for new release announcements and other news
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date: Fri, 1 Feb 2013 23:10:48 +0100
From: magnum <>
Subject: Re: NTLMv1 and MSCHAPv2

On 1 Feb, 2013, at 19:48 , magnum <> wrote:
> On 1 Feb, 2013, at 18:22 , Solar Designer <> wrote:
>> Here's a concern about these optimizations, though: they slow down the
>> loading, which may be nasty if someone is cracking a very large number
>> of C/R pairs at once.  I think the 32k DES encryptions with OpenSSL's
>> code on one CPU core may be taking about 10 ms, which means a loading
>> speed of 100 C/R pairs per second.  With 1 million to load, that's 3 hours.
>> Is it realistic that someone will have this many?
> And this is twice worse now, after I implemented the block 3 DES check in valid(). I should save the result from that test, and re-use it in binary() after a sanity check. I think I'll do this asap.

Fixed. With a fall-back, just to be sure.

> BTW when I implemented that, I was wondering if we could not add a late-reject for these cases: If binary() returns NULL we got a late reject. Would it be too late to efficiently handle that in loader?

Only after fixing the above, I went on with this. This trivial patch works like a charm:

diff --git a/src/loader.c b/src/loader.c
index 7b25ed3..39a9d41 100644
--- a/src/loader.c
+++ b/src/loader.c
@@ -532,7 +532,9 @@ static void ldr_load_pw_line(struct db_main *db, char *line)
        for (index = 0; index < count; index++) {
                piece = format->methods.split(ciphertext, index, format);
-               binary = format->methods.binary(piece);
+               if (!(binary = format->methods.binary(piece)))
+                       return; /* Late reject */
                pw_hash = db->password_hash_func(binary);
                if (!(db->options->flags & DB_WORDS) && !skip_dupe_checking) {
@@ -676,7 +678,8 @@ static void ldr_load_pot_line(struct db_main *db, char *line)
        if (format->methods.valid(ciphertext, format) != 1) return;
        ciphertext = format->methods.split(ciphertext, 0, format);
-       binary = format->methods.binary(ciphertext);
+       if (!(binary = format->methods.binary(ciphertext)))
+               return; /* Late reject */
        hash = db->password_hash_func(binary);
        if ((current = db->password_hash[hash]))

The former is no different to a dupe rejection (except it could happen with the very first hash), and the latter is just the same as a rejection from the valid() included in the patch context. I have tested it with a single late rejected hash as well as some rejected among others that are valid. And I have grepped around for possible caveats (a self-test for binary returning NULL is added too but this is just to help understanding problems).

Solar, are you OK with this? I will add it to bleeding and start actively using it. I know we have formats that detect late rejects in binary() but can't do anything about it other than make it safe (an uncrackable hash is added) or abort (not a good solution). This can arguably always be fixed in valid() but sometimes this is really impractical or expensive.

I will make use of it for bleeding's NTLMv1/MSCHAPv2 just for getting it field tested while we have a lot of time.


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.