Skip to content

Commit 09dc2a2

Browse files
authored
Merge pull request step-security#91 from swarit-stepsecurity/swarit/fix/windows-sys-schedule
fix(windows): run scheduled task as logged-in user via /ru INTERACTIVE
2 parents 30ed51d + 10f2422 commit 09dc2a2

2 files changed

Lines changed: 25 additions & 4 deletions

File tree

internal/schtasks/schtasks.go

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,21 @@ func Install(exec executor.Executor, log *progress.Logger) error {
4242
return fmt.Errorf("creating log directory: %w", err)
4343
}
4444

45+
// For admin installs the log dir lives at C:\ProgramData\StepSecurity, which
46+
// inherits ACLs from C:\ProgramData and only grants non-admin users
47+
// Read & Execute on the files inside. The /ru INTERACTIVE task fires under
48+
// whatever user is logged on — typically a non-admin developer — and
49+
// cmd.exe's `>>` redirect to agent.log would fail with Access Denied, which
50+
// aborts the whole task action. Grant BUILTIN\Users (SID 545) Modify rights
51+
// on the log dir, propagated to files and subfolders, so any logged-in
52+
// user can append to the log files.
53+
if exec.IsRoot() {
54+
_, _, _, icaclsErr := exec.Run(ctx, "icacls", logDir, "/grant", "*S-1-5-32-545:(OI)(CI)M", "/Q")
55+
if icaclsErr != nil {
56+
log.Warn("could not adjust log dir ACLs (%v) — non-admin users may not be able to write to %s", icaclsErr, logDir)
57+
}
58+
}
59+
4560
args := buildCreateArgs(binaryPath, logDir, hours, exec.IsRoot())
4661
log.Debug("schtasks create: binary=%q log_dir=%q hours=%d is_admin=%v", binaryPath, logDir, hours, exec.IsRoot())
4762

@@ -101,7 +116,13 @@ func buildCreateArgs(binaryPath, logDir string, hours int, isAdmin bool) []strin
101116
args := []string{"/create", "/tn", taskName, "/tr", taskCmd,
102117
"/sc", "HOURLY", "/mo", strconv.Itoa(hours), "/f"}
103118
if isAdmin {
104-
args = append(args, "/ru", "SYSTEM")
119+
// /ru INTERACTIVE binds the task to the NT AUTHORITY\INTERACTIVE
120+
// well-known group (SID S-1-5-4) so it fires under the security
121+
// context of whoever is interactively logged on at trigger time —
122+
// picking up their HKCU, %USERPROFILE%, and PATH. /ru SYSTEM would
123+
// run as NT AUTHORITY\SYSTEM, which can't see any of the user-scoped
124+
// data the scanner depends on.
125+
args = append(args, "/ru", "INTERACTIVE")
105126
}
106127
return args
107128
}

internal/schtasks/schtasks_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -122,13 +122,13 @@ func TestBuildCreateArgs_Admin(t *testing.T) {
122122
for i, a := range args {
123123
if a == "/ru" && i+1 < len(args) {
124124
foundRU = true
125-
if args[i+1] != "SYSTEM" {
126-
t.Errorf("expected /ru SYSTEM, got /ru %s", args[i+1])
125+
if args[i+1] != "INTERACTIVE" {
126+
t.Errorf("expected /ru INTERACTIVE, got /ru %s", args[i+1])
127127
}
128128
}
129129
}
130130
if !foundRU {
131-
t.Error("expected /ru SYSTEM for admin install")
131+
t.Error("expected /ru INTERACTIVE for admin install")
132132
}
133133
}
134134

0 commit comments

Comments
 (0)