From ea570a36ac35c4a5500dea37a98ee613c310b14d Mon Sep 17 00:00:00 2001 From: Mike Cohen Date: Thu, 7 Dec 2023 14:35:14 +1000 Subject: [PATCH 1/2] Disable Zip Decryption auth check. This default causes zip members to be decompressed into a memory buffer leading to huge memory consumption. In practice the embedded zip file has sufficient structure to be fortified against bit manipulation attacks anyway and there are checksums. --- accessors/zip/zip.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/accessors/zip/zip.go b/accessors/zip/zip.go index 7f9b8f5d6a4..2202b57c219 100644 --- a/accessors/zip/zip.go +++ b/accessors/zip/zip.go @@ -295,6 +295,12 @@ func (self *ZipFileCache) Open(full_path *accessors.OSPath) ( return nil, err } + // Disable stream authentication because the library unpacks the + // entire stream into memory to verify it. In practice, the + // embedded data.zip file provides sufficient authentication + // anyway. See https://github.com/Velocidex/velociraptor/issues/3150 + info.member_file.DeferAuth = true + fd, err := info.member_file.Open() if err == zip.ErrPassword { password := self.maybeGetPassword() From 63bb650d16c4019d98bcb09e5b71b35bb73d50fe Mon Sep 17 00:00:00 2001 From: Mike Cohen Date: Thu, 7 Dec 2023 16:10:50 +1000 Subject: [PATCH 2/2] Optionally report the derived session password. This allows decryption by other tools (e.g. 7zip). --- accessors/collector/collector.go | 6 +++++- bin/unzip.go | 9 ++++++--- constants/constants.go | 3 +++ 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/accessors/collector/collector.go b/accessors/collector/collector.go index 72652947fe7..e7f2f0aae4c 100644 --- a/accessors/collector/collector.go +++ b/accessors/collector/collector.go @@ -252,7 +252,11 @@ func (self *CollectorAccessor) maybeSetZipPassword( } self.scope.SetContext(constants.ZIP_PASSWORDS, string(zip_pass)) - + value, pres := self.scope.Resolve(constants.REPORT_ZIP_PASSWORD) + if pres && self.scope.Bool(value) { + self.scope.Log("CollectorAccessor: X509 Decrypted password is %q", + string(zip_pass)) + } // Transform the path so it can be used by the zip // collector. return collectorPathToDelegatePath(full_path), nil diff --git a/bin/unzip.go b/bin/unzip.go index 98a756dd8e2..dab2ac69ed4 100644 --- a/bin/unzip.go +++ b/bin/unzip.go @@ -8,6 +8,7 @@ import ( "github.com/Velocidex/ordereddict" config_proto "www.velocidex.com/golang/velociraptor/config/proto" + "www.velocidex.com/golang/velociraptor/constants" logging "www.velocidex.com/golang/velociraptor/logging" "www.velocidex.com/golang/velociraptor/reporting" "www.velocidex.com/golang/velociraptor/services" @@ -18,8 +19,9 @@ import ( ) var ( - unzip_cmd = app.Command("unzip", "Unzip a container file") - unzip_cmd_filter = unzip_cmd.Flag("where", "A WHERE condition for the query").String() + unzip_cmd = app.Command("unzip", "Unzip a container file") + unzip_cmd_report_password = unzip_cmd.Flag("report_password", "Log the X509 session password").Bool() + unzip_cmd_filter = unzip_cmd.Flag("where", "A WHERE condition for the query").String() unzip_path = unzip_cmd.Flag("dump_dir", "Directory to dump output files."). Default(".").String() @@ -77,7 +79,8 @@ func doUnzip() error { Env: ordereddict.NewDict(). Set("ZipPath", filename). Set("DumpDir", *unzip_path). - Set("MemberGlob", *unzip_cmd_member), + Set("MemberGlob", *unzip_cmd_member). + Set(constants.REPORT_ZIP_PASSWORD, *unzip_cmd_report_password), } if *unzip_cmd_list { diff --git a/constants/constants.go b/constants/constants.go index 3c5b412722e..75c5fc01b46 100644 --- a/constants/constants.go +++ b/constants/constants.go @@ -108,6 +108,9 @@ const ( // accessor to open password protected zip files. ZIP_PASSWORDS = "ZIP_PASSWORDS" + // If this is set, the logs will report the decrypted password + REPORT_ZIP_PASSWORD = "REPORT_ZIP_PASSWORD" + // If this is set we always copy SQLite files to a tempfile. Used // by the sqlite() plugin. SQLITE_ALWAYS_MAKE_TEMPFILE = "SQLITE_ALWAYS_MAKE_TEMPFILE"