During our malware analysis, we often come across samples that contain (custom) hashes in stead of cleartext. Hashing is done in an effort to bypass detection and hinder malware analysts. There are tools to recover cleartext from known hashing methods (like John the Ripper and hashcat). But for custom hashing methods, you’ll have to write some code. In this blog post, we illustrate a method to recover the custom hashes of a non-croptygraphic hashing method used by malware to obfuscate its behavior.
The Petya/Notpetya malware contains code to check which processes are running on a victim machine, and change its behavior accordingly. Microsoft has explained this in detail, with the custom hashing function and hash values to identify processes, however without reporting the process names the malware looks for.
At NVISO, we like to share knowledge and this blog post is no different. We will explain you how we recovered the process names, so that you can use this method in your own malware analysis endeavors.
The names of the processes are not hardcoded in the malware code, but a custom hash function is used to identify the processes of interest:
The custom hash function clears bits in variable v10 if process names that match custom hash values 0x2E214B44, 0x6403527E and 0x651B3005 are found.
A (cryptographic) hash function is not reversible, so we will need to figure out another way to match these hashes with actual process names. Cracking hashes is done with dictionary and brute force attacks: a trial and error method where the hash function is used with input values from the dictionary/brute-force generator until a matching hash is found. Remark that this custom hash function is not a cryptographic hash function and that the hash value is 4 bytes long, so many collisions will be found when brute forcing.
We suspected that the process names of interest are security related: antivirus software, firewalls, … Meterpreter has a killav function that contains a list of process names of security software. We used this list in our dictionary attack.
Next step is to write a program to perform the attack. Because execution speed could be crucial, we wrote it in C. We took the decompiled custom hash function source code and modified it a bit to be compiled with Visual Studio:
void ConfigCheckProcesses(_TCHAR* processname)
unsigned int v0; // ebx@3
unsigned int v1; // kr00_4@3
unsigned int v2; // edx@4
unsigned int v3; // esi@5
char *v4; // ecx@6
char v5; // al@6
int v9; // [esp+230h] [ebp-8h]@3
int v10; // [esp+234h] [ebp-4h]@1
v10 = -1;
v9 = 0x12345678;
v0 = 0;
v1 = _tcslen(processname);
v2 = 0;
v3 = v0;
v4 = (char *)&v9 + (v3 & 3);
v5 = (*v4 ^ LOBYTE(processname[v2++])) – 1;
*v4 = v5;
} while (v2 < v1);
} while (v0 < 3);
if (v9 == 0x2E214B44)
_tprintf_s(TEXT("0x2E214B44: %s\n"), processname);
else if (v9 == 0x6403527E)
_tprintf_s(TEXT("0x6403527E: %s\n"), processname);
else if (v9 == 0x651B3005)
_tprintf_s(TEXT("0x651B3005: %s\n"), processname);
While performing a dictionary attack with our customized hash function and Meterpreter’s list as dictionary, we recovered one hash: 0x2E214B44 is avp.exe (Kaspersky). When we searched Twitter for avp.exe, we found one interesting tweet. We looked at this person other tweets to see if they had recovered the other hashes, unfortunately they did not.
So the next step was to perform a brute-force attack. We generated process names with characters a-z, A-Z (the custom hash function is case sensitive), 0-9 and . – _ and assumed the extension would be .exe.
Here is the result:
Hash 0x651B3005 is NS.exe (Norton Security). That left us with hash 0x6403527E to recover. Since these two hashes are used to clear the same bit, we assumed that the processes might somehow be related. Googling for process names related to NS.exe, we came upon different process names for Norton and Symantec software. A short dictionary attack with these names revealed that 0x6403527E is ccSvcHst.exe (Symantec).
When you have a decompiler available to recover the source code of custom hash functions, one can quickly transform that source code in a working program to perform dictionary attacks and (small) brute force attacks. Larger brute force attacks will require more complex code to speed up the recovery process: multi-threaded code or code that uses GPUs.