An Analysis of Modified VeraCrypt binaries (Part 1)

On January 29, 2020, The Twitter account of VeraCrypt (@VeraCrypt_IDRIX) posted a tweet about a fake VeraCrypt website (httx://vera-crypt[.]com) that was distributing modified VeraCrypt installers that are signed with a valid EV code signing certificate from an unknown company. I was intrigued. The fake website was still up, so I decided to look into it. Here is a write-up of my analysis to try to understand what the modified binaries do, how are they obfuscated, who the authors could be, and what’s the motivation.

A phishing website?

The fake website looked identical to the official VeraCrypt website, but the links on the pages didn’t work too well. In particular, the download page was only serving the installer binaries for Windows and Mac, and the portable version for Windows. The rest led to a 404 error page. Interestingly, the Mac binary was actually genuine, only Windows binaries were modified.

Assuming the binaries are malicious in some way (which we will know for sure very soon), a successful attack would first require a victim to be lured into visiting the fake website. In fact, someone abused Google Ads to promote the fake website when searching for “veracrypt” on Google. A contact at ESET further told me that the ad was most likely targeted at Canada only.

Modified VeraCrypt installer

In this article, I will focus on the modified installer for Windows, as it’s probably the most common format people download. It is signed by a certificate issued to Calmic Software Ltd, a UK software development company.

Signature of the fake VeraCrypt installer. SHA-256: 9ebad58d714acb30422394bf8473f98dbc94446fc6918287cf5c4dd11324de3b

The fake installer version info matches that of the official VeraCrypt v1.23 from September 2018.

Fake VeraCrypt’s version info

In terms of size, the fake installer is slightly smaller than the official one (35,821,320 vs. 35,837,752 bytes, respectively). This does not necessarily mean something was “removed”. For instance, the difference in the signature certificates could easily account for the difference. But this may hint at a slight modification of VeraCrypt, maybe a backdoor?

I compared both binaries using Beyond Compare, which looks at each byte from the two files and tries to account for misalignment due to inserted/removed data. That’s how I started when I studied the official builds of TrueCrypt against their source codes. On the right image here, you can visualize in red the sections that are dissimilar. The white block that fills the first quarter is a section where both files fully match. So, this is not a totally different binary. Rather, there is still a taste of VeraCrypt here.

TrueCrypt/VeraCrypt’s installer is actually built as follows: the files to extract/install on a system are compressed and packaged at the end of the installer’s binary file. The logic of the installer is located at the beginning, which is roughly the part that is identical between the official and fake installers. The non-matching part therefore seems to correspond mostly to the compressed payload.

Roughly same installers

Firing IDA Pro and BinDiff, I was able to identify mismatching functions inside the installer. Most functions matched, except for few ones.

BinDiff output showing one main function difference and maybe a few more in the installer

The biggest difference is in sub_4236F0 (real) / sub_421540 (fake). The real function is populated with 100+ lines of decompiled code, while the fake installer’s version simply consists of “return 1;“.

These functions are called from the main function as follows.

Call to the function that’s modified in the fake installer

The string “DIST_PACKAGE_CORRUPTED” led me to a line in VeraCrypt source code in Setup.c that gives me the name of this function: VerifyModuleSignature.

VerifyModuleSignature is an addition in VeraCrypt compared to TrueCrypt, which verifies that the signing certificate used to sign the binary is the genuine VeraCrypt’s certificate, by comparing its hash against a hardcoded value. That should have pissed the malware author, who went the extra mile to re-sign the modified installer with a valid EV code signing certificate, and could not run the installer without modification 🙂

Next, the functions write_string, write_string_0 and other statically-linked libraries seem to be functionally the same, but technically a slightly different. They are most likely fine. The differences could be explained by the mismatch between the version of the compiler used for the official and fake builds.

Finally, sub_41ABF0 / sub_41AC50 is a VeraCrypt function responsible for checking whether the system boots with EFI/GPT or not. I was able to identify the function thanks to hardcoded error messages pointing to GetSystemDriveConfiguration. Due to code inlining, the source code looks much more concise than the actual generated code. That makes the identification of differences more difficult. Nevertheless, I was able to understand the few small differences I found.

One difference resides in the code generated for .str(). Again, this has to do with compiler versions. Another one also probably has to do with a library, but I was unable to confirm. The location of this difference, in GetSystemDriveConfiguration (a low interest function), probably indicates this is just an artifact of the compilation rather than a motivated change. The difference in the decompiled code is shown below.

Official (left) and fake (right) installer’s main difference in GetSystemDriveConfiguration

So, essentially, the installer code is the same as the official one, minus the signature certificate check.

Different extracted files

Now, let’s run the installer to extract its files. I often use Sandboxie to run those kind of unknown executables, but there’s always a risk it grabs real info from my system when it runs and sends it away. So in this case, I prefer to run it inside a virtual machine, on a fresh install of Windows.

Let’s extract VeraCrypt files from the fake installer

Next, we continue to compare the extracted files against the official VeraCrypt v1.23 files. However, most of them are different… This could be explained by a different way of compiling VeraCrypt, in which case I might need to figure out which version of the compiler and environment settings were used. That could be a pretty painful process.

Comparing the official (left) with the fake (right) extracted files (v1.23). Ignore the timestamps. Red files means they differ, black files mean they are the same.

Before I start the endeavor of recompiling VeraCrypt with different configurations, how about we check the extracted files’ version info again? Good hunch, the versions of the extracted files do not match. While the fake installer corresponds to v1.23, the extracted files are actually from v1.23-Hotfix2. Go figure who repackaged this with the wrong installer…

Official veracrypt.inf v1.23 (left), fake veracrypt.inf v1.23-Hotfix2 (right)

OK, let’s compare with VeraCrypt v1.23-Hotfix2 extracted files then:

Comparing the official (left) with the fake (right) extracted files (v1.23-Hotfix2). Ignore the timestamps.

Now, only VeraCrypt.exe and VeraCrypt-x64.exe differ. We are getting closer…

Modified VeraCrypt[-x64].exe

Firing IDA Pro and BinDiff, I was able to identify mismatching functions inside VeraCrypt-x64.exe. Most functions matched, except for few ones.
Note: I did the same exercise with VeraCrypt.exe and found similar results, so I’ll skip the analysis here.

BinDiff output showing at least three to four dissimilar functions

Let’s start with the most different function, with a similarity score of 0.00: sub_140001900 in the fake installer.

What you see in this function is something you do not want to see in an application like VeraCrypt: it wants to connect to a server. Note the first condition on the result of sub_140001780, which is already identified with BinDiff as another mismatching function (second-to-last in the list).

Parts of sub_140001900

The result of sub_140001780 is simply the result of calling InternetCrackUrlA on the argument to the function, which basically splits parts of the URL (yes, it’s a legitimate Windows function, despite the name). So this condition will always work if the URL is good.

sub_140001780

So what’s the URL? The function sub_140001900 is called by sub_140001E00, the third-to-last function identified by BinDiff. And here is your URL passed as argument!

Parts of sub_140001E00

Let’s rename the functions with the knowledge we gained so far.
sub_140001900 is basically in charge of fetching a URL, let’s call it download_file.
sub_140001780 parses a URL, it’ll be called crack_url.

Now let’s dig further into sub_140001E00 to understand what it does with the downloaded file. From my understanding, it simply is a PE loader: it makes sure the file is a Windows binary (checks for MZ and PE signatures), copy the content to a newly allocated memory region, parses the file’s import table to load the required DLLs into memory and provide their addresses, then it passes control to the file’s entry point.

Verifying the file is a valid Windows executable (annotations are mine) in sub_140001E00

We will therefore rename sub_140001E00 to download_and_run_dll. In turn, this function is called by a StartAddress function.

StartAddress

StartAddress is called from the main (wWinMain) as a new thread, which keeps running thanks to the infinite loop. Note the addition of StartAddress compared to the official VeraCrypt’s main function.

Modified and original wWinMain functions in TrueCrypt-x64.exe

What’s in getdll.php?

At this point, it is clear that the modified VeraCrypt’s main binary has been added with a downloader that fetches a remote payload hosted at 188.225.35.8.

The DLL returned from /getdll.php is a pretty verbose piece of code that further fetches other payloads and places them in a folder in %AppData%, named after some unique identifier returned from a request to /id.php.

To fetch the payloads, the “getdll” DLL comes with its own small HTTP client.

HTTP client in the DLL returned from getdll.php

It proceeds to do a POST request to various URLs while sending the data “geo”.

Fetching further payloads and writing them to disk

Eventually, the payloads will look like this on disk:

Dropped payloads in %AppData%
Dropped payloads in %AppData%\[ID]

The mapping between URLs and filenames is as follows:

  • 188.225.35.8/work/?work <-> [ID]\[ID].exe
  • 188.225.35.8/work/?code <-> [ID]\big_log
  • 188.225.35.8/work/?data=[ID] <-> [ID]\data
  • 188.225.35.8/work/?service <-> [ID]\pulse
  • 188.225.35.8/work/?check <-> [md5(username)].dll

pulse is encrypted when written to disk, by simply XORing the content with the [ID].

sub_180001E50 in getdll DLL

Similarly, the file proxy.txt contains the string “veracrypto.com” XORed with the [ID], and the file mask.txt contains the encrypted string “%d_yq_%02u.%02u.%02u”. data is also encrypted the same way, this time by the server (recall the ?data=[ID] argument). Finally, so that it knows which unique ID was picked, the ID is kindly written in the id.txt file in %AppData%. If the file is present, getdll will not fetch again these DLLs.

In the next parts of this write-up, I will cover what’s included in those multiple payloads, and how I emulated the malicious server as it stopped serving the malicious payloads due to the complaint addressed to its hosting provider. Stay tuned!

Update: Part 2 deals with plenty of obfuscation and anti-analysis techniques in the payloads.

2 thoughts on “An Analysis of Modified VeraCrypt binaries (Part 1)

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: