fix: harden generated Windows wrapper ACLs
This commit is contained in:
@@ -87,10 +87,7 @@ func (manager *execSupervisor) Start(ctx context.Context, spec supervisor.StartS
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
wrapperPath, cleanup, err := materializeWrapper(spec.WorkingDirectory, wrapper, manager.options.Now())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var cleanup func()
|
||||
fail := func(cause error) (supervisor.Process, error) {
|
||||
if cleanup != nil {
|
||||
cleanup()
|
||||
@@ -103,6 +100,12 @@ func (manager *execSupervisor) Start(ctx context.Context, spec supervisor.StartS
|
||||
return fail(err)
|
||||
}
|
||||
defer token.Close()
|
||||
wrapperPath, cleanup, err := materializeWrapper(spec.WorkingDirectory, wrapper, manager.options.Now(), func(path string) error {
|
||||
return secureWrapperFile(path, identity.UserSID)
|
||||
})
|
||||
if err != nil {
|
||||
return fail(err)
|
||||
}
|
||||
baseEnvironment, err := token.Environ(false)
|
||||
if err != nil {
|
||||
return fail(fmt.Errorf("build token environment: %w", err))
|
||||
@@ -264,6 +267,50 @@ func verifyExecutable(path string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// secureWrapperFile replaces the inherited directory ACL with a protected
|
||||
// DACL. The service writes the wrapper before this call; afterward only
|
||||
// LocalSystem and the selected effective token SID can read it. This is done
|
||||
// after token selection so active-user commands do not depend on inherited
|
||||
// broad ProgramData permissions.
|
||||
func secureWrapperFile(path, effectiveSID string) error {
|
||||
systemSID, err := winapi.StringToSid(securitySystemRID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
effective := systemSID
|
||||
if effectiveSID != "" && effectiveSID != securitySystemRID {
|
||||
effective, err = winapi.StringToSid(effectiveSID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
entries := []winapi.EXPLICIT_ACCESS{{
|
||||
AccessPermissions: winapi.GENERIC_ALL,
|
||||
AccessMode: winapi.SET_ACCESS,
|
||||
Trustee: winapi.TRUSTEE{
|
||||
TrusteeForm: winapi.TRUSTEE_IS_SID,
|
||||
TrusteeType: winapi.TRUSTEE_IS_WELL_KNOWN_GROUP,
|
||||
TrusteeValue: winapi.TrusteeValueFromSID(systemSID),
|
||||
},
|
||||
}}
|
||||
if effective != systemSID {
|
||||
entries = append(entries, winapi.EXPLICIT_ACCESS{
|
||||
AccessPermissions: winapi.GENERIC_READ,
|
||||
AccessMode: winapi.SET_ACCESS,
|
||||
Trustee: winapi.TRUSTEE{
|
||||
TrusteeForm: winapi.TRUSTEE_IS_SID,
|
||||
TrusteeType: winapi.TRUSTEE_IS_USER,
|
||||
TrusteeValue: winapi.TrusteeValueFromSID(effective),
|
||||
},
|
||||
})
|
||||
}
|
||||
acl, err := winapi.ACLFromEntries(entries, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return winapi.SetNamedSecurityInfo(path, winapi.SE_FILE_OBJECT, winapi.OWNER_SECURITY_INFORMATION|winapi.DACL_SECURITY_INFORMATION|winapi.PROTECTED_DACL_SECURITY_INFORMATION, systemSID, nil, acl, nil)
|
||||
}
|
||||
|
||||
func createStandardPipes() (*os.File, *os.File, *os.File, *os.File, *os.File, *os.File, error) {
|
||||
security := &winapi.SecurityAttributes{Length: uint32(unsafe.Sizeof(winapi.SecurityAttributes{})), InheritHandle: 1}
|
||||
var stdinReadHandle, stdinWriteHandle winapi.Handle
|
||||
@@ -559,7 +606,7 @@ func openTokenForAttempt(contextName ExecutionContext, candidate *SessionCandida
|
||||
if err != nil {
|
||||
return 0, supervisor.EffectiveIdentity{}, err
|
||||
}
|
||||
return token, supervisor.EffectiveIdentity{Context: string(ContextLocalService), Elevated: false, Integrity: "medium"}, nil
|
||||
return token, supervisor.EffectiveIdentity{Context: string(ContextLocalService), UserSID: securityLocalServiceRID, Elevated: false, Integrity: "medium"}, nil
|
||||
case ContextLocalSystem:
|
||||
return duplicateServiceToken()
|
||||
default:
|
||||
|
||||
Reference in New Issue
Block a user