Zeus/Zbot is an annoying threat. Its persistence is explained with a fact that it’s generated by a large army of attackers who use Zeus builder.
Those attackers who are high in the food chain pay thousands of dollars for the latest Zeus builder to make sure they distribute the most up-to-date undetectable bot builds. But many are still happy to use obsolete versions of the builder – these are available for free on various file sharing web sites.
One way or another, the wave if new Zeus/Zbot samples being distributed every day is alarming. It’s kind of an “attack of the clones” when multiple modifications of the bot are being produced in-the-wild, packed and encrypted on top with all sorts of packers, including modified, hacked, or private packer builds. Before being released, every newly generated and protected bot is uploaded into popular multi-AV scanner services to make sure it is not detected by any antivirus vendor. Hence, quite a bit of a problem in terms of its distribution scale.
The nasty thing about Zeus/Zbot is that it evolves. The latest generation bot uses rootkit techniques to hide its presence on a customer machine. The bot uses covert methods of injecting additional fields into online Internet banking websites, asking users to answer questions that the authentic website would not ask. The collected details are then silently delivered to remote websites, and added into remote databases. The databases are then sold to other criminal elements down the chain who specialize in withdrawing the funds. The money laundering groups anonymously hire physical people to withdraw money from their personal accounts – in the criminal world these people are called “drops”, and their accounts are called “drop accounts”.
Without going too much into detail about the whole economy that operates behind Zeus/Zbot, let’s rather concentrate on some of its technical aspects.
An important fact to mention is that the bot itself is like a framework with no “brains”. It is merely a program that hooks itself into the system and hides there effectively. The logics that drives behaviour of the bot is contained in its configuration file.
The configuration file of Zeus/Zbot is like a definitions database for an antivirus product. Without it, it’s pretty much useless. The logics contained in the configuration contains the list of banking institutions that the bot targets, URLs of the additional components that the bots relies on to download commands and updates, the lists of questions and the list of the fields that the bot injects into Internet banking websites to steal personal details/credentials, etc.
For instance, if the attacker only wanted to target local customers in Brazil, the bot’s configuration file would enlist Brazilian banks and the list of questions/fields would be in Brazilian Portuguese language only. This way, the bot could transparently allow Internet banking transactions for non-Brazilian customers because the attacker would not be interested in those transactions, attacking domestic customers and their transactions only.
The configuration of Zeus/Zbot is never stored in open text. It is encrypted. Previous generation of Zeus/Zbot used a hard-coded encryption mechanism for its configuration. It was possible to reverse engineer the encryption algorithm and build a decryptor for any configuration file that belonged to any bot of the same generation.
The game has changed. The latest generation of Zeus/Zbot encrypts configuration file with a key that is unique for and is stored inside the bot executable for which this configuration file exists. This way, configuration file of one bot sample will not work for another bot sample, even if both samples are generated with the same builder. As the decryption key is stored inside the bot executable, the configuration cannot be decrypted without the executable. However, the executable that contains the key is also packed on top so that the key cannot easily be retrieved from it. Brute-forcing the key is not a viable option as the key is 256 bytes long.
In other words, it’s practically “a riddle wrapped in a mystery inside an enigma, but perhaps there is a key”, as Winston Churchill once said about the homeland of Zeus author(s).
In order to reveal the key for Zeus/Zbot configuration and study the decryption mechanism, a few things need to be done first.
Firstly, Zeus/Zbot could be run on a virtual machine under OllyDbg debugger and dumped with the OllyDump plugin installed:
The created dump can be loaded into IDA disassembler – the variables that store dynamically retrieved addresses of APIs should be renamed into the API names to ease the code reading, as shown below:
The analysed dump does not reveal the code that downloads and decrypts the configuration file. It is because the dump was created for the first stage of the execution workflow – when it drops other files, installs hooks and injects its own code into the system process services.exe.
In spite of the decryption key being present in the dump (as it becomes known later), revealing it now along with the decryption mechanism by analysing the dump statically is not easy as the code did not branch that execution path yet.
Ok, so what do we do now?
Let’s run RootkitUnhooker to check the system integrity. According to its hook revealer, two installed IAT hooks can be seen:
According to ThreatExpert report, the bot creates the following files:
Because of the hooks, these files are not visible in Explorer, but trying to create a directory %System%\lowsec invokes the following message box:
The hook in the system process services.exe gives a good reason to dump it and analyse what’s in its memory. Dumping main module is not enough as a typical injection mechanism allocates memory on the heap of the process and writes the code there. Thus, the process needs to be dumped entirely, all of its heap pages.
From all the dumped pages of the system process services.exe, two allocations belong to the bot:
These two allocations may span over the address range 0×00040000 – 0×00057000 or 0×00980000 – 0×00997000 after reboot, and can be joined together to be loaded into the disassembler again.
Once reloaded into disassembler, the variables that store dynamically retrieved addresses of APIs should be renamed again into the API names. As the names of the APIs are not visible in this dump anymore, the APIs can either be retrieved by looking up the virtual addresses contained in the function pointers, or by matching the disassembled code with the previously disassembled dump (obtained from OllyDbg/OllyDump) and assigning the same names as in the former dump to the same pointer variables, as shown in the screen grab below:
With the properly named API function pointers, it’s much easier to read the code.
The bot contains a special section in its code that contains several important fields:
The URL fields in that section are encoded by using an older encryption mechanism that was used by older Zeus/Zbot generations. Here is a C equivalent of the decryptor – it’s straightforward:
for (int i = 0; i < iBufferSize; i++)
b = lpSourceBuffer[i];
if ((i % 2) == 0)
b += 2 * i + 10;
b += 0xF9 - 2 * i;
lpDestinationBuffer[i] += b;
One of the URLs points to an encrypted configuration file. The bot downloads that file and saves it into a hidden file %System%\lowsec\local.ds.
Next, the bot reads the 256-byte long encryption key stored in its section and uses it to decrypt the downloaded configuration file:
The decryption routine is not very easy to follow during static analysis. One way of building a configuration file decryptor is to blindly rip the assembler code out of the bot source, only taking care of interfacing it properly – that is passing it the same parameters. However, in order to understand the code and build its C equivalent, the code is better to be traced.
But here comes the question – how to trace the code that is running inside the services.exe process?
An easy way of doing that so it attach a debugger of your choice to the system process services.exe, break its execution, point EIP (the instruction pointer) into the first instruction of the decryption routine, patch memory contents to instruct the routine to unpack a file that is different from %System%\lowsec\local.ds (before you’re doing that, make sure the configuration file is downloaded from the earlier discovered URL and is saved under a different filename), suspend all other threads of services.exe process, and debug step-by-step its decryption routine.
The image below shows how the filename %System%\lowsec\local.ds is patched with c:\c
Stepping through the decryption routine reveals how the configuration file is fully decrypted:
Decryption routine itself is represented below:
During decryption, the values of its 256-byte key are constantly shuffled. The C equivalent of this routine is:
byCounter = 0;
byMask = 0;
iSectionOffset = 0x2a;
for (int i = 0; i < iConfigSize; i++)
byMask += byResource[iSectionStart + iSectionOffset + byCounter];
byTemp = byResource[iSectionStart + iSectionOffset + byMask];
byResource[iSectionStart + iSectionOffset + byMask] = byResource[iSectionStart + iSectionOffset + byCounter];
byResource[iSectionStart + iSectionOffset + byCounter] = byTemp;
byTemp += byResource[iSectionStart + iSectionOffset + byMask];
byConfig[i] ^= byResource[iSectionStart + iSectionOffset + byTemp];
Once the configuration file is decrypted, its internal structure reveals that it consists of data blocks. Every data block has a header that describes the length of the block, its type, and whether it’s compressed or not.
As shown in the image below, some fields’ meaning is not clear. But it seems that the 5th byte of the data block indicates if the data it contains is encrypted or not. Two DWORD values that follow are showing the size of compressed and uncompressed data. Next, the block contains the data itself.
For example, the first block has the size values equal 4 bytes, and the data block itself is 0B 07 02 01. Next two blocks are not compressed – the data size for both blocks is 0×28 bytes. The last block contains a flag that shows it’s compressed. The size of compressed data is 0×85 bytes; the size of uncompressed data is 0xA1 bytes, with the 0×85 bytes of data followed.
Analysis of the decompression routine reveals that it’s unrv2b algorithm. The decompression source code is available here.
By knowing the decryption/decompression mechanism and the data format, it is possible now to build a tool that will inspect full memory contents of the process services.exe, locate a page which contains Zeus/Zbot code in it, then locate a section in it with the 256-byte key, retrieve that key and use it to decrypt the provided configuration file. As the address of the section within the bot page is not known in advance, it can still easily be detected by probing the size of the structure, probing the bytes within the 256-byte encryption key, and trying to decode the URLs, knowing their length (from the structure) and the key-less encoding method (from the older Zeus generations).
Unfortunately, such tool could only be able to decrypt configuration file on a machine infected with Zeus/Zbot. Thus, it must be run on the same virtual machine that is infected with the bot.
The tool is available for download here.
One positive side-effect of the tool is that even if the configuration file is not available, the tool will still reveal if the machine is infected with Zbot.
The limitation of the tool is that it won’t be able to decrypt a configuration file for one bot if the virtual machine is infected with another bot, even if both bots are produced with the same Zeus builder. It’s because every bot uses a unique encryption key that will only decrypt configuration file created for the very same bot.
Running the Zeus configuration decryptor over several Zeus/Zbot samples submitted in the last few days reveals quite interesting characteristics. The full list of its capabilities is too big to be presented here, so only a few questions/additional fields that Zbot injects are highlighted below:
- Due to security measures, please provide the answers to all the security questions listed below:
- As an additional safeguard, we ask that you provide the last eight digits of your ATM or Check Card number
- Please enter your Credit Card Number linked to your account, security code (cvv) and expiration date
- For your Identity verification and Fraud prevention please send us answers that you need to answer when you log in to your account
- Our behavioral monitoring software has detected a variation in your use pattern. For your protection, we ask that you verify your identity by answering your personal questions below. Once verified, you will be directed to the page.
- Authorization Required. In order to provide you with extra security, we occasionally need to ask for additional information when you access your accounts online. Please enter the information below to Sign on:
- Please enter your Personal Access Code (PAC):
- Your first school
- Your mother’s maiden name
- Your place of birth
- Please enter all digits of your PIN
- What is your favourite meal or restaurant?
- The name of a memorable place to you?
- Your favourite film of all time?
- Your favourite book of all time?
- Your favourite teacher or subject?
- Your favourite TV star or show?
- Please enter a valid Mother’s Maiden Name
- Please enter a valid Driver’s License Number
- Please enter a valid Date of Birth
- Please enter a valid Social Security Number
- Please enter a valid Home Telephone Number
- Your favorite TV show?
- Your favorite flower?
- Your favorite leisure time activity?
- Your favorite type of music?
- Your favorite professional football team?
- Your favorite professional baseball team?
- The color of your first car?
- Your favorite holiday?
- Your favorite place to vacation?
- In which month were your parents married?
- What is the first letter of the name of your high school?
- What is the first letter of the name of your pet?
- In which month was your first child born?
- What was the last two digits of the year of your high school graduation?
- Please enter valid ATM/Debit Card # (CIN)
- Please enter valid PIN
- Please enter valid Last 4 Digits of Social Security or Tax ID #
The list goes on, but you get an idea of what an identity theft weapon it is.
Update: Thanks to Peter Kosinar and Thorsten Holz for identifying the encryption algorithm above as RC4.