diff --git a/tools/log4shell/main.go b/tools/log4shell/main.go index c4cfcfcb5..cee011db0 100644 --- a/tools/log4shell/main.go +++ b/tools/log4shell/main.go @@ -78,7 +78,7 @@ func scanCommand(c *cli.Context) error { return nil } -func hotpatchCommand(c *cli.Context) error { +func hotPatchCommand(c *cli.Context) error { enableGlobalFlags(c) ip := c.String("ip") @@ -104,6 +104,38 @@ func hotpatchCommand(c *cli.Context) error { return nil } +func jarPatchCommand(c *cli.Context) error { + enableGlobalFlags(c) + + fileName := c.String("file-name") + + if fileName == "" { + log.Info().Msg("Public IP not provided. Binding to the local network interface.") + panic("must specify a valid file name to patch") + } + + file, err := os.Open(path) + if err != nil { + log.Warn(). + Str("path", path). + Err(err). + Msg("unable to open archive") + panic("unable to open specified file") + } + + fileInfo, err := file.Stat() + + if err != nil { + panic("unable to read file info") + } + + findings := scan.SearchArchiveForVulnerableFiles(fileName, file, fileInfo.Size(), false) + + // TODO: Do something with these findings to actually patch them in-place. Either that or add the patching to `SearchArchiveForVulnerableFiles` above. + + return nil +} + func main() { zerolog.TimeFieldFormat = zerolog.TimeFormatUnix @@ -164,16 +196,28 @@ func main() { Action: scanCommand, }, { - Name: "hotpatch", + Name: "in-memory-hot-patch", Aliases: []string{"s"}, - Usage: "Perform a live hotpatch of a system by exploiting the log4shell vulnerability for immediate mitigation. The payload executed patches the running process to prevent further payloads from being able to be executed.", + Usage: "Perform a live hot patch of a system by exploiting the log4shell vulnerability for immediate mitigation. The payload executed patches the running process to prevent further payloads from being able to be executed.", Flags: []cli.Flag{ &cli.StringFlag{ Name: "ip", Usage: "If testing locally, set this to a local network interface (view available interfaces with ifconfig/ipconfig). If using on a remote server, set this value to the publicly accessible IP address of the server.", }, }, - Action: hotpatchCommand, + Action: hotPatchCommand, + }, + { + Name: "patch-local-jar", + Aliases: []string{"s"}, + Usage: "Patches a specified JAR or WAR file against log4shell by injecting a fixed version of the vulnerable code into vulnerable log4j instances found within it.", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "file-name", + Usage: "Patches the specified file (must be a valid JAR or WAR file).", + }, + }, + Action: jarPatchCommand, }, }, } diff --git a/tools/log4shell/patch/patch-local-jar.go b/tools/log4shell/patch/patch-local-jar.go new file mode 100644 index 000000000..3d34a9ff3 --- /dev/null +++ b/tools/log4shell/patch/patch-local-jar.go @@ -0,0 +1,29 @@ +// Copyright 2021 by LunaSec (owned by Refinery Labs, Inc) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +package patch + +import ( + "fmt" + ldapmsg "github.com/lor00x/goldap/message" + "github.com/lunasec-io/lunasec/tools/log4shell/constants" + "github.com/rs/zerolog/log" + "io/ioutil" + golog "log" +) + +import ( + "github.com/vjeantet/ldapserver" +) + diff --git a/tools/log4shell/scan/scan.go b/tools/log4shell/scan/scan.go index f5e14070f..63ba147c3 100644 --- a/tools/log4shell/scan/scan.go +++ b/tools/log4shell/scan/scan.go @@ -96,7 +96,7 @@ func scanArchive(path string, file *zip.File, onlyScanArchives bool) (findings [ archiveReader := bytes.NewReader(buffer) archiveSize := int64(len(buffer)) - return scanArchiveForVulnerableFiles(newPath, archiveReader, archiveSize, onlyScanArchives) + return SearchArchiveForVulnerableFiles(newPath, archiveReader, archiveSize, onlyScanArchives) } func scanFile(path string, file *zip.File, onlyScanArchives bool) (findings []types.Finding) { @@ -125,7 +125,8 @@ func scanFile(path string, file *zip.File, onlyScanArchives bool) (findings []ty return } -func scanArchiveForVulnerableFiles(path string, reader io.ReaderAt, size int64, onlyScanArchives bool) (findings []types.Finding) { +// SearchArchiveForVulnerableFiles Takes in a given JAR or WAR file and searches it for findings. +func SearchArchiveForVulnerableFiles(path string, reader io.ReaderAt, size int64, onlyScanArchives bool) (findings []types.Finding) { zipReader, err := zip.NewReader(reader, size) if err != nil { log.Warn(). @@ -157,7 +158,7 @@ func scanLocatedArchive(path string, info os.FileInfo, onlyScanArchives bool) (f } defer file.Close() - return scanArchiveForVulnerableFiles(path, file, info.Size(), onlyScanArchives) + return SearchArchiveForVulnerableFiles(path, file, info.Size(), onlyScanArchives) } // SearchDirsForVulnerableClassFiles walks each search dir looking for .class files in archives which have a hash