I`m creating a user-mode process with the correct (I believe) permissions to allow the system to access an object, however this seems to always fail on some system processes with "Access Denied".
The object is created in "Global\" namespace. Some processes succeed, and some not; with the same permissions. Permissions set using ConvertStringSecurityDescriptorToSecurityDescriptor.
Inspecting the processes that do not behave as expected, I've added more permissions:
User: Font Driver Host\UMFD-0
SID: S-1-5-96-0-0
For this I've set: (A;OICI;GA;;;LS) // Since there is no such user on Microsoft's website
User: NT AUTHORITY\NETWORK SERVICE
SID: S-1-5-20
For this I've set: A;OICI;GA;;;NS)
User: NT AUTHORITY\LOCAL SERVICE
SID: S-1-5-19
For this I've set: (A;OICI;GA;;;LS)
Some processes running under these permissions are able to read my object, and some not. Why is that ?
I will not use "Everyone" or empty/null DACL so please do not advise that as a solution. The idea is to "allow" what is necessary since windows denies everything by default.
Please point out what I am doing wrong, would really appreciate it.
It seems like you're setting the User-mode driver. Try to use UD instead of LS, This declaration is in the sddl.h file.
According to the rule of AccessCheck,
All ACEs have been checked and there is still at least one requested
access right that has not been explicitly allowed, in which case,
access is implicitly denied.
EDIT:
#include <iostream>
#include <windows.h>
#include <sddl.h>
BOOL CreateMyDACL(SECURITY_ATTRIBUTES* pSA)
{
const TCHAR* szSD = TEXT("D:")
TEXT("(A;OICI;GA;;;UD)")
TEXT("(A;OICI;GA;;;NS)")
TEXT("(A;OICI;GA;;;LS)");
if (NULL == pSA)
return FALSE;
return ConvertStringSecurityDescriptorToSecurityDescriptor(
szSD,
SDDL_REVISION_1,
&(pSA->lpSecurityDescriptor),
NULL);
}
int main()
{
TCHAR szName[] = TEXT("Global\\MyObject");
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.bInheritHandle = FALSE;
if (!CreateMyDACL(&sa))
{
printf("Failed CreateMyDACL\n");
exit(1);
}
HANDLE hEvent = CreateEvent(&sa, FALSE, TRUE, szName);
if (hEvent == NULL)
{
printf("error, %d\n", GetLastError());
return -1;
}
else
printf("success, %x\n", hEvent);
//Sleep(10000); //
CloseHandle(hEvent);
return 0;
}
Related
I have two applications:
first one: running with SYSTEM Privileges started by a service and the
second one: running still as SYSTEM but with lower privileges (SE_GROUP_INTEGRITY = "S-1-16-4096")
I want both applications to communicate over sharedmemory. Both need to read and write.
In my first application i create the filemapping with specific SECURITY_ATTRIBUTES i learned from this post: How to share memory between services and user processes?
SECURITY_ATTRIBUTES attributes;
ZeroMemory(&attributes, sizeof(attributes));
attributes.nLength = sizeof(attributes);
ConvertStringSecurityDescriptorToSecurityDescriptor(
L"D:P(A;OICI;GA;;;SY)(A;OICI;GA;;;BA)(A;OICI;GWR;;;IU)",
SDDL_REVISION_1,
&attributes.lpSecurityDescriptor,
NULL);
HANDLE test = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, 1024, "Global\\Test");
Everything works as expected, but if i then try to open the file mapping in my second application, it crashes with an access violation at OpenFileMapping.
HANDLE test = OpenFileMapping(FILE_ALL_ACCESS, FALSE, 1024, "Global\\Test");
if you want allow access to object for Low Integrity code you need add Low mandatory level (SDDL_ML_LOW) Integrity label (SDDL_MANDATORY_LABEL) to security descriptor. for example
"D:PNO_ACCESS_CONTROLS:(ML;;NW;;;LW)"
so in general code is next:
ULONG CreateSectionWithLowAccess(PHANDLE SectionHandle, ULONG dwMaximumSize, PCWSTR lpName)
{
SECURITY_ATTRIBUTES sa = { sizeof(sa) };
if (ConvertStringSecurityDescriptorToSecurityDescriptorW(L"D:PNO_ACCESS_CONTROLS:(ML;;NW;;;LW)",
SDDL_REVISION_1, &sa.lpSecurityDescriptor, NULL))
{
*SectionHandle = CreateFileMappingW(INVALID_HANDLE_VALUE, &sa, PAGE_READWRITE, 0, dwMaximumSize, lpName);
LocalFree(sa.lpSecurityDescriptor);
return *SectionHandle ? NOERROR : GetLastError();
}
return GetLastError();
}
I have followed Microsoft's on How to obtain a handle to any process with SeDebugPrivilege
to gain the SE_DEBUG privilege in my process token.
However, when trying to open a handle to MsMpEng.exe with full rights, I receive an error code of c0000022 (STATUS_ACCESS_DENIED).
I am wondering if I have made a mistake, or if it is simply not possible to gain that level of access rights to the process.
My code for opening the handle to the Defender process is below. This block follows the block for adding the SE_DEBUG privilege to my token.
if (Process32First(hSnap, &pt))
{
do {
if (!strcmp(pt.szExeFile, "MsMpEng.exe"))
{
printf("%d\n", pt.th32ProcessID);
OBJECT_ATTRIBUTES oa = { sizeof(oa), 0, 0, 0, 0 };
CLIENT_ID pid = { (HANDLE)pt.th32ProcessID, NULL };
NTSTATUS status = NtOpenProcess(&hDef, PROCESS_TERMINATE, &oa, &pid);
if (status == STATUS_SUCCESS)
{
printf("Handle to defender opened!");
NtClose(hDef);
}
else
printf("%x", status);
}
} while (Process32Next(hSnap, &pt));
}
Using C, I'm trying to establish a pipe connection between a process and it's child process, while the child process has a lower mandatory(integrity) level (low, while the parent process is high).
I wrote the following program (it's a simplified version if it), but it fails with: ERROR_ACCESS_DENIED (0x5)
INT wmain(IN SIZE_T nArgc, IN PWSTR *pArgv)
{
SECURITY_ATTRIBUTES securityArrtibutes = { 0 };
HANDLE hPipeRead = NULL;
HANDLE hPipeWrite = NULL;
tSecurityArrtibutes.nLength = sizeof(tSecurityArrtibutes);
tSecurityArrtibutes.bInheritHandle = TRUE;
SetSeSecurityNamePrivilege();
CreatePipe(&hPipeRead, &hPipeWrite, &securityArrtibutes, 0);
ChangeMandatoryLabelHandle(hPipeRead);
}
VOID ChangeMandatoryLabelHandle(HANDLE hObject)
{
BOOL bRetval = FALSE;
DWORD dwError = 0;
PSECURITY_DESCRIPTOR pSecurityDescriptor = NULL;
PACL ptSacl = NULL;
BOOL bSaclPresent = FALSE;
BOOL bSaclDefaulted = FALSE;
PWSTR pSDDL = NULL;
SDDL = L"S:(ML;;LW;;;NW)";
bRetval = ConvertStringSecurityDescriptorToSecurityDescriptorW(pSDDL, SDDL_REVISION_1, &pSecurityDescriptor, NULL);
if (FALSE == bRetval)
... // Handle failure
bRetval = GetSecurityDescriptorSacl(pSecurityDescriptor, &bSaclPresent, &ptSacl, &bSaclDefaulted);
if (FALSE == bRetval)
... // Handle failure
// getting ERROR_ACCESS_DENIED (0x5)
dwErr = SetSecurityInfo(hObject, SE_KERNEL_OBJECT, LABEL_SECURITY_INFORMATION, NULL, NULL, NULL, ptSacl);
if (ERROR_SUCCESS != dwErr)
... // Handle failure
... // Cleanup
}
I followed https://msdn.microsoft.com/en-us/library/windows/desktop/aa379588(v=vs.85).aspx and the remark that
To set the SACL of an object, the caller must have the SE_SECURITY_NAME privilege enabled. :
BOOL SetSeSecurityNamePrivilege()
{
HANDLE hToken;
TOKEN_PRIVILEGES tp;
LUID luid;
if(!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY | TOKEN_IMPERSONATE, &hToken)
return FALSE
if (!LookupPrivilegeValue(NULL, SE_SECURITY_NAME, &luid))
return FALSE;
tp.PrivilegeCount = 1;
tp.Privileges[0].Luid = luid;
if (bEnablePrivilege)
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
else
tp.Privileges[0].Attributes = 0;
if (!AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), (PTOKEN_PRIVILEGES)NULL, (PDWORD)NULL))
return FALSE;
if (GetLastError() == ERROR_NOT_ALL_ASSIGNED)
return FALSE;
return TRUE;
}
note: I get the same result when I try to execute it with files, with CreateFile instead of CreatePipe.
In addition if I try to do that with files, and I replace SetSecurityInfo with SetNamedSecurityInfoW, and give it the full path of the file, it works great.
Does anyone have an idea how to make it work? Thanks!
A few notes before addressing the cause of your immediate problem.
First and foremost, you do not need to change the security descriptor at all, and doing so is unlikely to help you achieve your ultimate goal. The security descriptor is only checked when you attempt to open a handle to an object; if you already have a handle, the security descriptor has no effect. Since you are creating an unnamed pipe, you must be passing the handle, not the pipe name, to the child, so you do not need the ChangeMandatoryLabelHandle function at all.
Secondly, the SE_SECURITY_NAME privilege is not needed when setting LABEL_SECURITY_INFORMATION. The mandatory label is logically distinct from the rest of the SACL, and is treated as a special case.
Thirdly, your "S:(ML;;LW;;;NW)" is invalid.
I tried to use it in ConvertStringSecurityDescriptorToSecurityDescriptorW and got error 1336, The access control list (ACL) structure is invalid. Instead, use"D:NO_ACCESS_CONTROLS:(ML;;;;;LW)" or better still use the following code to create a security descriptor with a low label and no DACL:
ULONG cb = MAX_SID_SIZE;
PSID LowLabelSid = (PSID)alloca(MAX_SID_SIZE);
ULONG dwError = NOERROR;
if (CreateWellKnownSid(WinLowLabelSid, 0, LowLabelSid, &cb))
{
PACL Sacl = (PACL)alloca(cb += sizeof(ACL) + sizeof(ACE_HEADER) + sizeof(ACCESS_MASK));
if (InitializeAcl(Sacl, cb, ACL_REVISION) &&
AddMandatoryAce(Sacl, ACL_REVISION, 0, 0, LowLabelSid))
{
SECURITY_DESCRIPTOR sd;
InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION);
SetSecurityDescriptorDacl(&sd, TRUE, NULL, FALSE);
SetSecurityDescriptorSacl(&sd, TRUE, Sacl, FALSE);
SECURITY_ATTRIBUTES sa = { sizeof(sa), &sd, TRUE };
// todo something here
}
else
{
dwError = GetLastError();
}
}
else
{
dwError = GetLastError();
}
But again, you need to understand that there is (almost) never any sense in creating a security descriptor for an unnamed object. The security descriptor is only checked when opening an object, and (in user mode) you cannot open an object that does not have a name.
(From kernel mode we can open an object by pointer using ObOpenObjectByPointer.)
(In older versions of Windows, CreatePipe actually created a pipe with a random name, but starting from Windows 7 the pipe really is unnamed, so it cannot be opened with CreateFile or any similar method.)
In any case, it is my opinion that using CreatePipe in this context was a bad choice. This function is not well designed and has too few parameters. There is no option to create a bidirectional pipe or to open the pipe in asynchronous mode. I think it is better to use CreateNamedPipeW and CreateFileW.
(Alternatively, from Windows 7 onwards, you can use ZwCreateNamedPipeFile and ZwOpenFile to create and open an unnamed pipe.)
The proximate problem with the code as posted is that SetSecurityInfo and SetKernelObjectSecurity return ERROR_ACCESS_DENIED when called with the handle returned by CreatePipe. This is because, as described in the documentation for LABEL_SECURITY_INFORMATION:
Right required to set: WRITE_OWNER
Since CreatePipe does not give you the option to select the access rights that the handles are opened with, you have no way of doing this. If you instead use CreateNamedPipe you can set WRITE_OWNER in dwOpenMode.
However, you should note that if you wish to create an object with a special security descriptor, it is preferable to provide that security descriptor when the object is created. There is no point in creating the object with a default security descriptor and then changing it; why do in two operations what you can do in one? In this case, the SECURITY_ATTRIBUTES structure you pass to CreatePipe or CreateNamedPipe can be used to specify the security descriptor, providing another way of addressing your immediate problem, although as previously mentioned this will not in fact be useful.
I am writing an application that will run as a service and will pick system proxy settings for communication with the outside world .I tried WinHttpGetIEProxyConfigForCurrentUser but it fails to retrieve proxy settings for current user which is obvious since service is running under the domain of Local System. To realize the same I have to made the user to logon to the service which I don't want application user to do. I read about SERVICE_USER_OWN_PROCESS parameter which can be passed to CreateService() but neither I found its declaration in WinNT.h nor I am sure if it will work. Development of application is stuck because of this issue. Can anybody please help.
I had similar situation with running a program in the context of the current user.
First of all - somewhere in a global scope - define following variables:
HANDLE hUsrToken;
HANDLE hDupToken;
int sessionId;
You need to get the session Id of the current user:
int getInteractiveSessinoId()
{
PWTS_SESSION_INFO pSessInfo;
ulong count; // Number of current user sessions
int result = -1;
if (!WTSEnumerateSessions(WTS_CURRENT_SERVER, 0, 1, &pSessInfo, &count))
{
printf("Getting session information failed with error %d\n", << GetLastError());
WTSFreeMemory(pSessInfo);
return -2;
}
for (ulong loop = 0; loop < count; loop++)
{
if (pSessInfo[loop].State == WTSActive)
{
printf("Session %d is currently active\n", pSessInfo[loop].SessionId);
result = pSessInfo[loop].SessionId;
break;
}
}
WTSFreeMemory(pSessInfo);
return result;
}
Next you need to impersonate the current user (I called it "attach to the session"):
bool attachToSession(int sessionId)
{
// We need to duplicate the token of the session's user
if (!WTSQueryUserToken(sessionId, &hUsrToken))
{
pritnf("Query the user token failed with error %d\n", GetLastError());
return false;
}
if (!DuplicateTokenEx(hUsrToken, TOKEN_ALL_ACCESS, NULL, SecurityImpersonation, TokenImpersonation , &hDupToken))
{
printf("Duplicating a token failed with error %d\n", GetLastError());
return false;
}
if (!ImpersonateLoggedOnUser(hDupToken))
{
printf("Impersonating the user failed with error %d\n", GetLastError();
return false;
}
return true;
}
Now do whatever you want to do want to do within the current user's context, when done, revert (or "detach") from the user's context:
bool detachFromSession()
{
if (!RevertToSelf())
{
printf("Reverting the token failed with error %d\n", GetLastError());
return false;
}
(void)CloseHandle(hDupToken);
(void)CloseHandle(hUsrToken);
return true;
}
I'm not sure if it is good idea to apply the token to the current (service) thread. I think it is a better idea to create a new thread which executes whatever you want to do in the user's context and apply the impersonated token to it. So your code part may look like this:
HANDLE hUsrToken;
HANDLE hDupToken;
HANDLE hThread;
int sessionId;
DWORD threadId;
DWORD WINAPI MyThreadFunction(LPVOID lpParam)
{
// WinHttpGetIEProxyConfigForCurrentUser(...
}
sessionId = getInterActiveSessionId();
if (attachToSession(int sessionId) == false)
{
// Error handling
return;
}
hThread = CreateThread(NULL, // default security attributes
0, // use default stack size
MyThreadFunction, // thread function name
NULL, // argument to thread function
CREATE_SUSPENDED, // Delay execution
&threadId);
if (SetThreadToken(hThread, hDupToken) == false)
{
// Error handling
return;
}
ResumeThread(hThread);
I cannot garantee that this will resolve your problem, but I hope it does. Good luck!
I asked this in CreateDesktop() with Vista UAC (C Windows)
I set a bounty but in trying to vote down the only answer the "accept" was pressed by mistake (i've been awake for more than 48 hs). so I am asking it again.
I'm using CreateDesktop() to create a temporary desktop where an application will run, perform a cleanup action (while remaining out of the way) and terminate. I'm closing that desktop once the application is gone. Everything is fine when using Windows XP and even Vista. The problem arises when you enable the (annoying) UAC.
Everything is OK when you create a desktop, but when you call CreateProcess() to open a program on that desktop it causes the opened application to crash with an exception on User32.dll.
I've been reading a lot about the different desktops and layers on Windows and the restrictions of memory. However, most of the programs I open (as test scenarios) are OK, but a few (like IE, Notepad, Calc and my own application) cause the crash.
Anyone has any idea why this happen on Vista with UAC, or more specifically for those specific programs? and how to fix this?
Anyone has a good solid example on how to create a desktop and open an application there without switching to it under Vista with UAC on?
Code is appreciated.
Thanks
The code used is
SECURITY_ATTRIBUTES sa;
HDESK dOld;
HDESK dNew;
BOOL switchdesk, switchdesk2, closedesk;
int AppPid;
sa.bInheritHandle = TRUE;
sa.lpSecurityDescriptor = NULL;
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
//Get handle to current desktop
dOld = OpenDesktopA("default", 0, TRUE, DESKTOP_SWITCHDESKTOP|
DESKTOP_WRITEOBJECTS|
DESKTOP_READOBJECTS|
DESKTOP_ENUMERATE|
DESKTOP_CREATEWINDOW|
DESKTOP_CREATEMENU);
if(!dOld)
{
printf("Failed to get current desktop handle !!\n\n");
return 0;
}
//Make a new desktop
dNew = CreateDesktopA("kaka", 0, 0, 0, DESKTOP_SWITCHDESKTOP|
DESKTOP_WRITEOBJECTS|
DESKTOP_READOBJECTS|
DESKTOP_ENUMERATE|
DESKTOP_CREATEWINDOW|
DESKTOP_CREATEMENU, &sa);
if(!dNew)
{
printf("Failed to create new desktop !!\n\n");
return 0;
}
AppPid = PerformOpenApp(SomeAppPath);
if(AppPid == 0)
{
printf("failed to open app, err = %d\n", GetLastError());
}
else
{
printf("App pid = %d\n", AppPid);
}
closedesk = CloseDesktop(dNew);
if(!closedesk)
{
printf("Failed to close new desktop !!\n\n");
return 0;
}
return 0;
The correct solution is given as a short comment by ChristianWimmer above:
The desktop must have a security descriptor that allows access to lower integrity level like IE has. Otherwise the GUI cannot access the desktop. – ChristianWimmer Jul 22 '10 at 17:00
Since the answer is a little bit hidden and there's no source code example, let me state it clearly here:
If IE runs in protected mode then the browser tabs are created as low integrity processes. The low integrity tab process will fail to initialize if the desktop does not have a low integrity mandatory label.
As a consequence, the main IE process terminates, too. An interesting observation is that if you start IE providing a command line URL from the secure zone, then IE will succeed to start, because protected mode is disabled by default for the secure zone.
I checked the integrity level of the default desktop, and indeed I was able to verify that the default desktop has a low integrity level! So the easiest solution to the problem is to (1) create the new desktop, (2) get the mandatory label from the default desktop, and (3) copy it into the new desktop. For (2) and (3), you can use the following code
PACL pSacl;
PSECURITY_DESCRIPTOR pSecurityDescriptor;
DWORD dwResult;
dwResult = GetSecurityInfo(hDefaultDesktop, SE_WINDOW_OBJECT, LABEL_SECURITY_INFORMATION, NULL, NULL, NULL, &pSacl, &pSecurityDescriptor);
if (dwResult == ERROR_SUCCESS) {
if (pSacl != NULL) {
dwResult = SetSecurityInfo(hNewDesktop, SE_WINDOW_OBJECT, LABEL_SECURITY_INFORMATION, NULL, NULL, NULL, pSacl);
if (dwResult != ERROR_SUCCESS)
_tprintf(_T("SetSecurityInfo(hNewDesktop) failed, error = %d"), dwResult);
}
LocalFree(pSecurityDescriptor);
} else {
_tprintf(_T("GetSecurityInfo(hDefaultDesktop) failed, error = %d"), dwResult);
}
#CristianWimmer: Thanks for providing the hint to the correct solution. This saved my a lot of time!!
You appear to have come across a bug in IE as it interacts with UAC. If protected mode is set to on you cannot run IE as an ordinary user in any desktop except the default one. In order to run IE in an alternate desktop you must be running as administrator or have protected mode set to off. This is true for Vista, W2K8 and Win7.
As to the other programs that you cannot run, unfortunately I can't confirm anything. I tried upwards of thirty different programs including notepad, calc, all the office apps, visual studio 2005, 2008 and 2010, MSDN help and a number of others and all worked as expected with the noted exception of IE. Is there something truly unusual about your app that might make it behave in an unexpected manner?
One note - if you attempt to run an application like this that needs elevation (such as regedit, etc.) it will fail in CreateProcess with the last error set to ERROR_ELEVATION_REQUIRED.
For your reference, in case I'm doing something different from you, the code I used is:
#ifndef _WIN32_WINNT // Specifies that the minimum required platform is Windows Vista.
#define _WIN32_WINNT 0x0600 // Change this to the appropriate value to target other versions of Windows.
#endif
#include <stdio.h>
#include <tchar.h>
#include "windows.h"
HANDLE PerformOpenApp(TCHAR* appPath);
int _tmain(int argc, _TCHAR* argv[])
{
HDESK dNew;
BOOL closedesk;
HANDLE hApp;
//Make a new desktop
dNew = CreateDesktop(_T("kaka"), 0, 0, 0, DESKTOP_SWITCHDESKTOP|
DESKTOP_WRITEOBJECTS|
DESKTOP_READOBJECTS|
DESKTOP_ENUMERATE|
DESKTOP_CREATEWINDOW|
DESKTOP_CREATEMENU, NULL);
if(!dNew)
{
_tprintf(_T("Failed to create new desktop !!\n\n"));
return 0;
}
TCHAR path[MAX_PATH];
_putts(_T("Enter the path of a program to run in the new desktop:\n"));
_getts(path);
while(_tcslen(path) > 0)
{
hApp = PerformOpenApp(path);
if(hApp == 0)
{
_tprintf(_T("Failed to open app, err = %d\n"), GetLastError());
}
else
{
_tprintf(_T("App pid = %d\n"), GetProcessId(hApp));
_putts(_T("Press any key to close the app.\n"));
_gettchar();
TerminateProcess(hApp, 0);
CloseHandle(hApp);
}
_putts(_T("Enter the path of a program to run in the new desktop:\n"));
_getts(path);
}
closedesk = CloseDesktop(dNew);
if(!closedesk)
{
_tprintf(_T("Failed to close new desktop !!\n\n"));
return 0;
}
return 0;
}
HANDLE PerformOpenApp(TCHAR* appPath)
{
STARTUPINFO si = {0};
PROCESS_INFORMATION pi;
si.cb = sizeof(si);
si.lpDesktop = _T("kaka");
BOOL retVal = CreateProcess(NULL, appPath, NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL,
NULL, &si, &pi);
if (retVal)
{
CloseHandle(pi.hThread);
}
return pi.hProcess;
}