Version: Avira Optimizer < 188.8.131.527
Operating System tested on: Windows 10 1803 (x64)
Vulnerability: Avira Optimizer Local Privilege Escalation through insecure named pipes
When users install the latest Avira antivirus, it comes shipped with a few different components along with it. One of these components is the Avira Optimizer. In short, “Avira.OptimizerHost.exe” runs as “NT AUTHORITY\SYSTEM” and takes commands issued over the “AviraOptimizerHost” named pipe (\\.\pipe\AviraOptimizerHost). The service does improper validation of the calling client along with invalid checks for started executables, which allows malicious code to issue process create calls to “Avira.OptimizerHost.exe” leading to local privilege escalation.
Identification and Exploitation
When assessing software for privilege escalation vulnerabilities, finding a starting point can often be overwhelming as there are many different primitives and vulnerability classes that exist. My approach often includes starting with the basics and working my way up in complexity. This process typically involves running a tool such as PowerUp, which will identify various trivial (yet common) misconfigurations.
If nothing interesting is returned, the next step is often looking for logical vulnerabilities. These vulnerabilities can be more difficult to automatically identify and require a little more manual poking. My workflow typically involves analyzing world-writable directories, writeable registry locations, exposed named pipes and RPC interfaces via NTObjectManager. When analyzing existing named pipes, it became apparent that some Avira process had created a named pipe with a NULL DACL. This effectively means that full access is granted to any user that requests it:
While interesting, it isn’t incredibly useful if the pipe isn’t being used by a privileged Avira process in some way. Checking the using process IDs of the pipe revealed that a SYSTEM Avira process is utilizing it:
The next step would be to figure out what “Avira.OptimizerHost.exe” is actually doing with the named pipe. This is a rabbit hole worth exploring since a privileged process is interacting with a resource that low privileged users have control over. Since “Avira.OptimizerHost.exe” has a handle to the pipe, it would make sense that the process is ingesting some sort of data being passed over it. In an effort to validate this, the next step was to pop open “Avira.OptimizerHost.exe” in IDA. After some poking, it became evident that the service was taking any client that connected to the “AviraOptimizerHost” named pipe and validating that it is a valid Avira file.
In order to abuse this named pipe, we need to circumvent this check in order to successfully send data to the service via the named pipe. The service does the check by getting the connecting client via GetNamedPipeClientProcessID() and then pulls the full image path via QueryFullProcessImageNameW().
Once the path is obtained, the service pulls the calling client’s certificate and makes sure that it is signed by Avira and hasn’t been tampered with. The idea here was to make sure only valid Avira processes are able to issue commands to the service. In order to circumvent this, we can simply inject our code into a running Avira process (or probably just clone an existing certificate).
The next step is to figure out what we can issue to the service over the named pipe. In cases like this, I typically like to investigate any potential legitimate clients and see what they do during normal operation. Since this pipe is a part of Avira’s optimizer, I began to look through the installed Avira components. After some dead ends, Avira’s System Speedup application boiled to the top due to the fact that optimization and speedup are Synonymous. After looking in Avira’s “System Speedup” folder, I stumped upon the Avira System Speedup libraries. I then loaded all of the files in the System Speedup folder into DnSpy and began to search for named pipe references. This led me down to “Avira.SystemSpeedup.Core.Client.Services.dll”, specifically the “StartServiceHost()” method.
As suspected, this is code to connect to the “AviraOptimizerHost” named pipe. Underneath, this function goes on to call the “OptimizerHostCommandsClient.Connect()” in the Avira.Optimizer.Common.Tools.OptimizerHostClient class, which sounds really interesting. When looking at this function, it just calls WaitNamedPipe() to wait for the pipe to be ready. Once it is, CreateFile is used to get a handle to the named pipe.
Looking back at the “StartServiceHost” method, it instantiates an instance of the Avira.Optimizer.Common.Tools.OptimizerHostClient class, connects to the “AviraOptimizerHost” named pipe and then goes on to call an interesting method named “StartParentProcess()”.
When looking at that instantiated class, there are many interesting methods. Such items include: StartProcess, StartParentProcess, StopProcess, AddTask and RemoveTask. These methods take various parameters and then go on to call “SendMessage” after converting the tasking to JSON:
The “SendMessage()” method takes the JSON of the command and sends it to the “AviraOptimizerHost” named pipe, where the SYSTEM process “Avira.OptimizerHost.exe” ingests it:
Looking at “Avira.OptimizerHost.exe”, we can see where the service reads in the JSON and parses out the arguments:
In this case, if we send the “StartProcess()” method to the named pipe, the service will pull out the “procid”, “exec” (executable path),“args” (arguments)/etc from the JSON blob sent over the named pipe. From there, it follows the same logic that was used to validate the named pipe in client, in which it takes the executable path from the “exec” parameter and checks the file’s certificate in order to ensure it belongs to Avira. The service relies on the subject and certificate serial number (both of which are attacker controlled), so it is possible to use a tool like SigPirate to clone the certificate off of a valid Avira executable and apply it to a custom payload.
In order to exploit this, we need to accomplish a few things:
- Prepare our payload. In this case, it is a .NET executable named Avira.SystemSpeedup.RealTime.Client.exe that starts cmd.exe
- Clone the certificate off of a valid Avira file and apply it to our payload
- Write code that injects into a valid Avira process, loads up Avira.Optimizer.Common.Tools.dll and instantiates an instance of the OptimizerHostClient class
- Use the exposed methods to connect to the “AviraOptimizerHost” named pipe and issue our commands to the service
For payload creation and certificate cloning, I will leave that as an exercise for the reader. In order to connect to the named pipe and send commands, we can reuse the existing Avira libraries by adding a reference to Avira.Optimizer.Common.Tools.dll and importing the Avira.Optimizer.Common.Tools.OptimizerHostClient namespace. Once done, we can just create an instance of the OptimizerHostCommandsClient class and call any of the interesting methods, such as “StartProcess”.
In order to achieve LPE, all we need to do is inject this assembly into an Avira process and invoke our entrypoint. Again, this is an exercise left up to the reader…but there are various projects that make this process easy (https://github.com/ChadSki/SharpNeedle).
After injecting into an Avira process and executing the above C# code, cmd.exe will be started as SYSTEM after the assembly connects to the “AviraOptimizerHost” named pipe and sends the “StartProcess()” method with the “exec” argument set to the payload with a cloned Avira certificate (in this case, a payload named Avira.SystemSpeedup.RealTime.Client.exe).
This vulnerability has been fixed in Avira Optimizer version 184.108.40.2067. After glancing at the fix, Avira now utilizes WinVerifyTrust() and an apparent path whitelist to ensure started processes aren’t influenced.
I’d like to take a second to give Avira and their development team props. The team remains in constant contact and fixes issues at a rapid pace. In the case of this report, a fix was developed and distributed to the public around 30 days after the initial report. It is refreshing to work with a vendor that takes vulnerability reports seriously and follows the community’s set expectations of 90 day fixes.
As committed as SpecterOps is to transparency, we acknowledge the speed at which attackers adopt new offensive techniques once they are made public. This is why prior to publication of a new bug or offensive technique, we regularly inform the respective vendor of the issue, supply ample time to mitigate the issue, and notify select, trusted vendors in order to ensure that detections can be delivered to their customers as quickly as possible.
- July 23rd, 2019: Vulnerability sent to the Avira security team
- July 24th, 2019: Avira acknowledged the report, noted some compile issues with the PoC
- July 26th, 2019: Avira reproduced the vulnerability with the PoC provided
- August 6th, 2019: Avira noted the developers fixed the issue, asked if I would like to test the fix
- August 6th, 2019: Replied to Avira with a bypass for the patch, provided updated PoC and details
- August 16th, 2019: Avira replied noting the developers implemented a new fix and asked if I’d like to test it
- August 16th, 2019: Tested the new fix. Let Avira know that it seemed decent enough
- August 27th, 2019: Fix pushed live
- August 29th, 2019: Details published