An icon-display bug in Windows allows attackers to masquerade PE files with special icons by automatically “borrowing” other commonly used icons from the local machine, thus tricking users into clicking them. The bug behind this vulnerability lies deep inside the image-handling code of Windows. The bug has been present since at least Windows 7 and is still present in the most updated versions of Windows 10.
We discovered the bug while researching a recent batch of malicious PE files. After copying files from one directory to another, we noticed an odd behavior: some of the files’ icons changed. To rule out the possibility of a mistake (or a simple lack of caffeine), we copied the files to a different directory and again the icons of these files changed to a different commonly-used and completely unrelated icon. This piqued our interest and prompted an investigation into this strange phenomenon.
To see this phenomenon in action, check out this video:
The batch of malware from April 2017 included several dozen samples of the Cerber ransomware, all of which exhibited this anomaly.
The following are the icons extracted from these samples, as displayed in Windows Explorer:
At first glance, one might think these are simply a bunch of benign-looking icons used by malware (admittedly, the Cybereason icon at the top-left corner is a bit of an odd choice), but after converting these icons to a different internal image format, the icons reveal their true form:
As can be seen, these files are almost identical with slight random pixel modifications, suggesting they were auto-generated to avoid icon-based signatures. The original icon seems a bit odd, though. It’s definitely based on the Adobe logo, and it’s all black-and-white, but other than that it’s a valid icon file.
(This is the original Adobe logo):
However, while many malicious programs use stolen pieces of resources to hide themselves both from security applications as well as prying human eyes, the icons in this case were not the ones actually displayed on the screen. Another thing they all had in common, other than mimicking the Adobe icon, was that they were all what we dub “ true monochrome icon ”, or TMI for short.
“True monochrome icons” are icons that have two specific qualities - they have only two colors (i.e: their bits-per-pixel count is 1) and these two colors are exactly black (0x000000) and white (0xFFFFFF). It should be noted that icons may be monochrome with other colors, as well as black-and-white but not monochrome (i.e: bpp higher than 1). However, this phenomenon only occurs for true monochrome icons .
Full documentation of the icon file format can be found here:
Here is an example of such a file, extracted from a Cerber sample:
Empiric experimentation showed that the icon-switching anomaly occurred for any TMI, not requiring them to be specially crafted. To prove this point, we made our own empty TMI using a hex editor (this can be easily reproduced by diligent readers, do-it-yourself style):
We then embedded this icon as the only icon of a small “hello world” application. Instead of the single-pixel monochrome icon, Windows Explorer displayed it like this:
After renaming it in the same directory, the displayed icon was changed to this:
So, what’s going on?
It appears the problem begins with the way rendered icons are cached, and the special treatment TMIs receive, which causes them not to overwrite existing icons.
Windows Explorer, as well as any other explorer-based frames in other applications, implements icon caching using the CImageList class from comctl32.dll (User Experience Controls Library):
The cache is implemented by mapping the file’s path to an index inside a CImageList (there are several such caches, one for each icon display size). Therefore, when viewing a file whose icon was already rendered in the past, it will simply be taken from the cache. Paths which were not yet encountered by the process will need to be rendered from scratch based on the file type and added to the cache. That is why when viewing a new directory with multiple icon files or PE files with embedded icons, files will be displayed gradually with some delay. When files are copied or renamed, their icon will be rendered again as they will be treated as newly-encountered paths.
These caches, however, have a limited and relatively small size. When a new icon is added to the image list, if it’s not yet empty, the index used will be -1 and the new icon will be appended. However, once the list is full, the new icon will overwrite a previously-created one by replacing it at at its index (presumably on a LRU-basis).
This logic is implemented in the CImageList::_ReplaceIcon function:
Add or replace depending on the given index:
After some manipulations, the function checks whether the current image at that index has an alpha channel, and if so (as is almost always the case), sets a flag which is later used to decide how to call DrawIconEx .
If this flag is set, the function will later call DrawIconEx to actually draw the given icon over the pre-existing one in the list using the flag DI_MASK (1) instead of DI_NORMAL (3).
Internally, icons and images in general may contain two different pixel maps - the “colors” and the “mask”, which can be applied over the colors, as can be seen in the documentation of ICONINFO:
So what happens in essence is that only the “mask” part of the icon is drawn and overwrites the DC (Device Context) of the mask ( [esi+7ch] ) instead of the DC of the colors ( [esi+78h] ). When the icon is a TMI, this in fact results in no new pixels being overwritten and the rendering of the icon borrows the previous occupant of the CImageList at this index!
This requires the cache to be full, which depends on the caller of these functions. However, for explorer-like components (such as “file open” dialog boxes) the size is usually very small.
Here is an example which shows this can happen in any process which uses such components. This is a screenshot taken from Outlook 2016’s “add attachment” window when viewing a directory full of TMIs (disclaimer: this screenshot was taken after browsing a few icon-rich directories in the same window before accessing the monochrome-icons directory):
This bug will trigger not only for the icon files, of course, but for any PE file which embeds them. The condition is that these are the only type of icons in the file, since Windows’ algorithm for choosing the “best fit” icon for rendering tends to go over embedded icons based on size and in the order of high-color-depth to low-color-depth.
Since this is the case, we decided to search our malware database for samples which contain only true-monochrome-icons in their resource section and managed to find several hundred such samples dating back from 2013 (the earliest samples in our database). All of them, without exception, triggered this bug. A similar search in the benign-samples database yielded no results.
We divided these samples into several groups, based on the icon variations they were using:
As mentioned above, the first detection was for Cerber ransomware samples from April 17 that abused the Adobe logo icon. Here are five such samples (and the way they currently appear on our machine):
It’s possible that these samples are only a small group of auto-generated PE files with pseudo-random resources attached to mask them, and that the bug was never knowingly or intentionally exploited by Cerber - it’s difficult to determine this with certainty. However, we also managed to find samples ranging from 2014 to 2017 that contain a single, empty, true-monochrome icon similar to the one we crafted ourselves. This does prove, in our opinion, that the creators of these files knew of this bug and actively exploited it, since an empty icon which does not attempt by itself to mimic any existing application is worthless otherwise.
This bug, while not a major security vulnerability, is a reminder to remain vigilant for spear phishing campaigns. The same security advice that users are accustomed to hearing applies to this situation as well: Avoid opening suspicious emails and attachments. Also, un-hide extensions for known files types, as it will help identifying executable extensions, as seen here:
The bug was reported to Microsoft on June 2017 and our research was published with their permission.