01.04
Not too long ago we dedicated a blog post to removing executable password protections. In that post we said that we will eventually return to this topic to deal with much harder opponent. Well today is that day. This time we take a look at executable password protection named PEPasswordEncryptor
As we have seen in our previous blog on this subject tools that provide this kind of protection are very often coded with major design flaws which enable us with quick and painless ways to work around the password protection. However today's password protection option doesn't have such flaws. And that is why we need to find an optimal way to quickly and accurately recover the password. Can it be done in this case?
Quick analysis of the protected file shows us these interesting pieces of code:
PUSH ECX PUSH ECX PUSH 40 PUSH 0D PUSH DWORD PTR SS:[EBP+20] CALL NEAR DWORD PTR DS:[EDI+402923] POP ESI CALL L025 ;Calculate the hash for input string CMP EAX,F492C2C1 ;Correct password hash MOV EAX,0 JNZ L015 ... L025: ;Slow hashing algorithm XOR EAX,EAX ;Hash initialization PUSH ECX PUSH ESI ;ESI holds the password pointer PUSH EDX XOR EDX,EDX ;Reason why it executes 0xFFFF times DEC ESI ;for every letter L031: INC ESI XOR AH,BYTE PTR DS:[ESI] L033: XOR AL,DL ADD EAX,434F4445 ;Constant MOV CL,AL ROR EAX,CL XOR EAX,55AA5A5A ;Constant DEC DX JNZ L033 CMP BYTE PTR DS:[ESI],0 JNZ L031 POP EDX POP ESI POP ECX RET
Now just by looking at this piece of code we see that the author of the program thought of many things when it comes to protected file's security. Why? Algorithm in charge of hashing the string is really slow because it executes 0xFFFF times for every letter of the password. If it wasn't for this bruteforcingĀ this algorithm would be nice and quick. But before we go for that extreme we should always check for possible shortcuts that can enable us to skip the password necessity.
Since we already know that password hash must be 0xF492C2C1 lets see if the memory content decryption has a weakness.
PUSHAD MOV EDI,EAX MOV ESI,EDX MOV EAX,48415348 ;Hash initialization XOR AL,BYTE PTR DS:[ESI] CALL GetPasswordHash MOV EBX,EAX XOR AH,BYTE PTR DS:[ESI] CALL GetPasswordHash SHR ECX,2 MOV EDX,ECX L011: ;Decrypt first section XOR DWORD PTR DS:[EDI],EAX MOV CL,AL ADD EDI,4 ROL EBX,CL XOR EAX,EBX ;Keys for decryption: EAX and EBX MOV CL,BH ROR EAX,CL ADD EBX,EAX DEC EDX JNZ L011 POPAD RET
Here is what happens here. Just before the program decrypts the first section it initializes two 32bit decryption keys. Both keys are initialized from the password string, more accurately the keys are password hashes. Second key is calculated fist. This key which is stored in EBX register is a direct product of password hash for algorithm initialize value 0x48415348. However this value isn't a constant, it is modified by XORing with the fist letter of the password. That is why the already calculatedĀ password hash we have 0xF492C2C1 isn't enough to break this algorithm. Fist key is calculated last and its value is stored in EAX register. Hashing algorithm for this key calculation is a direct result of first key stored in EBX XORed with the fist letter of the password. Only by having the state of both keys for decryption beginning we can correctly decrypt the file. However we put it the password seems like a must for decryption. We are going to return to shortcuts a bit later, lets discuss the bruteforce option first.
Building a bruteforcer for this algorithm is quite easy. We just need to go through the all possible combinations that a password can take in order to recover the 'lost' password. However that proves to be a much harder task then it sounds. Building the bruteforcer isn't a problem but the time its needed for recovering a simple password which is only 4 characters in length is very long. Take a look at this program log to see a basic log which shows the "slowness" of this algorithm. It seems that author of the program saw bruteforce as a potential risk and made it slow on purpose. But this isn't the only problem. Our bruteforce test shows that passwords collide meaning the multiple passwords have the same hash. Any of these passwords will unlock the program but it won't be decrypted correctly and therefore it will crash. Here is how the bruteforce program log looks like:
Correct password for this sample is: "ap0x" but to recover it bruteforcer needed approximately 4 minutes. And as you can see in log file time needed to go through every four character password combination (a..z + A..Z + 0..9) is more then 2 hours. And even that isn't a guarantee because you have check every password since the protection doesn't have an additional validation to check if the code decrypted correctly. Since that is way too slow for any practical use on longer passwords we return to finding the algorithm weakness.
But is there a weakness? If we look at the decryption algorithm we see that both EAX and EBX keys are needed for decryption to work correctly. However only one key, EAX, is used to decrypt data by XORing the file memory content. Therefore recovering the password would be easy if we knew the first four bytes unencrypted. Which we don't... Could it help if we need the unencrypted value of any random sequence of bytes in the file? It might but that would mean that we would also need one more information for that location, the value of ECX register because it is used to modify successive decryption keys. Is there just such a sequence of bytes? Sure, at the end of file whose sections are aligned to PE.FileAlignment (and its a must for this protection) we have at least 12 bytes which are zeroes. Last four bytes in that case are equal to EAX decryption key at that time, which leaves EBX and CL as unknowns. Four bytes before that we have the same story. In order to recover EAX, EBX and CL we must do the following:
- Reverse the decryption algorithm so it decrypts the memory backwards
- Bruteforce that decryption to recover EAX, EBX and CL values
- Make sure that those key values are correct since algorithm does collide
- Decrypt the file backwards using the correct keys
Since keys are 32bit and we already know the value of one of those at one point we can bruteforce keys between 0x00000000 and 0xFFFFFFFF in order to get the missing EBX and CL values. That is much faster then trying to bruteforce the infinity of possible passwords. Reversed algorithm can be seen in the source code under the function named PEPDecryptFile.
Writing a bruteforcer for PEPasswordEncryptor is a nice reversing exercise especially when algorithm shortcut inspection is involved. As always binary, source code and the samples are included with the blog. Until next week...
PEBrute
(package contains unpacker binary, source and samples used)

[...] This post was mentioned on Twitter by Keivan Komeilipour, ap0x. ap0x said: Removing executable password protections: Attacking the cipher http://bit.ly/6oFiEo [...]