Skip to content

Commit

Permalink
add log2j 2.15.0 detection
Browse files Browse the repository at this point in the history
  • Loading branch information
1lann committed Dec 17, 2021
1 parent d8bbe57 commit 2e2bd4d
Show file tree
Hide file tree
Showing 3 changed files with 29 additions and 12 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
.DS_Store

dist/
.idea/
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# log4shelldetect

Scans a file or folder recursively for Java programs that may be vulnerable to Log4Shell (CVE-2021-44228) by inspecting the class paths inside files.
Scans a file or folder recursively for Java programs that may be vulnerable to Log4Shell (CVE-2021-44228) and as of v0.0.5, the incomplete patch in Log4j v2.15.0 (CVE-2021-45046) as well, by inspecting the class paths inside files.

If you only want possibly vulnerable files to be printed rather than all files, run with `-mode list`.

Expand All @@ -13,7 +13,7 @@ Options:
-include-zip
include zip files in the scan
-mode string
the output mode, either "report" (every jar pretty printed) or "list" (list of potentially vulnerable files) (default "report")
the output mode, either "report" (every java archive pretty printed) or "list" (list of potentially vulnerable files) (default "report")
```

## License
Expand Down
36 changes: 26 additions & 10 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import (

var printMutex = new(sync.Mutex)

var mode = flag.String("mode", "report", "the output mode, either \"report\" (every jar pretty printed) or \"list\" (list of potentially vulnerable files)")
var mode = flag.String("mode", "report", "the output mode, either \"report\" (every java archive pretty printed) or \"list\" (list of potentially vulnerable files)")
var includeZip = flag.Bool("include-zip", false, "include zip files in the scan")

func main() {
Expand All @@ -31,9 +31,10 @@ func main() {

if flag.Arg(0) == "" {
stderr.Println("Usage: log4shelldetect [options] <path>")
stderr.Println("Scans a file or folder recursively for jar files that may be")
stderr.Println("vulnerable to Log4Shell (CVE-2021-44228) by inspecting")
stderr.Println("the class paths inside the Jar")
stderr.Println("Scans a file or folder recursively for Java programs that may be")
stderr.Println("vulnerable to Log4Shell (CVE-2021-44228) and the incomplete patch")
stderr.Println("in Log4j 2.15.0 (CVE-2021-45046) by inspecting")
stderr.Println("the class paths inside Java archives")
stderr.Println("")
stderr.Println("Options:")
flag.PrintDefaults()
Expand Down Expand Up @@ -64,7 +65,7 @@ func main() {
// Scan through the directory provided recursively.
err = godirwalk.Walk(target, &godirwalk.Options{
Callback: func(osPathname string, de *godirwalk.Dirent) error {
// For each file in the directory, check if it ends in ".jar"
// For each file in the directory, check if it ends in a known Java archive extension
if shouldCheck(osPathname) {
pool <- struct{}{}
// If it is, take a goroutine (thread) from the thread pool
Expand Down Expand Up @@ -129,8 +130,8 @@ func shouldCheck(filename string) bool {
return false
}

// checkJar checks a given jar file and returns a status and description for whether
// or not the Log4Shell vulnerability is detected in the jar.
// checkJar checks a given archive file and returns a status and description for whether
// or not the Log4Shell vulnerability is detected in the archive.
func checkJar(pathToFile string, rd io.ReaderAt, size int64, depth int) (status Status, desc string) {
// checkJar also checks for embedded jars (jars inside jars) as this is fairly common occurrence
// in some jar distributions.
Expand Down Expand Up @@ -171,6 +172,7 @@ func checkJar(pathToFile string, rd io.ReaderAt, size int64, depth int) (status

// Define some default variables.
var vulnClassFound = false
var oldPatchFound = false
var patchedClassFound = false
var maybeClassFound = ""
var worstSubStatus Status = StatusOK
Expand All @@ -196,7 +198,8 @@ func checkJar(pathToFile string, rd io.ReaderAt, size int64, depth int) (status
// JmsAppender is where the patch for Log4Shell is made in
// the latest versions of Log4j. If we find it, we can extract it
// and inspect it for the patched code.
if strings.HasSuffix(file.Name, "log4j/core/appender/mom/JmsAppender$Builder.class") {
if strings.HasSuffix(file.Name, "JmsAppender$Builder.class") ||
strings.HasSuffix(file.Name, "JndiManager.class") {
err := func() error {
// If for some reason the class file is bigger than 1 MB (it should be less then a few hundred kilobytes),
// we abort.
Expand All @@ -217,8 +220,14 @@ func checkJar(pathToFile string, rd io.ReaderAt, size int64, depth int) (status
return err
}

if bytes.Contains(data, []byte("allowedLdapHosts")) &&
bytes.Contains(data, []byte("allowedJndiProtocols")) &&
bytes.Contains(data, []byte("allowedLdapClasses")) {
oldPatchFound = true
}

// And check if it contains the known patched code.
if bytes.Contains(data, []byte("allowedLdapHosts")) {
if bytes.Contains(data, []byte("log4j2.enableJndi")) {
// If so, indicate that the jar is patched.
patchedClassFound = true
}
Expand Down Expand Up @@ -292,6 +301,9 @@ func checkJar(pathToFile string, rd io.ReaderAt, size int64, depth int) (status
} else if patchedClassFound {
status = StatusPatched
desc = ""
} else if oldPatchFound {
status = StatusOld
desc = ""
} else {
status = StatusVulnerable
desc = ""
Expand Down Expand Up @@ -319,6 +331,7 @@ const (
StatusPatched
StatusUnknown
StatusMaybe
StatusOld
StatusVulnerable
)

Expand All @@ -330,7 +343,7 @@ func printStatus(fileName string, status Status, desc string) {

// If we're running in -mode list, we only print likely vulnerable files.
if *mode == "list" {
if status == StatusVulnerable || status == StatusMaybe {
if status == StatusVulnerable || status == StatusOld || status == StatusMaybe {
fmt.Println(fileName)
}

Expand All @@ -346,6 +359,9 @@ func printStatus(fileName string, status Status, desc string) {
case StatusPatched:
c = color.New(color.FgGreen)
c.Print("PATCHED ")
case StatusOld:
c = color.New(color.FgRed)
c.Print("OLD2.15 ")
case StatusVulnerable:
c = color.New(color.FgRed)
c.Print("VULNRBL ")
Expand Down

0 comments on commit 2e2bd4d

Please sign in to comment.