“Last year I had to face a ransomware infection in a Spanish company… shared by twitter account @malwrhunterteam, who identified it as the “Whiterose Ransomware” and mentioned that it might be decryptable…
The attackers hardcoded a public RSA key in the ransomware source code, and they were ciphering the AES password with that RSA key so only they can decrypt it. However, I found that they were using an insecure Random function to calculate this password...”
Ransomware is a type of malware that threatens to publish the victim’s data or perpetually block access to it unless a ransom is paid. In the last two years, this kind of cyberattack has notably become more popular as it’s a very effective threat that doesn’t require much knowledge to code or deploy. Furthermore, it’s really easy to access different Proof of Concepts (PoCs) readily available on the internet, sometimes for educational purposes, or Ransomware as a Service platforms (RaaS). Subscribe to get access to a platform that allows them to build new compilations of a ransomware and manage all the victims’ cryptocurrency transfers or files for a certain fee or subscription taxes. An example of that model is Gandcrab, whose actors recently announced they were stopping the service after developers earned more than 150 million dollars per year.
Luckily for us, ransomware developers are not always as professional as they wish and sometimes, they make mistakes that allow us to recover the kidnapped files without having to pay the ransom. That’s exactly what happened with a ransomware called Whiterose.
Whiterose ransomware was discovered by the Twitter account @malwrhunterteam on April 3rd 2018 and was found to be distributed via hacked Remote Desktop (RDP) connections focusing on attacking Spanish targets.
The SHA-1 hash of the sample shared was 0d642ea85680b932e6dd45620c9c12d1060b46fd. It is a Portable Executable (PE) compiled for 32 bit Windows systems and created with the .NET framework. Usually, it’s really easy to access to the original code of binaries compiled in .NET by using .NET decompilers and, because of that, there are some obfuscation tools that can protect that code to avoid inspection via decompilation..
In this case, the binary was protected with a software called Confuser. However, even though this software protector was used, it’s still possible to recover the original code. In order to get the original code , you just need to place a breakpoint in the gchandle.Free() instruction and after running the sample, dump the new loaded module from memory to disk.
Confuser protection and original ransomware code extraction
Once unpacked, the source code structure looks like other families like InfiniteTear, BlackRuby or Zenis, reinforcing the theory of public code sharing in this type of malware. Among other classes, there’s one called Configuration which stores the criminal’s RSA public key to cipher the calculated per-victim encryption key, the extension added to the ciphered files, and the text for the ransom note.
The first action performed by the malicious code is calculated using the 48 character key for ciphering all the system files.
Function to generate “random” strings
During the ciphering routine, the function is called again, but this time, to generate a 16 character string to rename the file, keeping the original name inside the ciphered content and appending it at the end of the content of the file to be able to restore it after decryption.
Line of code that calculates the new filename
By looking at the method RandomString, it’s easy to see that they are using the .NET Random() API. This function is actually a PseudoRandom Number Generator (PRNG), which implements an algorithm that produces pseudorandom numbers based on three key concepts:
- Seed: initial value that determines the initial state for the PRNG.
- State: internal properties of the PRNG. If you know the state you can determine previous and following values for the algorithm.
- Period: Number of outputs before the algorithm starts to repeat the same numbers.
It’s easy to deduce that two PRNG with the same internal state will produce the same numbers sequence.
Simple Python program that illustrate how two PRNG with the same seed, produce the same output.
Checking the .NET Random() documentation, we found that if this method is called without any arguments, it uses the system Tickcount as seed. According to MSDN, this value represents the seconds elapsed since the system was turned on. The range goes from Int32.MinValue (a negative number) to Int32.MaxValue each 49.8 days. However, we have to take into account that the Random() function will only pick the absolute value of this number.
Our problem consists of finding a secret value generated by a PRNG (the ransomware key). The long way to run the attack could be trying all different integer values from 0 to Int32.MaxValue for the Random seed and trying to decrypt the file with the generated key. Due to the fact we’re able to see the output of the function when a filename is calculated, we can already see the entire number sequence produced by the algorithm in that moment. This makes it easier to perform a brute force attack to get the tick count. This problem is explained in an Insomnia Sec document too.
If we’re able to find the first ciphered file in the system and the Tickcount used to generate its filename, the one used to generate the AES key, will be a value close to that one.
Untwister is a tool developed by Bishop Fox in 2014 which allows you to guess by brute force the seed value that generated a certain sequence of numbers. To use it, you just need to indicate which PRNG was used, the range of possible outputs (the length of the charset), and the sequence of numbers so, we should have to convert each one of the 16 characters to it’s index inside the range of possible characters.
Ilustración 5. Charset used in the RandomString function and its index
If we take, for example a file called j6GEP2bOoAyQyXeK_ENCRYPTED_BY.WHITEROSE we should have to do the following conversion:
Now, we can use the Untwister tool with these parameters, getting the tickcount for the moment when the filename was calculated: 679296.
- -r dotnet-systemrandom: the PRNG algorithm
- -I tokenDecoded.txt: text file with the list of numbers (one line per number)
- -m 0: the lowest number that could be generated
- -M 63: the limit for the highest number could be generated
- -d 16: the length of the sequence of numbers
Running the Untwister.tool
Once we have this tickcount value, we know the AES key should use a value close to that one. Therefore, we just need to start trying values by adding and decreasing an integer counter to the value, generate a 48 characters key with them using the same function and seed, try to decrypt the file and, to know when we’ve found the correct key, we have to check the original filename will appear at the end of the file.
Modifying the AES function to decrypt instead of encrypt.
The brute force algorithm could be translated into the following .NET code:
After only just 1 second, we found the key used to cipher that system’s files:
The only thing needed is a tool similar to the one that the criminals send you after paying for the key, that walks through all the ciphered files and decrypts them.
Due to a small coding mistake that the ransomware developers didn’t think about, it has been possible to guess the 48 characters AES key used to cipher files within seconds, without having to pay and support the cybercriminals activity. Even if this wasn’t a massive worldwide threat, this brute force tool helped some Spanish and Portuguese businesses, saving them money and successfully recovering their files.