As part of Shearwater’s Managed Security Operations Team, I regularly come across all kinds of exploitable vulnerabilities.
If neglected, these could allow hackers to breach an organisation’s defences, potentially causing irreparable damage.
In most cases, the vulnerabilities I come across are already well-known to security experts. Usually, patches exist to fix them.
However, discovering a new bug for the first time, one that isn’t known by software vendors, security researchers or the general public, is quite rare.
Such a vulnerability is known as a ‘Zero-Day’.
This is the story of how I recently discovered a ‘Zero-Day’ bug in the widely used Adobe Reader.
One day, while performing some vulnerability research, I noticed that the Adobe Update Service runs from a slightly unusual path (Common Files).
While not inherently dangerous, it drew my attention enough to look a little deeper into the service. The AdobeARMService is installed with Adobe Reader, runs as LocalSystem and has the task of keeping Adobe Reader up to date.
I checked the service and binary permissions and did not discover any misconfigurations. Next up, I had to work out how the service functioned. For that purpose, I installed Sysinternal’s Process Monitor, set it up to capture events and ran the armsvc.exe binary.
I noticed the program calls out to a DLL that does not exist, this suggested the possibility of a DLL Injection.
Windows has a defined search order for DLL files when invoked by a program. Interestingly this program was only looking in the local directory and C:\Windows\SysWOW64\, not the entire search path, suggesting it may be a hardcoded reference. If it was a hardcoded refence leftover from development, there is a good chance the program would not be checking that the DLL was signed. If that was the case, by placing a specially crafted DLL in that path, I would be able to execute arbitrary code.
To test my theory, I needed to discover what function the armsvc.exe was trying to load from the DLL. To that end I used the NSA’s reverse engineering tool Ghidra.
Reversing the binary, we can see that it tries to load the UnloadUserProfile function from USERENV.dll. With this knowledge, we can code our own DLL, create a function with that name and whatever code we want to include, export the function and have armsvc.exe load and execute it.
For a proof of concept, we can make the function pop open Calculator.
BOOL APIENTRY DllMain(HMODULE hModule,
extern “C” __declspec(dllexport) void UnloadUserProfile()
# i686-w64-mingw32-g++ -c -DBUILDING_EXAMPLE_DLL USERENV.cpp
# i686-w64-mingw32-g++ -shared -o USERENV.dll USERENV.o
Note that I compiled on Linux with mingw, the code may need to be tweaked for a Visual Studio compilation on Windows.
Copy USERENV.dll to C:\Program Files (x86)\Common Files\Adobe\ARM\1.0\
Run C:\Program Files (x86)\Common Files\Adobe\ARM\1.0\armsvc.exe and observe Calculator being run proving arbitrary code execution.
A malicious actor could use this vulnerability for Privilege Escalation to SYSTEM, for Persistence and for Signed Execution, Whitelisting Bypass/Defence Evasion.
The root cause of this vulnerability was the non-existent DLL reference and not ensuring loaded DLLs are signed.
Thankfully, Adobe moved swiftly to release security updates to fix this ‘Zero-Day’ bug. Click here for further details and remediation solutions.
It’s a timely reminder to everyone to stay on top of software updates and make sure you regularly update patches to keep your systems secure.