Open exploide opened 3 years ago
Thanks, @exploide. This is a nasty issue. I added print(kdf_params)
into pem2john.py
, and here's what I see for your key above:
OrderedDict([('salt', b'\x0e\x8c:\x1c\xdd\xbcw\x90'), ('iteration_count', 2048), ('key_length', None), ('prf', OrderedDict([('algorithm', 'sha256'), ('parameters', None)]))])
Notice the sha256
there. For a key generated with older OpenSSL and crackable by current JtR, there's sha1
in that place.
We need to add encoding of this field into pem2john.py
output (perhaps upgrading it from $PEM$1
to $PEM$2
) and its decoding and reporting as a tunable cost and (most importantly) its actual usage in JtR formats pem
and pem-opencl
. As an option, it's OK to mostly exclude pem-opencl
from the initial PR, only having it refuse to load "hashes" that use other than SHA-1 in there - and fix that with a follow-up PR (possibly by a different person). In fact, it's OK for the initial PR to even be limited to introducing the new encoding and its support in the formats, while having both formats refuse to actually process other than SHA-1 - that in itself would already be an improvement and would fix the bug (we'd have explicit rejection rather than incorrect processing), leaving only the enhancement (actual support) to be worked on.
@exploide Would you like to work on any/all of this? :-)
Notice the sha256 there. For a key generated with older OpenSSL and crackable by current JtR, there's sha1 in that place.
Ok, I expected something like this. Good that it doesn't affect all PEM keys.
Would you like to work on any/all of this?
While I'm generally happy to help out, I'm not eager to do changes to the format implementations. I assume it is much less work for those of you who are already familiar with the C code base.
@exploide This is your opportunity to become familiar with the C and maybe the OpenCL code base, so that you'd be able to contribute more in the future. ;-)
Hi! Any news on this? Any way to workaround this issue manually?
@reinistihovs No, and no. You want to contribute a fix? Initially, and that's pretty easy, we need JtR to correctly refuse to process the unsupported inputs. Adding the support would then be the next task.
@solardiz I got it to work, heres my very very dirty fix: change all sha1 occurances to sha256 in: pem_fmt_plug.c and opencl_pem_fmt_plug.c
then Build, pem self test fails.
then used unchanged pem2john.py to create hash.
then used ./john --skip-self-tests test.hash
success:
Using default input encoding: UTF-8
Loaded 1 password hash (PEM, PKCS#8 private key (RSA/DSA/ECDSA) [PBKDF2-SHA256 256/256 AVX2 8x 3DES/AES])
Cost 1 (iteration count) is 4096 for all loaded hashes
Cost 2 (cipher [1=3DES 2/3/4=AES-128/192/256]) is 1 for all loaded hashes
Will run 4 OpenMP threads
Proceeding with single, rules:Single
Press 'q' or Ctrl-C to abort, almost any other key for status
Almost done: Processing the remaining buffered candidate passwords, if any.
Proceeding with wordlist:./password.lst
123456 (?)
1g 0:00:00:00 DONE 2/3 (2021-12-21 13:16) 11.11g/s 5688p/s 5688c/s 5688C/s 123456..crawford
Use the "--show" option to display all of the cracked passwords reliably
Session completed.
@reinistihovs Hey, that's interesting. I thought it'd require a bit more than that - e.g., also updating the hash size somewhere.
Have you got the OpenCL format to work as well (yes, you updated its source file, but the above doesn't test it)?
Would you submit a proper fix (that would keep things working for SHA-1 as well)?
I would be happy to help, but I am not a experienced C developer, it would take me days to solve this :) I didnt touch any other part of the code, yes the tests FAIL, thats why I used: --skip-self-tests
@reinistihovs OK, we'll take this from here. Your success so far is encouraging.
Hello, I would like to give this a go, if no one else is working on it. It's a lot of new concepts for me, I will be happy to get a break down, so I can solve some simpler problems first before moving on to more complicated ones. This can help me get familiar with the code base.
Notice the sha256 there. For a key generated with older OpenSSL and crackable by current JtR, there's sha1 in that place.
is the first step implementing a check in JtR and refuse to process if it's sha256?
is the first step implementing a check in JtR and refuse to process if it's sha256?
Yes. You can do it in two ways/steps:
Minimal initial: Have pem2john.py
report an error (and not produce a "hash") for anything other than sha1
.
Better suited for further steps: When encountering anything other than sha1
, have pem2john.py
switch to a new output encoding with this extra field, and have anything other than sha1
initially rejected by the "format" in john
proper (perhaps with a warning printed out of its valid
function once - search the tree for warned
to see some examples).
I think it makes sense to implement the minimal initial change first, and only implement the second when we're about to add the support to john
proper as well.
Now that the minimal initial is done, I would like to continue working on the next step. As I said before, a lot of new concepts for me here, apologies if it sounds too basic :)
When encountering anything other than sha1, have pem2john.py switch to a new output encoding with this extra field,
Is the encoding for output that is not sha1
already implemented? Or is implementing other encodings part of this task?
and have anything other than sha1 initially rejected by the "format" in john proper (perhaps with a warning printed out of its valid function once - search the tree for warned to see some examples)
I see a bunch of methods in pem_fmt_plug.c
and pem_common_plug.c
. Specifically, in pem_valid
, we seem to check for ciphertext length
amongst other things. Is this where the warning should be printed?
Is the encoding for output that is not
sha1
already implemented?
No, but it should be the same as for sha1
except that it'd need to indicate that it's a new one and it should have a field for prf algorithm
.
Or is implementing other encodings part of this task?
Yes.
Right now, we output strings that start with $PEM$1
. In the non-sha1 case, let's output $PEM$2
instead and follow that by the extra field, so e.g. $PEM$2$sha256
, and keep the rest of the string formatting the same.
in
pem_valid
, we seem to check forciphertext length
amongst other things. Is this where the warning should be printed?
Yes.
Is 640
the expected ciphertext length for sha1? If yes, I would love to know why.
Is
640
the expected ciphertext length for sha1? If yes, I would love to know why.
No, there isn't an "expected ciphertext length for sha1". 640 is a possible one, some others are also possible (see the test vectors we have in pem_common_plug.c
), and this isn't directly related to the prf algorithm
in use, although there might happen to be a correlation.
The recent PR said it "partially fixes" this issue, but GitHub doesn't parse the word "partially", so it closed the issue. I'll reopen for us to remember to fix the issue fully.
Now I am ready for hopefully the last part of this issue. This might be both interesting and challenging. This is what I gather after snooping around in the code a little. pem_fmt_plug.c
calls pbkdf2_sha1(...)
and there is an equivalent function pbkdf2_sha256(...)
. Maybe after parsing what prf encoding has been used, we store it in self.params
and call one of the above functions...? I am happy to get a break down.
@pradkrish Can you just try to research this on your own, without me (or someone) doing it for you? The way the two of us worked on the previous PR isn't practical long-term - it's 90% overhead and 10% progress - so can we already start to improve the work efficiency now? I understand you might still need someone else to take a look and provide guidance on specific sub-issues, but please at least try on your own first? Thank you!
As we've approached the more serious part of "the project" here, to confidently provide guidance to you I would have needed essentially to do the work, then somehow instead of simply creating a PR myself guide you through arriving at the same. That feels inefficient. And if I start giving advice without fully thinking "the project" through, we'd be going through lots of re-works like we did on the previous PR, which is also inefficient.
I agree, the previous PR got overly lengthy, not proud of it. This is a completely new and unrelated field for me, which could be one reason. I will dig around more on my own or pick some other low hanging fruits to learn more about this code base.
While working on #4833 I encountered a problem with cracking openssl encrypted private key files. It does work with the example key but not with a newly generated test key. The password is "test".