-
Notifications
You must be signed in to change notification settings - Fork 17.8k
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
os/exec: return error when PATH lookup would use current directory #43724
Comments
As a part time Windows user, I don't think I expect |
I don't understand why the const short = "prog"
actual := short
if abs, err := exec.LookPath(short); errors.Is(err, exec.ErrDot) {
actual = abs
}
cmd := exec.Command(actual)
cmd.Args[0] = short ? |
Thank you for the detailed description, @rsc! If this proposal would be adopted, how should we write the following code so it would be safe to run inside an untrusted directory? c := exec.Command("git", "status")
s, err := c.Output() With this proposal, if the current directory has a I understand that this proposal is meant to prevent silent failures in programs that relied on the Windows behavior. I'm just wondering, if this proposal gets accepted, what are our options for running commands in untrusted directories (i.e. so that the contents of the current directory is entirely ignored)? Might this proposal ship in tandem with #42420? |
For some additional context, I recently updated to Go 1.15.7 (which includes the security patch for this issue) and it broke build-time scripts that run on Mac and/or Linux systems. We have a build process where we intentionally Unfortunately for me, running
The suggested workaround is to prepend While I fully understand the security concern here, I take issue with the premise that all situations that involve executing a binary that resides within the current directory are invalid. With this change to Go, I now have approximately 800 |
@dylan-bourque wouldn't using the absolute path to the bin directory be a more proper fix for your situation? # assuming this is set from tooling in the root of the project
export PATH=$(pwd)/bin:$PATH |
@seankhliao your assumption is correct. We do The issue with injecting an absolute path into those As I said, it seems like updating those directives to use |
@dylan-bourque,
|
(And note that we currently use exactly that approach in the Go standard library: see https://golang.org/cl/261499.) |
All of mine weren't sensitive to That is no longer the case with Go 1.15.7, though. The same |
I see where my confusion was now. As a test, I added Even with this new information, though, I still have hundreds of I think my point stands that not every invocation of a command within the current directory should be treated as malicious, though. |
@rsc This proposal helps for the case where you want to catch the case of a local executable file. But it doesn't help for the case where you want to Concrete example: In my Update: It looks like my only option is to vendor both Update 2:
Do I also have to vendor that |
@dolmen, yes, there is no "do a non-standard PATH lookup". There is only "do the standard PATH lookup and refuse an insecure result". As noted above, the problem with "do a non-standard PATH lookup" is that it silently differs from the standard system behavior, which will cause mystery and confusion.
Those same users really probably should delete go.bat. If your goeval doesn't trip over it, something else will. I think it would be fine, as an independent change, to respect the presence of the NoDefaultCurrentDirectoryInExePath environment variable. If that variable were found in the environment, then LookPath would never look in dot and the security check would never trigger. |
@bcmills, cmd.Err is not strictly necessary - users can call LookPath themselves - but it is error-prone to assume the result of LookPath will be valid input to exec.Command. In general that's not true, since LookPath returns a clean path, so Also, there are now two packages (execabs and safeexec) that function as exec.Command-workalikes and are hampered in that by not being able to set Err themselves. Safeexec uses a not-as-nice API and execabs uses ugly reflection. Better to expose Err and let any future wrappers do what they need to do. That field (lookPathErr) is the only setup field in exec.Command that's not exported. |
@dylan-bourque It sounds like somehow you have
Why not instead write
? |
@rsc what we have are a bunch of For a more concrete example, one of those tools is With the security fix in Go 1.14.4 and 1.15.7, we're now looking at having to update a significant number of our My point here is that we are intentionally placing an executable within the current directory and explicitly configuring the environment such that that binary will be invoked (just like anyone would do in a shell script, etc.). This proposed change to treat any binary within $PWD as malicious will, in my opinion at least, break valid use cases. |
If $PWD is not an absolute path due to some mistake in |
@rsc That (making |
Change https://go.dev/cl/414054 mentions this issue: |
… explicit one If the current directory is also listed explicitly in %PATH%, this changes the behavior of LookPath to prefer the explicit name for it (and thereby avoid ErrDot). However, in order to avoid running a different executable from what would have been run by previous Go versions, we still return the implicit path (and ErrDot) if it refers to a different file entirely. Fixes #53536. Updates #43724. Change-Id: I7ab01074e21a0e8b07a176e3bc6d3b8cf0c873cd Reviewed-on: https://go-review.googlesource.com/c/go/+/414054 Reviewed-by: Russ Cox <[email protected]> Run-TryBot: Bryan Mills <[email protected]> Reviewed-by: Ian Lance Taylor <[email protected]> Reviewed-by: Alex Brainman <[email protected]> TryBot-Result: Gopher Robot <[email protected]>
Change https://go.dev/cl/419794 mentions this issue: |
The changes are likely to break users, and we need to make it easy to unbreak without code changes. For #43724. Fixes #53962. Change-Id: I105c5d6c801d354467e0cefd268189c18846858e Reviewed-on: https://go-review.googlesource.com/c/go/+/419794 Reviewed-by: Bryan Mills <[email protected]> Reviewed-by: Ian Lance Taylor <[email protected]> TryBot-Result: Gopher Robot <[email protected]> Run-TryBot: Russ Cox <[email protected]>
… explicit one If the current directory is also listed explicitly in %PATH%, this changes the behavior of LookPath to prefer the explicit name for it (and thereby avoid ErrDot). However, in order to avoid running a different executable from what would have been run by previous Go versions, we still return the implicit path (and ErrDot) if it refers to a different file entirely. Fixes golang#53536. Updates golang#43724. Change-Id: I7ab01074e21a0e8b07a176e3bc6d3b8cf0c873cd Reviewed-on: https://go-review.googlesource.com/c/go/+/414054 Reviewed-by: Russ Cox <[email protected]> Run-TryBot: Bryan Mills <[email protected]> Reviewed-by: Ian Lance Taylor <[email protected]> Reviewed-by: Alex Brainman <[email protected]> TryBot-Result: Gopher Robot <[email protected]>
The changes are likely to break users, and we need to make it easy to unbreak without code changes. For golang#43724. Fixes golang#53962. Change-Id: I105c5d6c801d354467e0cefd268189c18846858e Reviewed-on: https://go-review.googlesource.com/c/go/+/419794 Reviewed-by: Bryan Mills <[email protected]> Reviewed-by: Ian Lance Taylor <[email protected]> TryBot-Result: Gopher Robot <[email protected]> Run-TryBot: Russ Cox <[email protected]>
Fix xcaddy run on windows with go 1.19 See golang/go#43724
Is there a way to bypass this error at runtime in Go 1.18? I was under the impression that this was going to be released in Go 1.19 per the release notes but it looks like it also affects programs compiled with Go 1.18. We've got a few Go services deployed remotely at customer locations that can't self-upgrade due to this change, and we're looking for a way to bypass it temporarily. |
@clarkmcc This change is not in Go 1.18. I suggest that you open a new issue with more details. That said, if your Go 1.18 somehow has the Go 1.19 changes, perhaps it also has the documented |
This appears to cause substantial problems for go binaries that are run by Bazel and run subprocesses of their own because the directories in This appears to be blocking the use of gqlgen in Bazel because |
@Manbeardo Please open a new issue about these concerns. Thanks. |
We recently published a new package golang.org/x/sys/execabs, which is a forwarding wrapper around os/exec that makes three changes:
execabs.LookPath changes the result of exec.LookPath in the case where a PATH search returns an executable in the current directory (dot). In that case, execabs.LookPath returns an error instead. The error text is “prog resolves to executable in current directory (./prog)”
execabs.Command changes the result of exec.Command in the same case. It arranges that the subsequent cmd.Run or cmd.Start will return that same error.
execabs.CommandContext does the same.
As yet another possible answer in the “what to do about PATH lookups on Windows” saga, perhaps we should change os/exec to do these things by default. I am filing this proposal to help us discuss and decide whether to take this path. (For more background, see the blog post https://blog.golang.org/path-security.)
The proposal can be summarized as “replace os/exec with golang.org/x/sys/execabs”.
To recap how we got here, the fundamental problem is that lots of Go programs do things like
exec.Command("prog")
and expect that prog will come from a system directory listed in the PATH. It is a surprise and in some cases a security problem that on Windows prog will be taken from dot instead when prog.exe (or prog.bat, prog.com, ...) exists. The same is true on Unix for users who have an explicit dot or empty-string element in their PATH.We already have three issues with possible approaches to solving this problem. They are:
The problem with #38736 is that it silently changes the behavior of programs on Windows. Where
exec.Command("prog")
previously found and ran.\prog.exe
, it would now either silently switch to aprog.exe
from the PATH (surprise!) or return an error thatprog
could not be found (even though it used to be; confusion!). The same is true ofexec.LookPath
.#42420 avoids the problem of changing existing behavior by introducing a new function
exec.LookPathAbs
that never looks in dot. Clearly that doesn’t surprise or confuse anyone. But it also doesn’t fix any security problems.#42950 extends #42420 by changing
exec.Command
to useexec.LookPathAbs
by default. That brings back the surprise and confusion of #38736, but only forexec.Command
and notexec.LookPath
. And compared to #38736, #42420+#42950 has the added complexity of adding new API (LookPathAbs
).None of these are great.
The proposal in this issue, to adopt execabs semantics as the os/exec semantics, fixes the problems. Execabs doesn’t remove dot from the PATH lookup. Instead it reports use of dot as an error. This avoids the surprise of running a different program. And the reported error is very clear about what happened. Instead of a generic “program not found” it gives an error that avoids the confusion:
And of course because programs from the current directory are not being executed, that fixes the security problem too.
The specific changes this issue proposes in os/exec are:
var ErrDot = errors.New("executable in current directory")
LookPath
: if it would have chosen toreturn path, nil
where path is an executable in the current directory found by a PATH lookup, it now doesreturn path, err
whereerr
satisfieserrors.Is(err, ErrDot)
.Cmd
: add a new fieldErr error
which is returned byStart
orRun
if not set. This field replaces the current unexported fieldlookPathErr
.Consider this client code:
With the proposed changes, the code would no longer find and run
./prog
on Unix (when dot is in the PATH), nor.\prog.exe
on Windows (regardless of PATH), addressing the security issue.Programs that want to require the current directory to be used (ignoring PATH) can change
"prog"
to"./prog"
(that works on both Unix and Windows systems). This change ("prog"
to"./prog"
) is compatible with older versions ofos/exec
.Programs that want to allow the use of the current directory in conjunction with PATH can add a few new lines of code, marked with
<<<
:This should give programs the flexibility to opt back into the old behavior when necessary.
Of course, this change (adding the
errors.Is
checks) is not compatible with older versions ofos/exec
, but we expect this need to be rare. We expect most programs that intentionally run programs from the current directory to update to the./prog
form.Windows users, would this proposal break your programs?
You can check today by replacing
with
in your source code. No other changes are needed to get the effect. (Of course, golang.org/x/sys/execabs does not provide
ErrDot
, so theerrors.Is
stanzas cannot be written in this simulation of the proposal.)Thoughts? Comments? Concerns? Thanks very much.
The text was updated successfully, but these errors were encountered: