As enticing as the topic of today’s article is, we will be understanding the underlying concepts of how one becomes NT AUTHORITY/SYSTEM by just executing Getsystem (Incase one has a meterpreter foothold) or PowerUp(PowerSploit Module), in a nutshell — Debunking the magic Meterpreter performs to get you the highest privileged shell on a Windows system.
The word pipe in this context is related to pipelines in the Unix-like world. The pipelines we work within a terminal are unnamed and resided in the shell. The named pipe concept, on the other hand, gives the pipe a name, and by having a name, it utilizes the filesystem so that interaction with it is like interacting with a file. Remember the purpose of our pipelines, to take the output of a command and pipe it as input to another command. This is the easier way of looking at it: behind the scenes, each command fires off a process. So what the pipe is doing is allowing processes to communicate with each other with shared data. This is just one of several methods for achieving Inter-process Communication (IPC). Hence, to put it together, a named pipe is a file that processes can interact with to achieve IPC.
Microsoft has always liked doing things their own way. Named pipes in Windows have some important distinctions from the concept in Unix-like systems. For one, whereas named pipes can persist beyond process lifetime in Unix, in Windows, they disappear when the last reference to them disappears. Another Windows quirk is that named pipes, although they work a lot like files, cannot actually be mounted in the filesystem. They have their own filesystem and are referenced with
\\pipe\[name]. There are functions available to the software developer to work with named pipes (for example
CloseHandle), but the user isn’t going to see them.
There are some situations in which a named pipe is visible to the user in Windows. Using system debuggers like WinDbg
Let’s examine the concept as implemented in Windows a little deeper. I gave examples of functions for working with named pipes. Those are
pipe client functions. The initial creation of the named pipe can be done with the
CreateNamedPipe function — a
pipe server function. The creator of a named pipe is a
pipe server, and the application attaching to and using the named pipe is a pipe client. The client connects to the server end of the named pipe and uses
WriteFile to actually communicate with the pipe. Although named pipes can only be created locally, it is possible to work with remote named pipes. The period in the named pipe path is swapped with a hostname to communicate with remote pipes:
The server-client terminology is no accident. The
pipe server creates the named pipe and handles pipe client requests.
Impersonating the security context of a pipe client
If you’re new to this concept, you probably read the title of this section and thought, oh, named pipe client impersonation? I wonder what wizard’s hacking tool we’ll be installing next! Nope. This is normal behavior and is implemented with the
ImpersonateNamedPipeClient function. The security professional in you is thinking that allowing security context impersonation in IPC is just plain nutty, but the software designer in you may be familiar with the original innocent logic that allows for more efficient architecture. Suppose that a privileged process creates a named pipe. You thus have a situation where pipe client requests are being read and managed by a privileged pipe server. Impersonation allows the pipe server to reduce its privilege while processing pipe client requests. Naturally, allowing impersonation per se means that a pipe server with lower privilege could impersonate a privileged pipe client and do naughty things on the client’s behalf. Well, this won’t do. Thankfully, pipe clients can set flags in their
CreateFile function call to limit the impersonation, but they don’t have to. It’s not unusual to see this skipped.
Superfluous pipes and pipe creation race conditions
I know what the hacker in you is saying now: it seems that the entire named pipe server-client concept relies on the assumption that the named pipe exists and the pipe server is actually available. A brilliant deduction! A process could very well attempt to connect to the named pipe without knowing whether the pipe server has even created it yet. The server may have crashed, or the server end is simply not created — regardless, a unique vulnerability appears if this happens: the pipe client’s security context can get snatched up by a process that merely creates the requested pipe! This can be easily exploited in situations where an application is designed to keep requesting a named pipe until it succeeds.
A similar situation occurs when a malicious process creates a named pipe before the legitimate process gets the chance to — a race condition. In the Unix-like world, named pipes are also called FIFOs in honor of their first-in, first-out structure. This is pretty much how flowing through a pipe works, so it’s fitting. Anyway, a consequence of this FIFO structure in a named pipe creation race condition is that the first pipe server to create the named pipe will get the first pipe client that requests it. If you know for a fact that a privileged pipe client is going to be making a specific request, the attacker just needs to be the first in line in order to usurp the client’s security context.
This is the theoretical understanding of how getsystem creates a race condition by firing up a series of
NamedClients and tries reaching to the services of unflagged
NamedServers I highly recommend it to study the Ruby file within
/usr/share/metasploit-framework/scripts/meterpreter After understanding this underlying concept, the script is pretty much self-explanatory — even for coders who don't use Ruby as their primary-coding language.
I have always been interested in knowing and understanding ‘Behind-the-scenes’ of anything that takes me by surprise. Please feel free to comment on any queries below or if you’d like to share any other insights or facts.