How we found the vulnerability in the bank’s mail server and how it threatened

6 min

We often conduct penetration tests for banks and other financial institutions. We also often find vulnerabilities of different severity levels. This post is about one of these cases.

Recently, while checking the security of the bank’s web resources, we found a vulnerability in the Exim 4.89 mail server, which could lead to remote code execution. The vulnerability is known as CVE-2018-6789. Using the PoC exploit, we got the Reverse Shell on the remote machine, and then access to the bank’s website.

Naturally, we wondered why such exploitation of the vulnerability became possible.

Where did CVE-2018-6789 come from?

In short, the vulnerability is due to an error in calculating the buffer length in the base64.c: b64decode function used by Exim. You can read more about this. here (in English).

If you enter a string of a special length, you can overwrite one byte of information and, using simple actions, change the server commands, thereby executing arbitrary code (RCE).

Exim allocates a buffer of 3 * (len / 4) +1 bytes to hold the decoded data. However, if an incorrect base64-string is fed to the function input, for example, 4n + 3 long, then Exim will allocate 3n + 1 bytes for the buffer. But it will write 3n + 2 bytes of data into the buffer. This causes one byte to be overwritten in the heap.

Exim provides store_malloc_3 and store_free_3, which are wrappers for the malloc and free functions from Glibc. Glibc allocates a large block of data, then stores its metadata in the first 0x10 bytes and returns a pointer to memory where the user can write his data. This is how it looks:

Metadata includes the size of the previous block (the one in memory above), the size of the current block, and some flags. The first three bits of the size are used to store flags. In the example, size 0x81 implies that the current chunk is 0x80 bytes and the previous chunk is already in use.

Freed blocks, once used by Exim, are placed in a doubly linked list. Glibc maintains it by flags and merges contiguous freed chunks into a larger chunk to avoid fragmentation. For each memory allocation request, Glibc examines these chunks in FIFO order and reuses them.

To improve performance, Exim uses its own memory management add-on; it is based on the storeblock structure. The main feature of storeblock is that each of them is at least 0x2000 bytes in size, which becomes a limitation for exploitation. Note that storeblock is also block data. This is how it looks in memory:

The commands supported by the mail server for organizing data on the heap are:

  • EHLO hostname… Each time the EHLO command is executed, hostname is stored in the variable sender_host_name. Accordingly, store_free is executed first for the old name and store_malloc for the new one.
  • Any unidentified command… For each unrecognized command of non-printable characters, Exim allocates a buffer to convert it to readable characters.
  • AUTH… In most authentication procedures, Exim uses base64 encoding to communicate with the client. The encoding and decoding line is stored in the buffer allocated by store_get (). A buffer for the encoded and decoded string is allocated using store_get ().
  • Reset in EHLO / HELO, MAIL, RCPT commands… When the commands complete correctly, Exim calls smtp_reset. It in turn calls store_reset in order to reset the blockchain to the “reset point”. This means that all storeblock and allocated store_get after the last command will be freed.

How the vulnerability is exploited

In order to use a one-byte overflow in the heap, we must be able to free the data block that is under the base64 decoded string. Sender_host_name is suitable for this.

The heap needs to be formed in such a way as to leave the block of data freed above the block containing sender_host_name.

For this you need:

1. Put a large block in unsorted bin. First of all, we send an EHLO message with an oversized hostname so that it allocates and frees a chunk of length 0x6060 into unsorted bin.

2. Select the first storeblock. Then we send an unrecognized command to call store_get () and allocate the storeblock inside the freed chunk.

3. Select the second block and release the first. We send EHLO to receive the second block. The first block is freed sequentially due to smtp_reset called after EHLO completes.

Once the heap is prepared, we can use it to overwrite the original block size. We modify 0x2021 to 0x20f1, which slightly expands the block.

4. Send base64 data and overflow 1 byte on the heap. Run AUTH command to send base64 data.

5. Create and send a string of suitable size. Since we expanded the block of data, the beginning of the next chunk will now lie somewhere inside. Now we need to fix it in order to pass the Glibc integrity check. We’re sending another base64 string here.

6. Free the extended block. To control the content of the extended block, we need to free the block first, because we cannot edit it directly. That is, we must send a new EHLO message to free up the old hostname. However, processing the EHLO command calls smtp_reset upon successful execution. This can lead to program interruption or abnormal termination. To avoid this, we are sending an invalid hostname like a +.

7. Overwrite the next pointer of the overlapping storeblock.

After the chunk is freed, we can get it with AUTH and overwrite part of the overlapping storeblock. Here we use a technique called “partial recording”. This allows us to change the pointer without breaking ASLR. We have partially changed the following pointer to a block containing ACL lines. ACL lines are specified by a set of globals, such as uschar * acl_smtp_helo;

These pointers are initialized at the beginning of the Exim process and set according to the configuration. For example, if the configuration contains the line acl_smtp_mail = acl_check_mail, the acl_smtp_mail pointer points to the acl_check_mail line.

Each time the server receives a MAIL FROM command, Exim performs an ACL check, which first expands acl_check_mail. On expansion, if Exim encounters the line $ {run {cmd}} it will try to execute the cmd command, so a remote attacker can get the code to execute.

8. Reset storeblock and get the storeblock containing the ACL. The ACL block is now on the blockchain. It will be freed after smtp_reset () is done, and then we can retrieve it again by allocating a few blocks.

9. Overwrite ACL lines and run ACL check. Finally, we rewrite the entire block containing the ACL lines. Now we send commands like EHLO, MAIL, RCPT to run ACL check.

By the way, the exploitation of the vulnerability was facilitated by the ASLR disabled for some reason unknown to us.

What problems did the customer have?

The first is the lack of update management. Due to the fact that the old version of Exim was used, it was possible to organize a compromise of the system. To avoid this, we recommend that you organize regular checking and installation of security updates on the components of the information infrastructure.

We recommend checking for new security and critical updates at least once a month. To check for updates, it is better to use the sites / mailing lists of equipment manufacturers or information from the repositories.

The bank also lacked a vulnerability management process, which is why the vulnerability was not detected in time. The use of specialized vulnerability scanners – for example, OpenVAS, Nessus, xSpider, etc. – would help to rectify the situation. Regular penetration testing and monitoring the timing of vulnerability elimination would also help.

And last but not least. The bank lacked a change management process. All changes were made by administrators in the production environment. Consequently, no one controlled or monitored this. This led to the fact that ASLR was disabled on the server.


Several seemingly unrelated violations resulted in the bank’s website being compromised. This could be used by malefactors to, for example, change the bank details to their own.

The story ended well. After the compromise, we immediately notified the client of the situation. The bank urgently updated the Exim server to the current version, for which the vulnerability is no longer relevant. However, if the vulnerability had been identified not during the penetration test, but by real attackers, the outcome could have been different.


Leave a Reply