Waves Maxx Audio DLL Side-Loading LPE via Windows Registry
Introduction to Windows Registry
When performing vulnerability research, it is essential to make sure that all attack vectors concerning exploitation are exhausted. One avenue of exploitation comes from the Windows registry. For an attacker, the Windows registry holds critical data that applications may rely on during runtime. Not only do applications rely on this data, but also services and even the Windows OS itself relies on the information inside of the registry to function correctly.
This blog post details information about the Windows registry and how improper permissions assignment on registry keys can lead to vulnerabilities in applications or services that utilize this functionality.
Windows Registry
The Windows registry is a database that holds configuration data for the Windows OS, along with different applications, and services that choose to utilize this functionality. These applications and services can read, write, and modify the registry as well if given the proper permissions.
From the perspective of an attacker, the Windows registry is an attractive target. This is because many different use cases exist for why an application or service may utilize this extended functionality. The use cases recommended by Microsoft is for configuration and runtime functionality.
When dealing with the Windows registry, it is imperative to assign proper permissions to each key created. The reason for this is to prevent unauthorized access to potentially critical data within a given registry key. The next topic of interest is understanding how to enumerate registry key permissions.
Windows Registry Permissions
Permissions are a core component of operating systems and implemented in many ways.
When it comes to the Windows operating system, permissions are assigned using Access Control Lists (ACL). ACL’s are composed of Access Control Entries (ACE’s) which hold two separate permission structures.
The first is a Discretionary Access Control List (DACL) and the second is a SYSTEM Access Control List (SACL). The DACL is the one we are most interested in since it defines who can access a specified resource to modify, write, read, or execute respectively. The SACL, on the other hand, is for logging purposes for the security event log.
To view the DACL’s of a given resource, we can use the tool AccessEnum
from the SysInternals Suite
. AccessEnum
is a versatile tool that offers functionality to quickly enumerate the permissions of resources. Beyond just enumerating files and folders within the Windows operating system, it can also enumerate the permissions of keys within the Windows registry Hive. When using AccessEnum
for permission enumeration, the column Write
is what holds the most valuable information.
This column provides information on what users or groups are allowed permissions synonymous with the ability of write
permissions for each resource, or in this case, each registry key scanned. AccessEnum
offers the ability to navigate the registry hive by default with an option available to end users. This is performed through the Registry
option provided by AccessEnum
its self, and Figure 1 depicts this option.
After selecting this option, a new window pop-up will show, offering the ability to select any registry key available. For this example, the registry key HKEY_LOCAL_MACHINE
was selected. This scan enumerates every Subkey within this primary Key and displays permissions respectively for each Subkey. In Figure 2, this example of permission enumeration can be reviewed.
When it comes to reviewing registry key permissions, it is important to note the use of specific permissions. These different permissions are related to different functionality that is exposed through the Windows registry. One such example is the permission Set Value
. This permission allows for the ability to modify values within a registry key.
A complete list of the permissions including the specific ones are provided in the list below:
- Full Control
- Query Value
- Set Value
- Create Subkey
- Enumerate Subkeys
- Notify
- Create Link
- Delete
- Write DAC
- Write Owner
- Read Control
Now that we have covered general information and permissions concerning the Windows registry, the next section details a Local Privilege Escalation (LPE) vulnerability affecting a System level Service through the improper usage of Windows registry key permissions.
Abusing Registry Key Entries with a Real-World Vulnerability
Intro – Waves Maxx Audio Registry Key Abuse LPE to System
Waves Maxx Audio (Waves) is a software suite that offers advanced audio settings and manipulation for consumer and professional products. This software comes preinstalled on a few different Dell laptop’s. Waves installs a service named WavesSysSvc
that is configured to start automatically and starts as LocalSystem
at runtime.
This service establishes a handle to a registry key called General
and queries an entry with a name value of ExternalModule
that holds a semi-colon separated string as its data value. This string contains Dynamic Link Library (DLL) names that are loaded into the process memory when it is started. This registry key has improper permissions that allow any user in group Users
to modify the entries within the key. An attacker may abuse the permissions assigned in order to conduct a DLL side-loading attack and achieve local privilege escalation.
Discovery via Dynamic Analysis
This vulnerability was discovered during runtime analysis using the tool Procmon
from SysInternals Suite
. Procmon
is a versatile tool that is used to view different types of operations performed by an application. The specific operations that lead to this discovery were Load Image
and RegQueryValue
. In Figure 3 this specific Filter can be seen within Procmon
.
These filters provide useful information regarding any potential registry key entry where the data value holds references to either a DLL or a binary application executable. The RegQueryValue
operation relates to the Windows API function RegQueryValue
respectively and returns the results of certain data values within a registry key entry in the Detail
column. The second operation, Load Image
, is used to capture functionality that is related to the loading of a library or image into a process’ memory region. When analyzing the output of the resulting filter, we find that a DLL referenced in the Detail
column of a RegQueryValue
operation is also present in the Load Image
Path
column. This is seen in the Procmon
CSV output below.
After seeing this, the first thing we checked is the permissions associated with the File MaxxAudioIntelKabylake64.dll
. Given that this file is located within the C:WindowsSystem32
path a user may not have the proper permissions to abuse this local resource. Checking the permissions associated with this resource can be done using the tool AccessEnum
from SysInternals Suite
. To do this, we copied the absolute path present within the Path
column of the Load Image
operation to the search section of AccessEnum
and then hit the Scan
button. The results of the permissions associated with this file can be viewed in the lower pane window of AccessEnum
.
Reviewing the results of the permissions for this given resource reveals that only NT AUTHORITYSYSTEM
has the permissions to write to this file. These permissions are not suitable since normal users cannot modify or alter this file.
The next thing we checked is the registry key that is referenced before this Load Image
operation is executed. This is because shortly after the RegQueryValue
operation is performed, this DLL is loaded into the WavesSysSvc64.exe
process memory region. We used the tool Regedit
from Microsoft
to view the permissions associated with this given registry key. Using Regedit
, we navigated to the same registry key path location that was recorded from Procmon
.
Next, right click on the key named General
and select the option called Permissions
. After selecting this option, locate the Users
group and review the permissions associated. Given that the group Users
has complete control over this General
registry key, manipulation of entries within this key can be performed.
Now that we’ve verified that the Users
group is assigned the Full Control
permission over the registry key named General
, the next step is verifying this information by performing both static and dynamic analysis of the Waves service binaries in the next section.
Root Cause Analysis
Further analysis is required so that a deeper understanding of why this vulnerability exists and how it can be abused. To perform this task, two main tools will be utilized. These tools are IDA Pro
from Hex-Rays
and WinDbg Preview
from Microsoft. Now, when dealing with IDA Pro analysis, I will only cover the essential sections concerning the vulnerability. For WinDbg Preview
, we used the independent binary version of Time Travel Debugging
(TTD). TTD is used because of its ability to review a Trace
file to gain insight into memory, instructions being executed, and values of arguments being passed to functions at runtime.
The first task we performed is that of identifying the service binary responsible for functionality associated with the Waves service. We used the command line tool sc
, which is used to gather information and issue commands to services. The information we want to gather from the output of the sc
tool is the BINARY_PATH_NAME
field for the Waves service. We used the qc
flag, which queries a specific service for configuration information.
From the image above, it can be seen that the BINARY_PATH_NAME
holds an absolute path to the Waves service binary. This binary is what is executed once this service is started. With this information, we then used WinDbg’s Time Travel Debugger, to record the initial runtime functionality. To perform this action, we used the tool GFlags
from Microsoft. GFlags
can be used in both command line and a Graphical User Interface (GUI) mode with the same functionality. This tool will be used to set the WinDbg tool TTD
as the debugger
option to create a Trace file once this service is started.
The tool GFlags
is included as part of WinDbg and may be downloaded. After this is properly installed, start Gflags.exe, and a window like Figure 7 shall be present.
Next, select the Image File
tab near the top. In this new view, two spots will be modified. The first is the Image
section. For this section, type the name of the service binary WavesSysSvc64.exe
then hit the keyboard key Tab
. After this, everything else should become available to modify. The second option to change is the debugger
option. For this option, pass the full path to the Time Travel Debugging TTD
binary along with the arguments seen in the snippet below:
The full path where TTD
is located may be different, so make sure to change that to the current path for each environment. The -maxfile
option is for setting a max size limit for the trace file to 2GB. The -out
command specifies the location of where to store the trace file and finally the -launch
command is used. The reason the -launch
command has no following binary to launch is because after setting this value, GFlags
automatically assigns the service binary as the last argument being passed. Since the service binary is the last argument being passed, make sure to include a trailing white space after the -launch
command. Finally, select apply, and these new settings will be instantly applied.
The reason we set the TTD
binary as the debugger option inside of Gflags
is because WinDbg Preview’s
TTD
does not support active tracing of Session 0
processes through default usage. Using GFlags
, this limitation can be bypassed because, GFlags
starts TTD
in the same session as the Waves service binary allowing for the ability of Tracing
to be performed.
After setting these options within GFlags
, we then started the Service and verified that everything was working correctly. To perform this task, the tool Procexp
from SysInternals Suite
will be utilized. Procexp
is a tool that is used to review detailed information about running processes. Once Procexp
is open, locate the TTD.exe
binary executable and then terminate the process. By terminating the TTD process, the trace will complete and be appropriately recorded and can be provided to WinDbg preview for analysis.
As mentioned before in earlier sections, this vulnerability is centered around DLL side-loading. Now, that attack is quite vague and can be accomplished in many ways. However, the main thing is that a DLL is loaded into a processes memory region. Armed with this knowledge, we can infer that the LoadLibrary
functions exposed via the Kernel32 library are utilized. These LoadLibrary
functions can be called many times so by using a simple WinDbg command one-liner, we can iterate over all the calls to LoadLibraryA
, print out the contents of the argument being passed via the register RCX
, and dump the call stack. This one-liner can be seen below:
After setting this command and running the debugger, the output of the command is present. We are looking for the specific location where calls that hold an argument value equal to our first DLL referenced in the entry name ExternalModule
General
registry key. While reviewing this output the first DLL referenced is found, and the output from the one-liner can be seen below:
As we can see from the command output, as we enter the LoadLibraryA
function, the argument passed holds our specific DLL name. After that, a call stack was generated. Following the call stack information, a ModLoad
is present. Modload
signals that the DLL referenced has been loaded into the process memory region. So, given this information, we can infer that the instruction before the address offset at MaxxAudioAPOShell64!WavesFX_Preset_SetSoundMode+0x54895
is in fact the LoadLibraryA
function. The next step is to figure out how this value is acquired from the ExternalModule
registry entry. At this point, we will use the tool IDA Pro
and disassemble the MaxxAudioAPOShell64.dll
. First, we searched for usage of the function RegQueryValue
, as this function is used to return the data value associated with a specific entry name in a registry key. However, for this function to operate successfully a handle must first be created for the registry key General
. This is performed through the usage of the function RegCreateKey
. So, after a bit of cross-referencing and some more debugging with WinDbg, the specific RegCreateKey
function was found. This information can be seen in the snippet below:
According to the code snippet above, the registry key and its path should be passed via the argument lpSubKey
(RDX
) to RegCreateKeyExA
function. To verify this, we set a breakpoint at address offset 0x2C67B
inside of the MaxxAudioAPOSHell64.dll
using WinDbg and displayed the contents of the RDX
register. The contents of this register along with the WinDbg output can be seen in the snippet below:
After the handle is created, we then enter the renamed function AAA_regqueryvalue
where the data values from the above registry entry ExternalModule
is enumerated. So, if we step into this function, we will find the usage of the RegQueryValueExA
function. When reviewing this function call, we notice that the ExternalModule
registry key entry name is being passed. This can be seen in the related code snippet below:
Now, according to Microsoft’s documentation for the function RegQueryValueExA
, if we want to see the data values that are recovered from the entry named ExternalModule
, we will have to display the contents of [rsp+218h+lpData]
after this function call. The following WinDbg snippet below shows the output that we expected to see.
This semi-colon separated string that was enumerated from the data value of the ExternalModule
key entry, is then later passed as an argument to an _mbschr
function call. The code responsible for this can be seen in the IDA Pro snippet below:
The function _mbschr
, will return a pointer that is shifted to the first occurrence of the token specified. In our case, the returned pointer is pointing to the ;
token referenced from the data value. The following code after this function call, essentially copies the first string recovered before our token ;
and passes it to our final function that holds the call to LoadLibraryA
via the RDX
register.
After entering the final function where the vulnerable call to LoadLibraryA
exists, a strcpy_s
function is also called shortly before. This strcpy_s
function copies the reference of our first DLL name into the memory allocation that RCX
currently points to. At the start of this function, a reference of RCX
is stored into the register RBX
and when this strcpy_s
function is called; all references are updated with the first DLL name from the ExternalModule
data value.
After the referenced memory location is updated via the call to strcpy_s
, the final function LoadLibraryA
is called where the first DLL referenced is loaded. The code responsible for this can be seen below in the IDA Pro code snippet.
Finally, we will set a breakpoint at the address offset 0x5AC1F
inside of the DLL MaxxAudioAPOShell64.dll
and dump the contents of the Resource name argument passed via RCX
to LoadLibraryA
function. If the output from WinDbg backs up our assumptions, then we know our analysis was accurate. The results of WinDbg can be seen in the output snippet below:
Reviewing the above output, we can verify that our analysis was correct. The main points covered during this phase of analysis were the creation of the registry key, the enumeration of registry key entry name ExternalModule
data values, the token string search to identify each DLL to load, and finally, the vulnerable function call its self. Now that we have covered the analysis section of this vulnerability, the last stage is exploitation!
Exploitation
We will exploit this vulnerability by replacing the first DLL referenced before the first token ;
with the absolute path to our malicious DLL. Inside of our malicious DLL, we will place the start of our payload inside of the function DLLMain
. The DLLMain
function in reference to DLL’s is unique because whenever a DLL is loaded into a process memory region via LoadLibrary or similar functions, the DLLMain
function is automatically executed. Given that the Waves service is configured to runs as the account LocalSystem
, any child processes that are spawned will inherit the permissions of system.
Now that we have covered the high-level theory behind this attack, the next thing to cover is the actual exploitation of this vulnerability. The first thing we did was build a simple DLL where the DLLMain
function calls another function called bind_shell
, that spawns a bind system level shell on the local port 4444.
This Proof of Concept (PoC) code is used to showcase that the ability to gain a system level interactive command prompt session is possible. The code responsible for this can be seen in the code snippet below:
We used Python to automate the overwriting of data within the entry name ExternalModule
registry key General
. The Python based PoC can be seen below:
This PoC Python script accepts a single argument. This argument is the absolute path of the malicious DLL. This script takes that argument and appends it to the variable reg_value_data
that is passed to the function set_registry
. The function set_registry
performs the action of modifying the entry named ExternalModule
data value accordingly. This Python script can run successfully from any user account in the Users
group. The output below displays the output from the Python script once executed.
After running this PoC script and manually restarting the Waves service, we use ncat to establish a connection to localhost
on port 4444 to gain a SYSTEM level shell. This can be viewed in the command window below:
Final Thoughts
At VerSprite, we have noticed that the trend of improper permission assignment on critical resources is quite common. The impact of improper permissions assignment ranges from information leakage to code execution. To counter this, when handling the creation and or usage of registry keys and accompanying value entries, it is essential to apply proper permissions. By applying proper permissions, the attack presented in this blog would have been prevented.
If you are interested in further reading of vulnerabilities in Windows software, below is a list of recent vulnerabilities and exploitation analysis:
- Waves MaxxAudio LPE Advisory
- Exploitation of Remote WCF Vulnerabilities
- Windows Userland Application Attack Surface Enumeration
- Abusing Insecure Windows Communication Foundation (WCF) Endpoints
Offensive Minded Security Exploit Development
VerSprite’s Research and Development division (a.k.a VS-Labs) is comprised of individuals who are passionate about diving into the internals of various technologies. Our clients rely on VerSprite’s unique offerings of zero-day vulnerability research and exploit development to protect their assets from various threat actors. From advanced technical security training to our research for hire B.O.S.S offering, we help organizations solve their most complex technical challenges. Learn more about VS-Labs→
- /
- /
- /
- /
- /
- /
- /
- /
- /
- /
- /
- /
- /
- /
- /
- /
- /
- /
- /
- /
- /
- /