Skip to content

os/user: user.Current() returns wrong user for NT AUTHORITY/SYSTEM #73471

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
petemoore opened this issue Apr 22, 2025 · 6 comments
Open

os/user: user.Current() returns wrong user for NT AUTHORITY/SYSTEM #73471

petemoore opened this issue Apr 22, 2025 · 6 comments
Assignees
Labels
BugReport Issues describing a possible bug in the Go implementation. NeedsDecision Feedback is required from experts, contributors, and/or the community before a change can be made. OS-Windows

Comments

@petemoore
Copy link

Go version

go version go1.24.2 windows/amd64

Output of go env in your module/workspace:

set AR=ar
set CC=gcc
set CGO_CFLAGS=-O2 -g
set CGO_CPPFLAGS=
set CGO_CXXFLAGS=-O2 -g
set CGO_ENABLED=0
set CGO_FFLAGS=-O2 -g
set CGO_LDFLAGS=-O2 -g
set CXX=g++
set GCCGO=gccgo
set GO111MODULE=
set GOAMD64=v1
set GOARCH=amd64
set GOAUTH=netrc
set GOBIN=
set GOCACHE=C:\Users\pmoore\AppData\Local\go-build
set GOCACHEPROG=
set GODEBUG=
set GOENV=C:\Users\pmoore\AppData\Roaming\go\env
set GOEXE=.exe
set GOEXPERIMENT=
set GOFIPS140=off
set GOFLAGS=
set GOGCCFLAGS=-m64 -fno-caret-diagnostics -Qunused-arguments -Wl,--no-gc-sections -fmessage-length=0 -ffile-prefix-map=C:\Users\pmoore\AppData\Local\Temp\go-build4283846930=/tmp/go-build -gno-record-gcc-switches
set GOHOSTARCH=amd64
set GOHOSTOS=windows
set GOINSECURE=
set GOMOD=NUL
set GOMODCACHE=C:\Users\pmoore\go\pkg\mod
set GONOPROXY=
set GONOSUMDB=
set GOOS=windows
set GOPATH=C:\Users\pmoore\go
set GOPRIVATE=
set GOPROXY=https://proxy.golang.org,direct
set GOROOT=C:\Program Files\Go
set GOSUMDB=sum.golang.org
set GOTELEMETRY=local
set GOTELEMETRYDIR=C:\Users\pmoore\AppData\Roaming\go\telemetry
set GOTMPDIR=
set GOTOOLCHAIN=auto
set GOTOOLDIR=C:\Program Files\Go\pkg\tool\windows_amd64
set GOVCS=
set GOVERSION=go1.24.2
set GOWORK=
set PKG_CONFIG=pkg-config

What did you do?

  • Start with a fresh Windows 11 VM
  • Install go 1.24.2
  • Install PSTools (standard Windows tools from MS)
  • Create a test go program
package main

import (
        "fmt"
        "os/user"
)

func main() {
        u, err := user.Current()
        if err != nil {
                panic(err)
        }
        fmt.Printf("%v\n", u.Username)
}
  • Create a calling batch script
whoami
go run <go program above>
pause
  • Execute the above script as LocalSystem user
PsExec -i -s <above batch script>

What did you see happen?

The output of whoami was: nt authority\system
The output of the go program was: WORKGROUP\pmoore$.

What did you expect to see?

I expected to see output of both whoami and the go program to be nt authority\system.

@seankhliao seankhliao changed the title os/user: [windows] user.Current() returns wrong user for NT AUTHORITY/SYSTEM os/user: user.Current() returns wrong user for NT AUTHORITY/SYSTEM Apr 22, 2025
@seankhliao seankhliao added OS-Windows NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. labels Apr 22, 2025
@gabyhelp gabyhelp added the BugReport Issues describing a possible bug in the Go implementation. label Apr 22, 2025
@qmuntal qmuntal self-assigned this Apr 22, 2025
@qmuntal
Copy link
Member

qmuntal commented Apr 23, 2025

Thanks for reporting, I can reproduce this issue, even not using PsExec but a plain Windows Service. Note that this only reproduces when the logged in user is system service account. If a normal user is specified, then the correct name is returned.

I'm preparing a fix. Edit: not sure if the new behavior is really a bug.

Window's GetUserNameW says that it returns the host name (in your case WORKGROUP\pmoore) followed by a dollar sign ($) when the process is running as service account when on Windows Vista or higher, and nt authority\system (or equivalent) on previous versions. I've tested with C# Environment.UserName and it also returns the hostname+$.

It looks like that the Windows ecosystem is moving into the hostname+$ direction, so the new behavior might be de desired one. whoami is stuck in the past, it is not be a good baseline in this case. Regardless, WORKGROUP\pmoore$ is not the wrong name for a local system account, given that it comes directly from a Windows API and that it ending with a dollar sign allows differentiating it form the real user name.

For the record, this behavior change happened in CL 597255. It replaced a really slow and unreliable call to LookupAccountSid for GetUserNameW, which is faster and works on more situations.

@alexbrainman @golang/windows thoughts?

@qmuntal qmuntal added NeedsFix The path to resolution is known, but the work has not been done. NeedsDecision Feedback is required from experts, contributors, and/or the community before a change can be made. and removed NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. NeedsFix The path to resolution is known, but the work has not been done. labels Apr 23, 2025
@petemoore
Copy link
Author

petemoore commented Apr 23, 2025

Many thanks @qmuntal for the quick investigation.

I have to confess not being too familiar with Windows accounts, but chatgpt suggests that these are in fact different accounts (LocalSystem vs Machine Account). It appears they are related, in that LocalSystem can access network resources using the Machine Account, but they are distinct accounts with unique SIDs.

@qmuntal
Copy link
Member

qmuntal commented Apr 23, 2025

I have to confess not being too familiar with Windows accounts, but chatgpt suggests that these are in fact different accounts (LocalSystem vs Machine Account).

Can't access the chatgpt prompt.

It appears they are related, in that LocalSystem can access network resources using the Machine Account, but they are distinct accounts with unique SIDs.

user.Current() returns the SID and HomeDir of the system account, not from the machine account, even when executed within PsExec -i -s.

@petemoore
Copy link
Author

Can't access the chatgpt prompt.

Apologies! Here it is:

Excellent question, Pete — these two identities, NT AUTHORITY\SYSTEM and WORKGROUP\<computername>$ (or <hostname>$), look superficially similar in logs or ACLs, but they are very different system accounts in Windows, and only one of them is the LocalSystem account.

Let’s break it down clearly:


NT AUTHORITY\SYSTEM – The LocalSystem Account

  • This is the LocalSystem account.

  • It’s a built-in Windows account used by services that need unrestricted access to the local machine.

  • Also known as SYSTEM or LocalSystem, it's part of the NT AUTHORITY namespace.

  • It has full privileges over the local system and acts as the computer on the network (in some contexts).

  • Used to run core Windows services (e.g., winlogon, lsass, etc.).

Key traits:

  • SID: S-1-5-18

  • Can access everything on the local machine

  • Can authenticate to other machines as the computer account (see next item...)


WORKGROUP\<hostname>$ – The Computer Account

  • This is the machine account, also called the computer identity.

  • It is not a user, but a security principal used by the OS when authenticating to other systems on a network.

  • The $ at the end is the giveaway — it marks this as a computer account, not a user account.

  • Typically used when the system connects to another system over SMB, RPC, etc.

Key traits:

  • Appears when the computer authenticates as itself to another host

  • Might show up in logs as a network identity or in ACL evaluations

  • The SID is distinct (not S-1-5-18), and permissions are limited


✅ So… are they the same thing?

Nope. Here's the difference:

Account SID Description
NT AUTHORITY\SYSTEM S-1-5-18 The LocalSystem account used to run services with full local privilege
$ Varies per machine The machine/computer account used for network authentication

@alexbrainman
Copy link
Member

@qmuntal

It looks like that the Windows ecosystem is moving into the hostname+$ direction, so the new behavior might be de desired one. whoami is stuck in the past, it is not be a good baseline in this case. ...

I don't have an opinion about what to choose here. I am stuck in the past together with whoami command. :-)

For the record, this behavior change happened in CL 597255. It replaced a really slow and unreliable call to LookupAccountSid for GetUserNameW, which is faster and works on more situations.

It is actually changed in CL 605135 after some reverts. And you can keep existing code, and just hard code some user names based on the account SIDs (if SIDs are correct).

You can also add new tests to the golang.org/x/sys/windows/svc.TestExample to verify that os/user.Current() returns correct user name unless you have simpler way.

Up to you.

Alex

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
BugReport Issues describing a possible bug in the Go implementation. NeedsDecision Feedback is required from experts, contributors, and/or the community before a change can be made. OS-Windows
Projects
None yet
Development

No branches or pull requests

5 participants