Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

o3logon format error for salts with 8bit character #2243

Closed
frank-dittrich opened this issue Aug 28, 2016 · 17 comments
Closed

o3logon format error for salts with 8bit character #2243

frank-dittrich opened this issue Aug 28, 2016 · 17 comments
Assignees
Labels

Comments

@frank-dittrich
Copy link
Collaborator

A slightly modified o3logon self test:

$ echo -e '$o3logon$PASSWORD\xF9$8CF28B36E4F3D2095729CF59510003BF$3078D7DE44385654CC952A9C56E2659B' > fail_o3logon.pw
$ ./john fail_o3logon.pw
Warning: invalid UTF-8 seen reading fail_o3logon.pw
Using default input encoding: UTF-8
Loaded 1 password hash (o3logon, Oracle O3LOGON protocol [SHA1 DES 32/64])
Press 'q' or Ctrl-C to abort, almost any other key for status
ASAN:SIGSEGV
=================================================================
==19282==ERROR: AddressSanitizer: SEGV on unknown address 0x10009face319 (pc 0x000000af0c0e bp 0x7ffffd6b1c90 sp 0x7ffffd6b1440 T0)
    #0 0xaf0c0d in memcpy /usr/include/x86_64-linux-gnu/bits/string3.h:53
    #1 0xaf0c0d in crypt_all /home/ubuntuadm/git/JtR/src/o3logon_fmt_plug.c:296
    #2 0xd070c8 in crk_password_loop /home/ubuntuadm/git/JtR/src/cracker.c:790
    #3 0xd0c55f in crk_salt_loop /home/ubuntuadm/git/JtR/src/cracker.c:955
    #4 0xdf8908 in do_wordlist_crack /home/ubuntuadm/git/JtR/src/wordlist.c:1392
    #5 0xcd5567 in do_wordlist_pass /home/ubuntuadm/git/JtR/src/batch.c:38
    #6 0xcd5567 in do_batch_crack /home/ubuntuadm/git/JtR/src/batch.c:58
    #7 0x41f7b1 in john_run /home/ubuntuadm/git/JtR/src/john.c:1724
    #8 0x41f7b1 in main /home/ubuntuadm/git/JtR/src/john.c:1967
    #9 0x7fd84b3d282f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)
    #10 0x422638 in _start (/home/ubuntuadm/git/JtR/run/john+0x422638)

AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV /usr/include/x86_64-linux-gnu/bits/string3.h:53 memcpy
==19282==ABORTING
@jfoug
Copy link
Collaborator

jfoug commented Aug 31, 2016

This may not be a bug in the format. Here is the bug.

within valid, we call enc_to_utf16() which has 2 code paths (one for if we are not in utf8 mode, and one when we are). Then when we load the salt, we call enc_to_utf16_be() which does the same thing, BUT this time, jtr is IN utf8 mode, so it does utf8 decoding on the user name. What we have here, is the last byte when read in utf8 says there should be 4 trailing bytes. So the convert function nulls out the string, and returns -8. That is what causes the catastrophic error. But I think fixing the bug this way is not good. I think I need to look at when that option gets set, and make sure that it's set prior to valid getting called, looking for the proper format.

@magnumripper any ideas on this one???

@jfoug
Copy link
Collaborator

jfoug commented Aug 31, 2016

Here is the debugging code (in valid)

       memcpy(tmp, ciphertext, cp-ciphertext);
        tmp[cp-ciphertext] = 0;
        len = enc_to_utf16((UTF16 *)cur_key_mixedcase, MAX_USERNAME_LEN+1, (unsigned char*)tmp, strlen(tmp));
printf ("%x %x %s (in valid)  %d\n", options.target_enc, UTF_8, tmp, len);
        if (len < 0 || len > MAX_USERNAME_LEN)
                return 0;

In get_salt()

       salt.userlen = enc_to_utf16_be(salt.user, MAX_USERNAME_LEN, tmp, cp-ciphertext);
printf ("%x %x %s (in get_salt)\n", options.target_enc, UTF_8, tmp);
printf ("%x %x %s\n", salt.userlen, 2*salt.userlen, tmp);
        salt.userlen *= 2;
        base64_convert(cp+1,e_b64_hex,32,salt.auth_sesskey,e_b64_raw,16,0,0);
        cp = strchr(cp+1, '$') + 1;

Ok, here is the output:

0 16 PASSWORD<F9> (in valid)  9
16 16 PASSWORD<F9> (in get_salt)
fffffff8 fffffff0 PASSWORD<F9>

As you can see from the 2nd line of output, the options.target_enc went from 0 to 16 (0 in valid). Also the length of this user name was 9 in valid (not using utf8 internal code), but -8 in get_salt since the processing WAS done in utf8 mode there.

@jfoug
Copy link
Collaborator

jfoug commented Aug 31, 2016

I wonder if this code at the bottom of john_load_conf() is wrong (it is loading wrong values, I think)

    /* Pre-init in case some format's prepare() needs it */
    internal = options.internal_cp;
    target = options.target_enc;
    initUnicode(UNICODE_UNICODE);
    options.internal_cp = internal;
    options.target_enc = target;
    options.unicode_cp = CP_UNDEF;

@jfoug
Copy link
Collaborator

jfoug commented Aug 31, 2016

@magnumripper please look at this change to john.c This eliminates the problem, but I am not 100% sure it is correct. In this case, when prepare and valid are called, the target_enc is set the same as when get_salt is later called. Thus this bad hash is rendered invalid, since the user name length is -8

diff --git a/src/john.c b/src/john.c
index b8ade71..98411d4 100644
--- a/src/john.c
+++ b/src/john.c
@@ -901,7 +901,10 @@ static void john_load_conf(void)
        target = options.target_enc;
        initUnicode(UNICODE_UNICODE);
        options.internal_cp = internal;
-       options.target_enc = target;
+       if (target)
+               options.target_enc = target;
+       else
+               options.target_enc = options.input_enc;
        options.unicode_cp = CP_UNDEF;
 }

@magnumripper
Copy link
Member

It doesn't look right: The idea is to reset everything as it was since this happens before the real Unicode init. So this may upset the real init. But I'll have a look at this today.

@magnumripper magnumripper self-assigned this Aug 31, 2016
@magnumripper
Copy link
Member

magnumripper commented Aug 31, 2016

The reason for this pre-init is we have a hen-or-egg problem with options vs. config values vs defaults depending on format (eg. our Unicode defaults is different with LM from the ones with NT). It may be that the suggested fix is perfectly fine, but it might also screw up some handling of defaults in certain situations.

@jfoug
Copy link
Collaborator

jfoug commented Aug 31, 2016

That is why I tossed it out as a question.

My thinking was if the user requested encoding as an option, keep it, otherwise use the default. What is happening now, is that if no option is set on command line, then 0 is set (iso-8859-1 ??) but only for prepare and the FIRST valid (i.e. finding out which format to use). Once a format is found, I think the proper value from config is set (if nothing specified on command line).

But again, I do not know this logic well at all. BUT the way the comment reads, I really think that if the user did not specify anything, that defaults should be set at this point, and NOT keep it set to undefined (i.e. 0).

Set the way I have it, it 100% passes -test-full=0 (in asan mode), and fully passes test suite (again in asan). BUT that does not test all nuances of using -enc= command line switches against all type formats, nor any of the other complex things about this. Also with this change, the hash @frank-dittrich provided is skipped as invalid (which it is in utf8 mode)

@jfoug
Copy link
Collaborator

jfoug commented Aug 31, 2016

Ok, new question. You said some formats HAVE defaults. Is this the problem, in that we simply have not set the default encoding for o3logon ? If that is the case, then we might want to look for other formats that do unicode stuff, to make sure that they are setup properly in their prepare/valid (first call).

@jfoug
Copy link
Collaborator

jfoug commented Aug 31, 2016

Ok, within loader, at this point,

        prepared = alt->methods.prepare(fields, alt);
        if (!prepared)
            continue;
        valid = alt->methods.valid(prepared, alt);
        if (!valid)
            continue;

        if (retval < 0) {
            retval = valid;
            *ciphertext = prepared;
            ldr_set_encoding(alt);
#ifdef HAVE_OPENCL

We are calling prepare and valid without calling ldr_set_encoding. Should we instead do something like this:

        ldr_set_encoding(alt);
        prepared = alt->methods.prepare(fields, alt);
        if (!prepared)
            continue;
        valid = alt->methods.valid(prepared, alt);
        if (!valid)
            continue;

        if (retval < 0) {

@jfoug
Copy link
Collaborator

jfoug commented Aug 31, 2016

Ok, I really think we need to look at moving all of the ldr_set_encoding(fmt) to above the prepare() function. Or else, list that any unicode based decoding based upon code page, is not valid to do within prepare() or valid()

Here is an example:

echo -e 'M$test2\xF9#ab60bdb4493822b175486810ac2abe63' > fail_mscash.pw
$ ../run/john fail_mscash.pw -form=mscash
Warning: invalid UTF-8 seen reading fail_mscash.pw
saltlen=5 enc=0
=================================================================
==7694==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x631000434800 at pc 0x0000006b0d04 bp 0x7ffe072375d0 sp 0x7ffe072375c0
READ of size 2 at 0x631000434800 thread T0
    #0 0x6b0d03 in salt_hash /home/ghost/JtR/src/mscash1_fmt_plug.c:808
    #1 0x7bb3a2 in ldr_load_pw_line /home/ghost/JtR/src/loader.c:1006
    #2 0x7bc509 in read_file /home/ghost/JtR/src/loader.c:226
    #3 0x7bf69d in ldr_load_pw_file /home/ghost/JtR/src/loader.c:1115
    #4 0x7b293c in john_load /home/ghost/JtR/src/john.c:1172
    #5 0x7b417a in john_init /home/ghost/JtR/src/john.c:1499
    #6 0x7b5fc6 in main /home/ghost/JtR/src/john.c:1957
    #7 0x7f2d367c06ff in __libc_start_main (/lib64/libc.so.6+0x206ff)
    #8 0x405f38 in _start (/home/ghost/JtR/run/john+0x405f38)

0x631000434800 is located 0 bytes to the right of 65536-byte region [0x631000424800,0x631000434800)
allocated by thread T0 here:
    #0 0x7f2d3826097a in malloc (/lib64/libasan.so.2+0x9897a)
    #1 0x7d4a7e in mem_alloc_func /home/ghost/JtR/src/memory.c:88
    #2 0x7d4c60 in mem_alloc_tiny_func /home/ghost/JtR/src/memory.c:196
    #3 0x7d4eab in str_alloc_copy_func /home/ghost/JtR/src/memory.c:330
    #4 0x4d2516 in dynamic_SETUP /home/ghost/JtR/src/dynamic_fmt.c:7801
    #5 0x4d6cac in dynamic_LOAD_PARSER_FUNCTIONS /home/ghost/JtR/src/dynamic_parser.c:988
    #6 0x4b130e in LoadOneFormat /home/ghost/JtR/src/dynamic_fmt.c:7857
    #7 0x4d31c9 in dynamic_Register_formats /home/ghost/JtR/src/dynamic_fmt.c:7977
    #8 0x7ae50e in john_register_all /home/ghost/JtR/src/john.c:379
    #9 0x7b40bd in john_init /home/ghost/JtR/src/john.c:1484
    #10 0x7b5fc6 in main /home/ghost/JtR/src/john.c:1957
    #11 0x7f2d367c06ff in __libc_start_main (/lib64/libc.so.6+0x206ff)

SUMMARY: AddressSanitizer: heap-buffer-overflow /home/ghost/JtR/src/mscash1_fmt_plug.c:808 salt_hash
Shadow bytes around the buggy address:
  0x0c628007e8b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c628007e8c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c628007e8d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c628007e8e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c628007e8f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x0c628007e900:[fa]fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c628007e910: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c628007e920: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c628007e930: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c628007e940: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c628007e950: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Heap right redzone:      fb
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack partial redzone:   f4
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
==7694==ABORTING

Note, if run without the -format, it is MUCH more confusing of a run:

$ ../run/john fail_mscash.pw
Warning: invalid UTF-8 seen reading fail_mscash.pw
saltlen=-4 enc=16
mscash1: One or more hashes rejected due to salt length limitation
len=1 enc=16
Using default input encoding: UTF-8
Loaded 1 password hash (HMAC-MD5 [password is key, MD5 128/128 AVX 4x3])
Will run 3 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
0g 0:00:00:02  3/3 0g/s 149902p/s 149902c/s 149902C/s abirub..sonas13
Session aborted

Here, no crash, and we get the warning from within mscash valid function. The reason we get this, is that the format found was hmac-md5. But loader keeps looking (to output warning messages). When it hits mscash valid this time, the target_enc is set, so the length returned is -4 and valid returns 0. Even though it 'appears' that the run should be running against mscash (from the input file), it is actually running against hmac-md5 which does not have the encoding problem.

Putting ldr_set_encoding(fmt) call before the prepare call (when searching for the format) solves this issue. NOTE, I have no idea how this would cause other problems. NOTE, once we HAVE hammered down on a format, we probably should NOT call ldr_set_encoding(fmt) any more, OR we should save off the encoding, to restore it at the bottom of that loader function. But I really think we need to have this set before we call prepare()/valid()

@magnumripper
Copy link
Member

Here's one thing your first proposed fix breaks:

Before:

$ ../run/john alltests.in -form:lm
Using default input encoding: UTF-8
Using default target encoding: CP850
Loaded 104 password hashes with no different salts (LM [DES 256/256 AVX2-16])
(...)

After:

$ ../run/john alltests.in -form:lm
Using default input encoding: UTF-8
Loaded 104 password hashes with no different salts (LM [DES 256/256 AVX2-16])
(...)

Unfortunately the Unicode heuristics and calls to initUnicode() are scattered in john_load_conf(), ldr_set_encoding(), john_load_conf_db() and finally john_init() (in that order). Reason being the mentioned hen-or-egg problem with picking defaults:

  • When john_load_conf() is called, we don't yet have a format so can't decide on default target encodings. Your initial fix pegged one and blew it.
  • When ldr_set_encoding() is called, we are still evaluating formats (unless one was given on command line). It's called many times.
  • When john_load_conf_db() is called, we have settled on a format and should know all settings.
  • The last call to initUnicode(), from john_init(), is (I think) for cases like -stdout which doesn't have a format or db. It might be that this last one is redundant after 6896971 but I'm not sure and it doesn't hurt.

Ok, I really think we need to look at moving all of the ldr_set_encoding(fmt) to above the prepare() function. Or else, list that any unicode based decoding based upon code page, is not valid to do within prepare() or valid()

For performance reasons, I believe we have to do the latter, if anything. However, I would argue that this issue's test case would be a PEBCAK (please note that user _is being warned_ that invalid UTF-8 was seen!) so the fix is just to ensure we don't segfault. We'll load it but can't ever crack it, but user was warned so he's on his own. Same would happen with other formats if incorrect encodings are specified (or defaulted to).

So I propose that dc632b3 is the only thing we actually do for now.

@magnumripper
Copy link
Member

Or else, list that any unicode based decoding based upon code page, is not valid to do within prepare() or valid()

If nothing else, this should be a recommendation. But how else would o3logon be able to do the right thing (given proper -enc options) in prepare()?

@jfoug
Copy link
Collaborator

jfoug commented Aug 31, 2016

However, I would argue that this issue's test case would be a PEBCAK (please note that user is being warned that invalid UTF-8 was seen!) so the fix is just to ensure we don't segfault.

I would argue right back that your 'assumption' is false. The user is NOT being warned. The only reason the warning was issued for mscash was because another format SET the encoding! Again, run that mscash hash but you have to use the -form=mscash or else hmac-md5 format will snag the processing.

When you run that baby with the -format=mscash then that format is the one used, it does NOT warn at all (since it is using '0' target_enc type), and there is a cato crash. Same as was found by @frank-dittrich original test hash for this issue.

Again, if you expect to not change this, then by all means, we need to document that using any enc_utf16 type stuff is NOT valid in prepare() or valid() functions period. I would much rather see this 'fixed' by some means.

NOTE, this is only done spinning through loader, initially looking for a usable format. I do not think that is done for each line BEING loaded (or am I wrong in that assumption).

@jfoug jfoug reopened this Aug 31, 2016
@magnumripper
Copy link
Member

mscash? I was talking about o3logon. The warning is seen in Frank's OP. But I see now what you mean.

Anyway I DO agree we should improve this if possible. But it's definitely not a simple fix! And the slight change in o3logon improved the situation for now.

@jfoug
Copy link
Collaborator

jfoug commented Aug 31, 2016

@magnumripper this does nothing to fix the bug (it tries to hide it). Again, here is another example (there ARE others)

echo -e 'M$test2\xF9#ab60bdb4493822b175486810ac2abe63' > fail_mscash.pw
$ ../run/john fail_mscash.pw -form=mscash
Warning: invalid UTF-8 seen reading fail_mscash.pw
=================================================================
==7694==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x631000434800 at pc 0x0000006b0d04 bp 0x7ffe072375d0 sp 0x7ffe072375c0
READ of size 2 at 0x631000434800 thread T0
    #0 0x6b0d03 in salt_hash /home/ghost/JtR/src/mscash1_fmt_plug.c:808
    #1 0x7bb3a2 in ldr_load_pw_line /home/ghost/JtR/src/loader.c:1006
    #2 0x7bc509 in read_file /home/ghost/JtR/src/loader.c:226
    #3 0x7bf69d in ldr_load_pw_file /home/ghost/JtR/src/loader.c:1115
...

@magnumripper
Copy link
Member

I think we should close this issue and open another one for the "core issue" we are talking about now.

@jfoug
Copy link
Collaborator

jfoug commented Aug 31, 2016

I can agree with that. Makes sense.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants