go: os/exec: execution of batch-files (.cmd/.bat) is vulnerable in go-lang for windows / insufficient escape
Execution of batch-files using os/exec with arguments containing some special meta-characters is vulnerable and may be used to execute foreign data/code.
What version of Go are you using (go version)?
Latest stable build: go1.10.3 windows/386
Does this issue reproduce with the latest release?
Yes
What operating system and processor architecture are you using (go env)?
Windows / x86
set GOARCH=386
set GOBIN=
set GOCACHE=%USERPROFILE%\AppData\Local\go-build
set GOEXE=.exe
set GOHOSTARCH=386
set GOHOSTOS=windows
set GOOS=windows
set GOPATH=%USERPROFILE%\go
set GORACE=
set GOROOT=C:\dev\go
set GOTMPDIR=
set GOTOOLDIR=C:\dev\go\pkg\tool\windows_386
set GCCGO=gccgo
set GO386=sse2
set CC=gcc
set CXX=g++
set CGO_ENABLED=1
set CGO_CFLAGS=-g -O2
set CGO_CPPFLAGS=
set CGO_CXXFLAGS=-g -O2
set CGO_FFLAGS=-g -O2
set CGO_LDFLAGS=-g -O2
set PKG_CONFIG=pkg-config
set GOGCCFLAGS=-m32 -mthreads -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=C:\Temp\go-build080181794=/tmp/go-build -gno-record-gcc-switches
What did you do?
Execution of batch-file using os/exec with arguments containing some special meta-characters.
A recipe for reproducing the error as well as more extensive PoC with additional info (and more lang’s affected also) - github/sebres/PoC/SB-0D-001-win-exec A complete runnable program - test-dump-part.go:
Content of test-dump-part.go
package main
import (
"os"
"os/exec"
)
func main() {
args := os.Args[2:]
cmd := exec.Command(os.Args[1], args...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err := cmd.Start()
if err != nil {
os.Exit(1)
}
}
An example:
# invoke exe-file:
go run test-dump-part.go test-dump.exe "test&whoami"
+ `test-dump.exe´ `test&whoami´
# invoke cmd-file:
go run test-dump-part.go test-dump.CMD "test&whoami"
- `test-dump.exe´ `test´my_domain\sebres
For more “broken” cases see the result of my test-suite: https://github.com/sebres/PoC/blob/master/SB-0D-001-win-exec/results/go.diff
What did you expect to see?
Arguments are escaped/quoted properly.
What did you see instead?
Arguments are insufficient escaped/quoted, so it is vulnerable currently.
Solution:
For possible solution see the algorithm description resp. how it was fixed in TCL (see the function BuildCommandLine)
Possible similar issues:
About this issue
- Original URL
- State: closed
- Created 6 years ago
- Comments: 17 (8 by maintainers)
Minimal reproducer:
exec.go
echo.bat
output
expected output
I looked into this and it does appear to be a real issue.
Background
On Windows, command line arguments are not passed to programs as an array like they are on UNIX, but as a single, unprocessesed string.
In principle, this means that any program is free to parse the command line string any way it wants, using any quoting rules it wants.
In practice, most programs use the CommandLineToArgv function to parse command line arguments, so Go’s os/exec package quotes the argument array in such a way that CommandLineToArgv will yield the same arguments.
The problem
Most windows programs use CommandLineToArgv to parse their arguments, but there is an important exception: cmd.exe. Also, .BAT and .CMD files are executed on windows by running CMD.EXE /C. If you run a .BAT file with arguments, as in e.g.,
then windows ends up running
The part after
/cis interpreted as a command, which may contain metacharacters like<>&. cmd.exe also has different quoting rules: in particular, backslash escapes like\"are not recognized and the caret^is treated as an escape character.All together, this means that if a Go program ever executes a .bat file with os/exec, it is likely that the command line string will be misinterpreted. Since CMD.EXE can execute arbitrary commands, this leads directly to arbitrary code execution.
@sebres does that about sum it up?
Incidentally, it looks like this problem was already recognized and reported two years ago (https://github.com/golang/go/issues/15566, maybe https://github.com/golang/go/issues/17149) and a fix proposed (https://golang.org/cl/30947) but never merged.
FWIW (suspected) security vulnerabilities should be disclosed privately (see https://golang.org/security).