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
panics in production for us, and as a result, regardless of what the spec says, I strongly believe thatarchive/zipshould 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, thetoValidNamewill 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.ValidPathto 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).Openclearly 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.