Windows Telephony Service Elevation of Privilege Vulnerability
Introduction
As part of knowledge sharing, I will be releasing the details of some of the bugs I’ve found that have been patched by Microsoft. Today’s post is on CVE-2024-43626, Windows Telephony Service Elevation of Privilege Vulnerability patched in November 2024.
Details
The Telephony Service is a local service running on Windows by default. The service runs in a svchost.exe
process, with its main logic written in tapisrv.dll
.
tapisrv.dll
registers 3 RPC interfaces that an unprivileged client can interact with.
1 | [ |
ClientAttach()
is called to retrieve a context handle, which is then passed to ClientRequest()
to perform operations.ClientRequest()
is a dispatcher function that invokes other functionalities based on an opnum.
1 | if ( (unsigned int)opNum < 0xA9 ) |
The opnum 69 corresponds to the function LSetAppPriority()
.
This function has a path to call into GetPriorityListTReqCall()
:
1 | void __fastcall GetPriorityListTReqCall(wchar_t **pOutput) |
A handle to the registry key HandOffPriorities
in the current user hive is passed to GetPriorityList()
:
1 | void __fastcall GetPriorityList(HKEY hKey, LPCWSTR RequestMediaCallName, wchar_t **pOutput) |
The function calls RegQueryValueExW()
with a null output buffer first to get the size of the RequestMediaCall
data as cbData
. Afterwards it allocates a heap buffer of size cbData + 2
, and uses it as an output buffer to store the data of RequestMediaCall
. Finally, the data is passed to _wcsupr()
directly, which is an unsafe string function that depends on the null terminator to determine the string’s size.
Since an unprivileged user is able to control the contents of the RequestMediaCall
value, the user can leave out the null terminator, shape the heap such that an adjacent chunk with non null fields borders the current chunk, and the _wcsupr()
function will follow the bytes in the adjacent chunk to modify data belonging to the adjacent chunk. To give a sense of how that might be exploited, if the adjacent chunk is a structure with the first member being a “buffer size used” field, we can make the initial “size used” 0x61(‘a’), and _wcsupr()
will corrupt it to become 0x41(‘A’), confusing the program to think the buffer has 0x20 more bytes than it actually has. Or we can find a structure with the first member being a pointer and corrupt that pointer to hijack it.
Apart from potentially executing code, an attacker can use this bug to read adjacent heap memory to leak heap pointers. This breaks ASLR and heap randomization.
LSetAppPriority()
eventually calls SetPriorityList()
to write the RequestMediaCall
data back to the same place.
1 | LSTATUS __fastcall SetPriorityList(HKEY hKey, LPCWSTR lpValueName, LPCWSTR lpString) |
It uses lstrlenW()
to calculate the length of the string, which unfortunately also depends on the presence of the null pointer for length. This means the function will write data belonging to the adjacent heap chunk into a registry value we can read from. By querying that value, we get to leak information from the service process.
POC

Suggested Mitigations
Use
RegGetValueW()
instead ofRegQueryValueExW()
. The former has the ability to restrict the data type of value to be queried, and adds terminating nulls automatically if the value is a string type.Look through code for other occurrences of
RegQueryValueExW()
followed by an unsafe string manipulation function, and fix those too(like the one inClientAttach()
).
Actual Fix
Microsoft eventually decided to manually null terminate the string after RegQueryValueExW()
.
1 | v6 = (BYTE *)HeapAlloc(ghTapisrvHeap, 8u, cbData + 2); |