One more Steam Windows Client Local Privilege Escalation 0day
Previously on Privilege Escalation
Not long ago I published an article about Steam vulnerability. I received a lot of feedback. But Valve didn’t say a single word, HackerOne sent a huge letter and, mostly, kept silence. Eventually things escalated with Valve and I got banned by them on HackerOne — I can no longer participate in their vulnerability rejection program (the rest of H1 is still available though).
You can read the story in more detail in previous article, here is a couple of words about current situation.
And it’s sad and simple — Valve keeps failing. Last patch, that should have solved the problem, can be easily bypassed (https://twitter.com/general_nfs/status/1162067274443833344) so the vulnerability still exists. Yes, I’ve checked, it works like a charm.
But this article is not about an old vulnerability, it’s about new one. Since Valve decided to read a public report instead of private report one more time, I won’t take that pleasure away from them.
Short vulnerability description
Vulnerability exploitation is quite simple and consists of three steps:
1) Preparing the exploitation environment (you’ve got two ways via different weaknesses).
2) Making Steam copy and run our DLL.
3) DLL has to match some requirements.
Any of these actions could be performed by any OS user, more precisely — any program on computer. As a result any code code could be executed with maximum privileges, this vulnerability class is called «escalation of privileges» (eop) or «local privilege escalation» (lpe). Despite any application itself could be harmful, achieving maximum privileges can lead to much more disastrous consequences. For example, disabling firewall and antivirus, rootkit installation, concealing of process-miner, theft any PC user’s private data — is just a small portion of what could be done.
Theoretical minimum
It was very funny to read comments to previous article, where community wrote “user can’t write to HKLM registry keys” or “you need administrator rights to create a symlink”. What’s interesting is that checking this statements takes almost as long as writing such comment. And yes, just in case, both statements are false. Because of this I decided to write a small section in this article, where I describe some difficult steps of exploitation.
1) “User can’t write to HKLM registry section”.
There is no such general rule. There are specific security rules for specific registry keys. Valve gives full control to all users on HKLM\SOFTWARE\Wow6432Node\Valve\steam branch, so any user can do whatever he wants with it.
2) “It’s not possible to start or stop service without administrator rights”.
There is no such general rule. There are specific security rules for specific services. Valve gave any user permissions to start and stop Steam Client Service.
3) “You need administrator rights to create a symlink”.
This statement is funny itself, because there are five general types of links in Windows and only one and a half require such rights. Let me introduce you to: file symbolic link, object directory symbolic link, hard link, NTFS reparse point and reg_link. Administrator rights are necessary only to create file symbolic link and for permanent object directory symbolic link (temporary object directory symbolic link lives as long as lives the session, where it was created, most commonly until reboot, and doesn’t require any special rights).
4) “Folder to folder symlink”.
This is called NTFS reparse point or NTFS mount point. The name isn’t really important, the point is, this thing allows to use one directory as a link to another one. Could be created by user from an empty folder, if user has write permissions for it. Use CreateMountPoint.exe from symboliclink testing tools package (https://github.com/googleprojectzero/symboliclink-testing-tools/) to create it.
5) Opportunistic Lock
Opportunistic Lock or OpLock (https://docs.microsoft.com/ru-ru/windows/win32/fileio/opportunistic-locks) is a special mechanism that allows to temporarily block access to a certain file. There are many details about OpLocks, that could be described here, like features of working with files and different types of access. In short, the program can “catch” an event of access to a certain file and temporarily hold it. OpLocks could be set with SetOpLock.exe utility from the same symboliclink testing tools package (https://github.com/googleprojectzero/symboliclink-testing-tools/). Launch of this utility sets the needed oplock; when access to oplocked file occurs, the utility prints a message; oplock is released by pressing “Enter”.
6) BaitAndSwitch
It’s a name of a technique, that combines creation of links and oplocks to win TOCTOU (time of check\time of use). This method is simpler described with an example:
Imagine a program that does something like this:
ReadContentFromFile(“C:\test\myfile.txt”);
ReadContentFromFile(“C:\test\myfile.txt”);
It’s just a reading of the same file twice in a row. Is there always the same information read? No, not necessarily.
First, we create two folders with “C:\test1\myfile.txt” and “C:\test2\myfile.txt” files. Next, we empty “C:\test” and create a reparse point to “C:\test1” from it. Then we set oplock to the file from the first folder and run a program. As soon as it opens the file, oplock is being triggered. We change the reparse point, so “C:\test” links to “C:\test2”. Now, after oplock is released, program will read information for a second time from another file.
Where is it applicable? It’s easy to answer — there are quite typical situations, where firstly file is being checked (first read) and then executed (second read). That’s how you give one file for a check and the other for execution.
Now we’re all set for exploitation.
Exploitation 1. Preparing the environment
It’s necessary to prepare the working environment. Firstly, we need to obtain CreateMountPoint.exe and SetOpLock.exe files.
Now it’s needed to make some small changes to Steam file structure. Our goal is to have folder with Steam.exe and steamclient.dll, and without “bin” folder. There are two ways to do this.
Way 1
Rename\remove bin folder from Steam root folder. That’s it, you’re gorgeous (Steam gives every user permissions for everything in it’s folder).
Way 2
Change InstallPath value to path to any of our folders in HKLM\SOFTWARE\Wow6432Node\Valve\steam registry key. Copy Steam.exe and steamclient.dll to that folder from original Steam folder.
So, we’ve prepared C:\Steam folder (there could be any path, but I will use this in examples) with any of the ways. Now we have to create b1, b2, b3 and b4 folders in it. Let’s copy steamservice.dll in the first three of them (originally located in bin folder of Steam package), and copy specially crafted library with the same name — steamservice.dll — in b4. More specifically on library preparing in Exploitation 3.
Open two console windows. Now environment preparation is complete.
Exploitation 2. Switching the file
I think that it’s clear from preparations that it’s going to be something like BaitAndSwitch, described earlier.
ProcMon screenshot:
Above is a fragment of typical Steam Client Service start log. Take a look at the part, where firstly dll is being copied to C:\Program Files (x86)\Common Files\Steam and then is being loaded. We will make Steam copy our library from C:\Steam\b4 instead. Unfortunately, at first there are checks, among them the library signature is being checked to prevent dll hijacking (oh, irony).
So, I’ll describe exploitation step by step. Steps are combined in groups of similar actions. For each step there is noted what to execute, where to do it and what happens after it (the names of console windows are “cmd1” and “cmd2”).
1) Create C:\Steam\bin folder and execute the following command in cmd1:
CreateMountPoint.exe С:\Steam\bin C:\Steam\b1
2) Set oplock in cmd1:
SetOpLock.exe C:\Steam\b1\steamservice.dll
3) Run Steam Client Service. In cmd1 you can see that the access to the file has been caught.
4) Remove C:\Steam\bin, create C:\Steam\bin folder in it’s place and execute the following in cmd2:
CreateMountPoint.exe С:\Steam\bin C:\Steam\b2
5) Set oplock in cmd2:
SetOpLock.exe C:\Steam\b2\steamservice.dll
6) Release oplock in cmd1. You can see that you’ve caught an access to file in cmd2.
7) Remove C:\Steam\bin, create C:\Steam\bin folder in it’s place and execute the following in cmd1:
CreateMountPoint.exe С:\Steam\bin C:\Steam\b3
8) Set oplock in cmd1:
SetOpLock.exe C:\Steam\b3\steamservice.dll
9) Release oplock in cmd2. In cmd1 you can see that you’ve caught access to file.
10) Remove C:\Steam\bin, create C:\Steam\bin folder in it’s place and execute the following in cmd2:
CreateMountPoint.exe С:\Steam\bin C:\Steam\b2
11) Set oplock in cmd2:
SetOpLock.exe C:\Steam\b2\steamservice.dll
12) Release oplock in cmd1. You can see that you’ve caught access to file in cmd2.
13) Remove C:\Steam\bin, create C:\Steam\bin in it’s place and execute the following in cmd1:
CreateMountPoint.exe С:\Steam\bin C:\Steam\b3
14) Set oplock in cmd1:
SetOpLock.exe C:\Steam\b3\steamservice.dll
15) Release oplock in cmd2. In cmd1 you can see that you’ve caught access to file .
16) Remove C:\Steam\bin, create C:\Steam\bin folder in it’s place and execute the following in cmd2:
CreateMountPoint.exe С:\Steam\bin C:\Steam\b4
17) Release oplock in cmd1.
It might look complicated, but the idea is quite simple: redirect first 5 (out of 6) accesses to C:\Steam\bin\steamservice.dll to the original file from different folders was given (in the access order: b1, b2, b3, b2, b3), and at the 6th access redirect to our payload.
Here is schematic description:
On the left is a normal behavior of application, on the right is behavior during exploitation.
Exploitation 3. Injectable library
First I tried to use my most common dll, which creates interactive console in DllEntry, as a payload. Since the dll code is being executed within Steam Client Service, it will be executed with the service’s permissions — NT AUTHORITY\SYSTEM. But in the end of exploitation the console didn’t show up.
After loading, Steam service figures out that it’s being tricked and shuts down before payload in my dll is being executed.
I had to reverse a little bit and it turned out that the service checks the existence of
int WINAPI SteamService_RunMainLoop()
void WINAPI SteamService_Stop()
functions in library after dll loads. Moreover, service calls the first function, where I decided to place my payload (running of interactive console with service permissions — NT AUTHORITY\SYSTEM). That’s all, I repeat all actions and gain the console with maximum permissions.
Conclusion
All of it could be wrapped in exe-file, but I don’t really want to bother. I think the demo videos would be enough (registry way (https://www.youtube.com/watch?v=ZCHrjP0cMew), filesystem way (https://www.youtube.com/watch?v=I93aH86BUaE)).
I will not copy «Speculations» section from the previous article here. The facts are: the old vulnerability is relevant; you have just read about new one, and Valve still don’t want to hear about any problems.
Update (Aug 22, 2019)
Two hot news:
1) Beta Client got update with fix (https://steamcommunity.com/groups/SteamClientBeta#announcements/detail/1599262071399843693). I'll check it when main client get this update.
2) Valve change their policy of LPE on h1 (https://hackerone.com/valve/policy_versions). Great news!
Update (Aug 27, 2019)
Good news.
Main client got update with fixes (https://store.steampowered.com/news/53677/).
I was unbanned on H1 and got reward.
About author
Vasily Kravets, Lead Expert