Note: All hashes and passwords are redacted.
Previously ... (link)
In January 2019, the installer files for IDA 7.2 were leaked. This does not mean it was usable however, as you need an installer password to install and a licence file to activate it. Separately to that, a license file from ESET was leaked, which didn't match the feature set of the installer file.
But all the leaks didn't matter, because without the installer password, the program files were safe. Until now. :)
On 2019-06-21, devcore published a blog post about obvious flaws in the MacOS and Linux installers for IDA, including the password as plaintext in the setup file. The Windows installer, however, uses InnoSetup as installation engine.
InnoSetup encrypts the program data with the installer password and hashes it via SHA-1, prepending it with
PasswordCheckHash and eight random bytes as salt. The password being 12 alphanumeric characters long means that bruteforcing it is pretty much out of the question.
Unless you find out how the passwords were generated in the first place! Devcore found out that the passwords are simply generated with a small Perl script using
rand(). This only works for versions up to 6.8 though, and not even all installers, as qudiss noted:
I noticed Perl 5.20.0's PRNG implementation can't be used to find seeds for the other leaked passwords or to bruteforce IDA 7.0-7.2 setup passwords. I assume different algorithms/charsets/etc. were used for these?
I found that interesting and verified their findings by converting the code to Rust and do a full search for all PRNG seeds (assuming a 32-bit seed). To do that I've dug through the Perl source code to find the exact implementation of DRand48. This, converted to Rust, amounts to something like:
The rest is just a big loop over the full 32-bit range, setting the initial seed, generating the password, hashing it according to InnoSetup's scheme, and comparing that with the saved hash in the installer. Using Rayon I've quickly converted it to use multiple threads, yielding an >8x speed improvement on my system.
... and yes, as qudiss said, neither the IDA 7.0 nor the other mentioned leaked passwords can be found. So, what now?
Breaking open the 7.0 installer (link)
I guess I was pretty lucky since my first idea was actually correct. Qudiss' comment above included a very helpful link and hint: The PRNG implementation used in Perl was first introduced in Perl 5.20. So I've looked into what Perl used before DRand48 was added, thinking the passwords could have just be generated with an even older version.
And here it is:
Just a simple call to the C functions
rand, converting the random number to a double with a bit of bit-fiddling.
Re-checking the full range was a little bit uglier with the older PRNG code, since C's
rand are not thread-safe, as it uses globals to store the PRNG state. This meant that instead of using rayon multithreading, the brute-force loop has to run single-threaded. To make the program not lose all of its previous speed, chunking the search space into 16 blocks and just running the tool 16 times in parallel worked well enough.
use hex; use ; use ; use ; const CHARS: & = b"abcdefghijkmpqrstuvwxyzABCDEFGHJKLMPQRSTUVWXYZ23456789"; const HASH: = hex!; const PEPPER: & = b"PasswordCheckHash"; const SALT: = hex!;
This, surprisingly, turned out to be the right idea and successfully bruteforced the other hashes/passwords (when using Microsoft's C runtime implementation for
IDA 7.2, though (link)
The next thing I've tested was the IDA 7.2 installer, of course. InnoExtract (
innoextract.exe --show-password) is probably the easiest way of extracting the relevant data from the installer:
Inspecting "IDA Pro v7.2 and Hex-Rays Decompiler (x64)" - setup data version 5.5.7 (unicode) Password hash: SHA-1 0000000000000000000000000000000000000000 Password salt: 50617373776f7264436865636b486173680000000000000000 (hex bytes, prepended to password) Password encoding: UTF-16LE Done.
One thing that immediately stood out was the password encoding. IDA 7.2 uses the unicode variant of InnoSetup, hence the UTF16 encoding—previous installers used the ANSI variant. With a bit of unsafe slicing, the DRand48 version of the hashing code looks like this now:
let mut buf = ; let mut rand = DRand48 ; for i in pos..
I've tried both the DRand48 PRNG and the older C-based PRNG, to no avail. Giving up for the night I've shared my findings as a reply to qudiss' comment.
On the next morning a lot of comments on both devco.re and the chinese discussion board pediy.com had appeared, pointing to a few more ideas to test. The first one was pretty simple, checking if the password length was increased to 14 characters, which sadly was not the case.
The second hint was posted as a small inconspicuous reply in the comments section on the devco.re blog post, by hishe:
i think you need to omit the first rand.
this article doesn't mention this.
Yup, got it!
It probably was a well-educated guess by hishe that led to IDA 7.2 being finally pried open. Discarding the first generated number after setting the seed was everything that had to be changed to make it work (with DRand48):
for i in pos..
After waiting for somebody else to post the actual password for the installer I pushed my code for anyone interested to see: gh/find_drand48_innosetup_pw
The timing of it all was lucky as well—the weakness was reported to Hex-Rays on January 31st 2019, while the leaks happened just a few weeks earlier. Since Hex-Rays promised to harden the installer password, this will probably be the last version of IDA to be leaked/cracked in a usable state without a password.