Avira is one of the leading Anti-Virus vendors and also the biggest one in Germany. Security is their daily business and they’ve done a quite nice job in hardening their products. But even the toughest software may be broken sometimes ;-). So, this time I’d like to present a common vulnerability with a really interesting (and uncommon) root cause.
Quoted from my official Full-Disclosure post:
A buffer overflow vulnerability has been identified in Avira Secure Backup v126.96.36.199 Build 3616.
The application loads the values of the Registry Keys “AutoUpdateDownloadFilename” and “AutoUpdateProgressFilename” from “HKEY_CURRENT_USER\Software\Avira Secure Backup” on startup but does not properly validate the length of the fetched values before using them in the further application context, which leads to a buffer overflow condition with possible persistent code execution.
The application queries the values via a RegQueryValueExW call and a fixed buffer pointer (lpData) and a fixed buffer size pointer (lpcbData). If the input string size is greater than the predefined size, the application uses a second RegQueryValueExW call with the new buffer size set to the length of the input string, but reuses the original buffer pointer (lpData), which has not been resized. This results in overwriting memory space inlcuding SEH - records.
An attacker needs to force the victim to import an arbitrary .reg file in order to exploit the vulnerability. Successful exploits can allow attackers to execute arbitrary code with the privileges of the user running the application. Failed exploits will result in a denial-of-service condition. The attack scenario is persistent, because the code is executed as long as the manipulated values are loaded into the Registry.
Crashing the application!
To exploit this vulnerability, the following script creates an arbitrary .reg file, which needs to be imported into the target registry.
Starting the application results in EIP control:
via overwritten SEH records:
Sounds like a boring strcpy() overflow ?
After importing the arbitrary .reg file created by the Python script, the Call stack of the application looks like the following on crash-time:
The last entry clearly shows that a return address has been overwritten by the PoC code indicating that the application flow might be controlled. The last call is a ntdll.memmove triggered somewhere from within the function at 0x0043F0D2.
The complete vulnerable code part located at this address:
Looks like there is no common strcpy() ;-)…
Hunting the Bug!
The vulnerable code part needs to be divided into different parts:
The first part queries the base key „HKEY_CURRENT_USER\Software\Avira Secure Backup“ using a CALL ESI (0x0043F0FD), which holds the function RegOpenKeyExW, that has been moved into ESI at 0x0043F0DA.
The function arguments for the RegOpenKeyExW can be found on the stack:
The second part queries the final values:
The function RegQueryValueExW is moved into EDI (0x0043F0FF) and later called (0x0043F11B) with the ValueName (first: “AutoUpdateProgressFilename” and in a second run “AutoUpdateDownloadFilename”) of the vulnerable key. The function arguments for the RegQueryValueExW call on the stack are:
Let’s have a look at a basic RegQueryValueExW function call and its arguments (http://msdn.microsoft.com/en-us/library/windows/desktop/ms724911(v=vs.85).aspx)
In the context of this vulnerability the two most important arguments are „lpData“ that stores a pointer to a buffer, where the input is placed to and „lpcbData“ which stores a pointer to a variable that holds the size of the buffer. This results in a correlated, logical function call like:
The arguments can be found at the referenced addresses:
0x0012EC7C (aka lpData) is empty because it’s the target location where the buffer content will be stored:
0x0012EC4C (aka lpcbData) contains a hex value of 820 – decimal for 2080. This value has been moved to memory at 0x0043F0F6:
This means the target buffer has a length of 2080 bytes, which needs to be reduced by half, because this is a Unicode call that inserts a „0x00“ after each character, resulting in a total usable buffer size of 1040 chars.
After RegQueryValueExW is called, it stores its return-value into EAX. If the content of lpData is less or equal of the size stored in lpcbData, the function will exit with the return code 0, therefor EAX will hold the value 0. Using a TEST call at 0x0043F11D the application checks whether the value of EAX is 0, which means the call has exited normally.
But in this test case EAX holds the value „EA“:
This return code means „ERROR_MORE_DATA“, which indicates that the parsed value from „AutoUpdateProgressFilename“ is too big for the buffer, resulting in the JNZ (0x0043F11F) being taken – since the TEST statement returns „false“:
This bypasses the instruction at 0x0043F121, which would move the value „1“ into EBP-1. Let’s have a look at the stored value at EBP-1 (0x0012EC57):
EBP-1 holds „0“ and is not replaced by a „1“ which can be correlated to a simple „IF“ statement later. The application jumps to 0x0043F125:
…and closes the opened Reg-Key using a RegCloseKey call. The following code part is important for why this crash happens:
The application compares (0x0043F12E) the value at EBP-1 with BL. EBP-1 still holds „0“ and BL holds 0 too:
This results in the next jump not being taken and the application continues to execute the next part:
The application CALLs ESI (0x0043F143) referencing RegOpenKeyExW to open the Registry key „ HKEY_CURRENT_USER\Software\Avira Secure Backup“ (0x0043F13D) again.
Then the application CALLs EDI (0x0043F15B) referencing RegQueryValueExW, but this time with different parameters, which can be found on the stack:
The CALL arguments are pushed in reversed order onto the stack, resulting in the following logical function call, which is at first view exactly the same as in the first CALL.
But this time the argument lpcbData has changed to a hex value of 9B2 (decimal=2482), which is exactly the length (+2 for terminators) of the input string from the PoC script. This means the attacker is able to control the lpcbData argument :-)! This new value has been set by the ReqQueryValueExW call at 0x0043F11B.
0x0012EC4C (aka lpcbData):
But the application still uses the old memory space at 0x0012EC7C, which has not been resized at this point to store the data, which leads to attacker-controlled overwriting of memory space!
Reverse Engineering the Bug!
The root cause of this security vulnerability is the re-usage of the same memory address location for storing the lpData value and the fact that the attacker is able to control the size of the lpcbData variable without resizing lpcbData buffer-size on the application side. An exemplary C++ code snippet that demonstrates the vulnerability:
The first RegQueryValueEx call uses pBufsize which is sizeof(buffer) = 255. If the input value is bigger than the buffer, the RegQueryValueEx call returns ERROR_MORE_DATA. If this happens, a second RegQueryValueEx - call uses the same buffer pointer (buffer), but with a new buffer size pointer (pBufsize2), which is sizeof (ret) = the size of the previous input buffer.
Reverse Engineering the Fix!
I’m always skeptical. Avira released v188.8.131.52 of their Secure Backup product, let’s see how they’ve fixed the issue:
Yay - the code part became smaller ;-).
The buffer size (lpcbData) is still 2080 (decimal), but the IF statement and the second RegQueryValueExW call is missing:
…resulting in a fixed vulnerability.
Some last words about the exploitability!
It’s a unicode based vulnerability (RegQueryValueExW)
SafeSEH is in place to prevent the misusage of the overwritten SEH records:
I didn’t take a deeper look at how to bypass the SafeSEH constellation, because finding the root-cause is always more important…but if you have a smart idea on how to beneficially exploit this vulnerability, leave a message below this post :-).
Some very last words about the disclosure process!
I’ve reported this flaw on 3rd November 2013 and the updated version was released on 13th November 2013. It has taken Avira only 10 days to fix this issue and release an update. WOW! Quite fast!!! You’re now on the second place on my fastest-vendor-patch-list (1st place is still Nullsoft with a 5-day patch).
I’d like to thank the Avira Technical Support for the very friendly and always professional way of dealing with my report. Another shiny example how vulnerability coordination really works!