I have a process (let's call it ProcessX) that runs by default with only Terminate
, Synchronize
, and Query Limited Information
permissions.
When I look at ProcessX in Process Hacker, I can see the permission (ACE, Owner, etc). I can see that I'm the owner of ProcessX, I can see the 3 limited permissions associated with it (Terminate
, Synchronize
, and Query Limited Information
), and I can even edit the permissions (for instance, set Full Control
on it).
However, when I run the code below to check the DACL of ProcessX, with the same user that owns ProcessX, I'm getting an error code 5 (Access Denied) on the GetSecurityInfo()
function.
Same results with AccessChk and Process Explorer on ProcessX.
However, Process Hacker is perfectly able to read the DACL of ProcessX and modify it.
I don't understand. How is that possible? Why is my code unable to read the DACL for ProcessX?
I've read in MS documents that to read the DACL, I must use OpenProcess()
with READ_CONTROL
.
But READ_CONTROL
is not an available ACE on the process for my user. So, I can't open the process with it (OpenProcess()
errors if I try, which is logical).
So, I'm the owner of the process, but I can't modify the ACE, but Process Hacker can.
Can anybody help me understand?
#include <windows.h>
#include <accctrl.h>
#include <aclapi.h>
#include <winnt.h>
#include <stdio.h>
#include <lmcons.h>
DWORD AddAceToObjectsSecurityDescriptor (
HANDLE pszObjName, // name of object
SE_OBJECT_TYPE ObjectType, // type of object
LPTSTR pszTrustee, // trustee for new ACE
TRUSTEE_FORM TrusteeForm, // format of trustee structure
DWORD dwAccessRights, // access mask for new ACE
ACCESS_MODE AccessMode, // type of ACE
DWORD dwInheritance // inheritance flags for new ACE
)
{
DWORD dwRes = 0;
PACL pOldDACL = NULL, pNewDACL = NULL;
PSECURITY_DESCRIPTOR pSD = NULL;
EXPLICIT_ACCESS ea;
if (NULL == pszObjName)
return ERROR_INVALID_PARAMETER;
//retrieve user
char username[UNLEN 1];
DWORD username_len = UNLEN 1;
GetUserName(username, &username_len);
printf(username);
// Get a pointer to the existing DACL.
dwRes = GetSecurityInfo(pszObjName, ObjectType,
DACL_SECURITY_INFORMATION,
NULL, NULL, &pOldDACL, NULL, &pSD);
if (ERROR_SUCCESS != dwRes) {
printf( "GetSecurityInfo Error %u\n", dwRes );
goto Cleanup;
}
// Initialize an EXPLICIT_ACCESS structure for the new ACE.
ZeroMemory(&ea, sizeof(EXPLICIT_ACCESS));
ea.grfAccessPermissions = dwAccessRights;
ea.grfAccessMode = AccessMode;
ea.grfInheritance= dwInheritance;
ea.Trustee.TrusteeForm = TrusteeForm;
ea.Trustee.ptstrName = pszTrustee;
// Create a new ACL that merges the new ACE
// into the existing DACL.
dwRes = SetEntriesInAcl(1, &ea, pOldDACL, &pNewDACL);
if (ERROR_SUCCESS != dwRes) {
printf( "SetEntriesInAcl Error %u\n", dwRes );
goto Cleanup;
}
// Attach the new ACL as the object's DACL.
dwRes = SetSecurityInfo(pszObjName, ObjectType,
DACL_SECURITY_INFORMATION,
NULL, NULL, pNewDACL, NULL);
if (ERROR_SUCCESS != dwRes) {
printf( "SetSecurityInfo Error %u\n", dwRes );
goto Cleanup;
}
Cleanup:
if(pSD != NULL)
LocalFree((HLOCAL) pSD);
if(pNewDACL != NULL)
LocalFree((HLOCAL) pNewDACL);
return dwRes;
}
int main(int argc, char *argv[])
{
int pid = atoi(argv[1]);
printf("[ ] Ensuring we have the proper privs....\n");
HANDLE self = OpenProcess(
PROCESS_TERMINATE | PROCESS_QUERY_LIMITED_INFORMATION | SYNCHRONIZE,
FALSE, (DWORD) pid);
if(self != NULL){
printf("process open !\n");
AddAceToObjectsSecurityDescriptor(self, SE_KERNEL_OBJECT, (LPSTR)"S-1-5-21-BLABLALEAKBLALBLA",TRUSTEE_IS_SID, PROCESS_ALL_ACCESS, GRANT_ACCESS, 0);
}
else{
printf("error in opening of the process\n");
}
}
CodePudding user response:
Problem solved !
I've assumed that READ_CONTROL
will be refused because it wasn't available on the DACL of the running process.
Turns out, when you own an object, you have implicit READ_CONTROL
and WRITE_DAC
permission on it, even if zero ACE are set on the object for the user owning it.
#include <windows.h>
#include <accctrl.h>
#include <aclapi.h>
#include <winnt.h>
#include <stdio.h>
#include <lmcons.h>
DWORD AddAceToObjectsSecurityDescriptor (
HANDLE pszObjName, // name of object
SE_OBJECT_TYPE ObjectType, // type of object
LPTSTR pszTrustee, // trustee for new ACE
TRUSTEE_FORM TrusteeForm, // format of trustee structure
DWORD dwAccessRights, // access mask for new ACE
ACCESS_MODE AccessMode, // type of ACE
DWORD dwInheritance // inheritance flags for new ACE
)
{
DWORD dwRes = 0;
PACL pOldDACL = NULL, pNewDACL = NULL;
PSECURITY_DESCRIPTOR pSD = NULL;
EXPLICIT_ACCESS ea;
if (NULL == pszObjName)
return ERROR_INVALID_PARAMETER;
// Get a pointer to the existing DACL.
dwRes = GetSecurityInfo(pszObjName, ObjectType,
DACL_SECURITY_INFORMATION,
NULL, NULL, &pOldDACL, NULL, &pSD);
if (ERROR_SUCCESS != dwRes) {
printf( "GetSecurityInfo Error %u\n", dwRes );
goto Cleanup;
}
// Initialize an EXPLICIT_ACCESS structure for the new ACE.
ZeroMemory(&ea, sizeof(EXPLICIT_ACCESS));
ea.grfAccessPermissions = dwAccessRights;
ea.grfAccessMode = AccessMode;
ea.grfInheritance= dwInheritance;
ea.Trustee.TrusteeForm = TrusteeForm;
ea.Trustee.ptstrName = pszTrustee;
// Create a new ACL that merges the new ACE
// into the existing DACL.
dwRes = SetEntriesInAcl(1, &ea, pOldDACL, &pNewDACL);
if (ERROR_SUCCESS != dwRes) {
printf( "SetEntriesInAcl Error %u\n", dwRes );
goto Cleanup;
}else{
printf("SetEntriesAcl SUCCESS !\n");
}
// Attach the new ACL as the object's DACL.
dwRes = SetSecurityInfo(pszObjName, ObjectType,
DACL_SECURITY_INFORMATION,
NULL, NULL, pNewDACL, NULL);
if (ERROR_SUCCESS != dwRes) {
printf( "SetSecurityInfo Error %u\n", dwRes );
goto Cleanup;
}else{
printf("SetSecurityInfo SUCCESS \n");
}
Cleanup:
if(pSD != NULL)
LocalFree((HLOCAL) pSD);
if(pNewDACL != NULL)
LocalFree((HLOCAL) pNewDACL);
return dwRes;
}
int main(int argc, char *argv[])
{
int pid = atoi(argv[1]);
DWORD dwRes = 0;
char username[UNLEN 1];
DWORD username_len = UNLEN 1;
GetUserName(username, &username_len);
HANDLE self = OpenProcess(READ_CONTROL | WRITE_DAC, FALSE, (DWORD) pid);
if(self != NULL){
printf("process open !\n");
AddAceToObjectsSecurityDescriptor(self, SE_KERNEL_OBJECT, (LPSTR)username,TRUSTEE_IS_NAME, PROCESS_ALL_ACCESS, GRANT_ACCESS, 0);
}
else{
dwRes = GetLastError();
printf("error in opening of the process. Code %u \n", dwRes);
}
}