Analyzing an Integer Overflow in Bitdefender AV: Part 1 – The Vulnerability
June 19, 2018 | The ZDI Research TeamIn the pantheon of software bugs, vulnerabilities that occur in security software are considered more severe than others. We rely of the security software to defend against attackers, so bugs in our defenses could not just allow attackers to cause harm, they could lead to a false sense of security. We think we’re protected when we really aren’t. That’s one reason we took special note of a case reported in the Bitdefender Internet Security that could allow remote code execution if exploited. The now-patched bug resulted from an integer overflow that deserves a closer look.
What follows is the write-up from the researcher who submitted the case. The researcher, who goes by the nom de plume Pagefault, did such an excellent job describing the vulnerability, we thought others would be interested in seeing just one method for exploiting an integer overflow in modern software despite the presence of DEP and ASLR. Here’s how he describes the bug, which could allow remote code execution on affected systems.
The Setup
Bitdefender provides a variety of products with antivirus (AV) functionality, and all of these AV products install the vsserv.exe
SYSTEM service. This service handles AV scanning requests. As you would expect from an AV engine, it processes various file types (e.g. PDF, JPG, DOC, EXE) and EXE packers through a series of compressed executable files having the .xmd or .cvd extension, placed under %PROGRAMFILES%\Common Files\Bitdefender\Bitdefender Threat Scanner\Antivirus_XXXX\Plugins\
. However, a lesser known function of the engine is the emulation of encountered executable code, which is used for detecting unknown or obfuscated viruses.
When encountering an x86 portable executable (PE), Bitdefender virtually executes the PE through a highly complex code emulator included in the cevakrnl.xmd
and ceva_emu.cvd
files. The emulator creates a private virtual address space, handles bytecode interpretation, provides an implementation of various windows APIs, and creates Just-In-Time (JIT) code for frequently executed addresses.
The Vulnerability
When a call to a function is detected by the emulator, cevakrnl.xmd!sub_366F4D0()
gets called. The function processes the first instruction in the emulated function, searching for matches through a constant table. When processing following instructions, the algorithm takes into consideration the current state of the interpreter. In other words, for each matched instruction, another match table is searched for following instructions.
The initial search ends when the first 16 bytes in the function are matched by the emulator, or when the instruction sequence is unknown.
The final matching entry in a constant table provides a number of known bytes before the function and 16 bytes after the start of the function.
[X bytes before]
FUNCTIONSTART:
16 bytes already matched
[Y bytes after]
The bytes are then read and checked to determine if they match a known AV signature. If they match, a handler for the detected code signature is called for further processing.
Diving Deep
When a Themida packer code sequence is encountered, the sub_36906D0()
function gets called in order to perform code interpretation on the matched sequence.
The only variable part in the interpreted code is the "X" ebp offset where ebx is read from. The function extracts the offset from the code stream and uses it to obtain the value of ebx from the emulated stack.
The mov ecx, [ebx]
instruction is interpreted next, with ecx
being extracted from the emulated address placed in ebx
.
The following code sequence is next interpreted:
The process computes the resulting emulated esi from ecx + dword[ecx + 0x78 + word[ecx+0x3c]]
.
A total of 0x28 bytes are next extracted from the emulated esi, and the dword at offset 0x18 read (mov edi, [esi+18h]
in emulated code).
The dword(N)
is multiplied by four, but without integer boundary checks and passed in a call to malloc()
. This results in the allocation of an undersized buffer.
Next a loop is entered where the buffer is filled with the CRC32 checksum of N strings found at offsets placed on the emulated stack. The loop is also aborted when the offset to a string is overly large.
This provides everything we need for code execution to occur. The first requirement is the ability to overwrite an arbitrary number of bytes with our own content. This requires the inclusion of strings for which the calculated CRC has a desired value (e.g. reverse CRC). The included CRC32 algorithm is non-standard, as it takes into account the terminating zero byte for each string. However, reversing it is possible through brute force.
When the matched function is called, the vulnerability is triggered and a desired number of bytes overflowed with arbitrary content.
Conclusion – Part One
This deep analysis allowed us to see where an attacker could execute arbitrary code on affected versions of Bitdefender. This bug (and others) were addressed by the vendor with update 73447. Users of Bitdefender should ensure their systems have this release or newer applied to their system. Pagefault didn’t stop there in his analysis. In our next blog, we’ll walk through the exploit itself, which has to take care to avoid being impacted by DEP and ALSR.
Until then, follow the team for the latest in exploit techniques and security patches.