go: archive/zip: Reader.Open panics on empty string
What version of Go are you using (go version
)?
$ go version go version go1.17 linux/amd64
Does this issue reproduce with the latest release?
What operating system and processor architecture are you using (go env
)?
go env
Output
$ go env GO111MODULE="" GOARCH="amd64" GOBIN="" GOCACHE="/home/user/.cache/go-build" GOENV="/home/user/.config/go/env" GOEXE="" GOEXPERIMENT="" GOFLAGS="" GOHOSTARCH="amd64" GOHOSTOS="linux" GOINSECURE="" GOMODCACHE="/home/user/go/pkg/mod" GOOS="linux" GOPATH="/home/colin/go" GOPROXY="https://proxy.golang.org,direct" GOROOT="/usr/lib/go" GOSUMDB="sum.golang.org" GOTMPDIR="" GOTOOLDIR="/usr/lib/go/pkg/tool/linux_amd64" GOVCS="" GOVERSION="go1.17" GCCGO="gccgo" AR="ar" CC="gcc" CXX="g++" CGO_ENABLED="1" GOMOD="/dev/null" CGO_CFLAGS="-g -O2" CGO_CPPFLAGS="" CGO_CXXFLAGS="-g -O2" CGO_FFLAGS="-g -O2" CGO_LDFLAGS="-g -O2" PKG_CONFIG="pkg-config" GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build2958766942=/tmp/go-build -gno-record-gcc-switches"
What did you do?
r, _ := zip.NewReader(bytes.NewReader(raw), int64(len(raw)))
r.Open("")
https://play.golang.org/p/7x5dvzJSoCY
What did you expect to see?
What did you see instead?
panic: runtime error: index out of range [-1]
goroutine 1 [running]:
archive/zip.split(...)
/usr/local/go-faketime/src/archive/zip/reader.go:800
archive/zip.(*Reader).openLookup(0x0, {0x0, 0xffffffffffffffff})
/usr/local/go-faketime/src/archive/zip/reader.go:821 +0x265
archive/zip.(*Reader).Open(0x4e1360, {0x0, 0x16})
/usr/local/go-faketime/src/archive/zip/reader.go:785 +0x45
main.main()
/tmp/sandbox521722687/prog.go:11 +0xb2
Program exited: status 2.
Anything else?
This was discovered via io/fs.WalkDir
, but the root cause is Reader.Open
: https://play.golang.org/p/WWHxZRKu2gY:
panic: runtime error: index out of range [-1]
goroutine 1 [running]:
archive/zip.split(...)
/usr/local/go-faketime/src/archive/zip/reader.go:800
archive/zip.(*Reader).openLookup(0x40c6a7, {0x0, 0x4bb740})
/usr/local/go-faketime/src/archive/zip/reader.go:821 +0x265
archive/zip.(*Reader).Open(0x40c354, {0x0, 0xc000134000})
/usr/local/go-faketime/src/archive/zip/reader.go:785 +0x45
io/fs.Stat({0x4e4060, 0xc000134000}, {0x0, 0x0})
/usr/local/go-faketime/src/io/fs/stat.go:25 +0x9c
io/fs.WalkDir({0x4e4060, 0xc000134000}, {0x0, 0x0}, 0x0)
/usr/local/go-faketime/src/io/fs/walk.go:108 +0x3c
main.main()
/tmp/sandbox3566573894/prog.go:12 +0xbe
Program exited: status 2.
I was not expecting this to work, since most other fs.FS
implementations (say embed.FS
) return a not found error when root == ""
, but a panic seemed like a bug.
About this issue
- Original URL
- State: closed
- Created 3 years ago
- Reactions: 2
- Comments: 32 (16 by maintainers)
Commits related to this issue
- [release-branch.go1.17] archive/zip: don't panic on (*Reader).Open Previously, opening a zip with (*Reader).Open could result in a panic if the zip contained a file whose name was exclusively made up... — committed to golang/go by Jason7602 3 years ago
- [release-branch.go1.16] archive/zip: don't panic on (*Reader).Open Previously, opening a zip with (*Reader).Open could result in a panic if the zip contained a file whose name was exclusively made up... — committed to golang/go by Jason7602 3 years ago
I very strongly disagree with this. This was a cause of
panic
s in production for us, and as a result, regardless of what the spec says, I strongly believe thatarchive/zip
should not panic in such a scenario, as absolute paths in zips can and do exist in real-world files. If anything, I would consider this issue almost as serious as two previous panics in archive/zip, both of which have a CVE (first and second). Crafted/invalid input should not cause a panic, it should be handled appropriately and defensively.I don’t believe that this case is comparable, nor that the scenario is particularly “absurd” when real world files exist with this characteristic. There is a precedent from the above linked CVEs (most particularly the second one) that the panic from absolute paths should be addressed, as well as a precedent from other utilities that handle this particular case.
I completely understand that the case wouldn’t have been thought of while the code was written, I lay no blame on anyone for that, Im simply hoping for the scenario to be handled in such a way that it is still possible for me to consume the zip file and do with the erroneous paths as I wish (in this case, we would skip extracting them).
Of additional note, the issue is not triggered when I do
*archive/zip.File.Open
, but it is triggered when I do*archive/zip.Reader.Open
, so theres a definitive inconsistency there that should be addressed. Also do observe that the panic is triggered not when I try to access an absolute path file, but when I was trying to accessMETA-INF/MANIFEST.MF
.Regardless of our thoughts, I was strongly recommended to notify the security team of this issue, which I have 🙂 I appreciate your thoughts nonetheless
Also having the same issue/symptoms, code and zip (actually JAR) file attached. Of note when printing the file names from
reader.Files
(as commented out below) is that there are absolute paths including a/
. See picture at endDetails
liquibase-core-4.4.3-sources.zip
Thank you for reporting this to security@golang.org. Invalid input should not cause programs to panic, if the input could be attacker controlled. If this required a call to
Open("")
to trigger, it could have been borderline, since it’s hard for an attacker to control the argument to Open. However, the reproducer in https://github.com/golang/go/issues/48085#issuecomment-912659635 triggers a panic with a real file name.We’ll backport this as a security fix in the PUBLIC track. @gopherbot, please open backport issues.
When the zip file exist absolute path entry, in function
initFileList
, thetoValidName
will remove/
or../
element in front of the path and change it to the relative path entry and add it to thereader.fileList
, So even we do not usefs.ValidPath
to check the path when via archive/zip.(*ReadCloser).Open that corresponds with an absolute path entry, it also will returnfs.ErrNotExist
. Actually the files are in the zip file, but Go return the result of file not exist which become more confused.Actually the comment of
archive/zip.(*ReadCloser).Open
clearly tells developers not to open the filename leading with/
or../
elements.So to better distinguish the two error cases, return the
fs.PathError{fs.ErrInvalid}
when open the filename leading with / or …/ is more accurate from my point.