Follow @Openwall on Twitter for new release announcements and other news
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date: Fri, 10 Jun 2016 22:06:08 +0300
From: Solar Designer <>
Subject: Re: dmg2john's dmg file header parsing

On Fri, Jun 10, 2016 at 09:53:03PM +0300, Solar Designer wrote:
> Our dmg2john.c and assume that the lengths of some dmg file
> header fields are fixed, even though the actual lengths are encoded in
> the header and are propagated into the output "hash".  I got a couple of
> dmg header v2 files where:
> kdf_salt_len = 20
> blob_enc_iv_size = 8
> encrypted_keyblob_size = 64
> However, blindly assumes blob_enc_iv_size = 32 and
> encrypted_keyblob_size = 48.  dmg2john.c appears to similarly assume the
> former, but not the latter (so it encodes 32 and 64 instead of 8 and 64).
> kdf_salt_len appears to be handled correctly by both.

Upon a second thought, maybe padding the IV to 32 bytes with zeroes, and
reporting its size in the "hash" as 32, is actually correct for our

The obvious bug is different output by dmg2john.c vs. for
encrypted_keyblob_size = 64, where .py would replace 16 bytes that are
non-zero in the dmg file with zeroes.

> I have no idea whether any such assumptions are also present in the
> actual dmg crackers.  Either way, we clearly have this bug in dmg2john*,
> where they blindly output erroneous "hashes" instead of processing the
> input files correctly or reporting an error.

Per the above, I am not so sure about dmg2john.c's handling of IV.

encrypted_keyblob_size other than 48 appears to be supported in the
cracker, as there are two checks like this:

                if (cur_salt->encrypted_keyblob_size == 48)
                        AES_set_decrypt_key(aes_key_, 128, &aes_decrypt_key);
                        AES_set_decrypt_key(aes_key_, 128 * 2, &aes_decrypt_key);

I am not sure if my specific case is supported, though.

> For, fixing this problem correctly would require changing
> the approach it uses.  Right now, it parses the entire header with:
> v1_header_fmt = '> 48s I I 48s 32s I 296s I 300s I 48s 484s'
> v1_header_size = struct.calcsize(v1_header_fmt)
> v2_header_fmt = '> 8s I I I I I I I 16s I Q Q 24s I I I I 32s I 32s I I I I I 48s'
> v2_header_size = struct.calcsize(v2_header_fmt)
> It will need to separately parse the length fields and the actual data.
> In my case, the "48s" in v2_header_fmt should become "64s", but it would
> probably be wrong to patch it in the v2_header_fmt line, because some
> other file could in fact need "48s" or something else.

Since the cracker seems to only support one other value besides 48,
maybe an acceptable workaround would be to support only 48 and 64 in, and error out on anything else.  This is a much smaller
change from the current code.

> Dhiru, will you fix this properly, please?  Thanks!


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.