Skip to content

Commit

Permalink
Add CVE-2021-45105 support
Browse files Browse the repository at this point in the history
  • Loading branch information
hupe1980 committed Dec 19, 2021
1 parent 4adec6c commit 285bded
Show file tree
Hide file tree
Showing 6 changed files with 122 additions and 45 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ test:

.PHONY: run
run:
@go run *.go remote url -h
@go run *.go local -h

.PHONY: run-local
run-local:
Expand Down
12 changes: 10 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ CVE-2021-44228 is a remote code execution (RCE) vulnerability in Apache Log4j 2.
- Domain Name Service (DNS)

:warning: There is a patch bypass on Log4J v2.15.0: [CVE-2021-45046](https://nvd.nist.gov/vuln/detail/CVE-2021-45046)

:warning: Log4J v2.16 High Severity Vulnerability discovered: [CVE-2021-45105](https://nvd.nist.gov/vuln/detail/CVE-2021-45105)
## Installing
You can install the pre-compiled binary in several different ways

Expand Down Expand Up @@ -70,9 +72,10 @@ Usage:
scan4log4shell local [paths] [flags]

Flags:
--check-cve-2021-45046 check for CVE-2021-45046
-e, --exclude stringArray path to exclude
-h, --help help for local
--ignore-cve-2021-45046 ignore CVE-2021-45046
--ignore-cve-2021-45105 ignore CVE-2021-45105
--ignore-ext stringArray ignore .jar | .zip | .war | .ear | .aar
--ignore-v1 ignore log4j 1.x versions
--max-threads int max number of concurrent threads (default 5)
Expand All @@ -97,12 +100,17 @@ scanner_1 | [i] Inspecting /walk/apache-log4j-2.14.0-bin/log4j-api-2.14.0-javad
scanner_1 | [i] Inspecting /walk/apache-log4j-2.14.0-bin/log4j-api-2.14.0-sources.jar...
scanner_1 | [i] Inspecting /walk/apache-log4j-2.14.0-bin/log4j-api-2.14.0.jar...
scanner_1 | [i] Inspecting /walk/apache-log4j-2.14.0-bin/log4j-core-2.14.0.jar...
scanner_1 | [!] Hit: possibly vulnerable file identified: /walk/apache-log4j-2.14.0-bin/log4j-core-2.14.0.jar
scanner_1 | [!] Hit: possibly CVE-2021-45046 vulnerable file identified: /walk/apache-log4j-2.14.0-bin/log4j-core-2.14.0.jar
scanner_1 | [!] Hit: possibly CVE-2021-45105 vulnerable file identified: /walk/apache-log4j-2.14.0-bin/log4j-core-2.14.0.jar
scanner_1 | [!] Hit: possibly CVE-2021-44228 vulnerable file identified: /walk/apache-log4j-2.14.0-bin/log4j-core-2.14.0.jar
scanner_1 | [i] Inspecting /walk/apache-log4j-2.15.0-bin/log4j-api-2.15.0.jar...
scanner_1 | [i] Inspecting /walk/apache-log4j-2.15.0-bin/log4j-core-2.15.0.jar...
scanner_1 | [!] Hit: possibly CVE-2021-45046 vulnerable file identified: /walk/apache-log4j-2.15.0-bin/log4j-core-2.15.0.jar
scanner_1 | [!] Hit: possibly CVE-2021-45105 vulnerable file identified: /walk/apache-log4j-2.15.0-bin/log4j-core-2.15.0.jar
scanner_1 | [i] Inspecting /walk/apache-log4j-2.15.0-bin/log4j-spring-boot-2.15.0.jar...
scanner_1 | [i] Inspecting /walk/apache-log4j-2.16.0-bin/log4j-api-2.16.0.jar...
scanner_1 | [i] Inspecting /walk/apache-log4j-2.16.0-bin/log4j-core-2.16.0.jar...
scanner_1 | [!] Hit: possibly CVE-2021-45105 vulnerable file identified: /walk/apache-log4j-2.16.0-bin/log4j-core-2.16.0.jar
scanner_1 | [i] Inspecting /walk/jakarta-log4j-1.2.8/dist/lib/log4j-1.2.8.jar...
scanner_1 | [!] Hit: log4j V1 identified: /walk/jakarta-log4j-1.2.8/dist/lib/log4j-1.2.8.jar
scanner_1 | [i] Completed scanning
Expand Down
23 changes: 13 additions & 10 deletions cmd/local.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,12 @@ import (
)

type localOptions struct {
excludes []string
ignoreExts []string
ignoreV1 bool
maxThreads int
checkCVE2021_45046 bool
excludes []string
ignoreExts []string
ignoreV1 bool
ignoreCVE2021_45046 bool
ignoreCVE2021_45105 bool
maxThreads int
}

func newLocalCmd(noColor *bool, output *string, verbose *bool) *cobra.Command {
Expand Down Expand Up @@ -51,12 +52,13 @@ func newLocalCmd(noColor *bool, output *string, verbose *bool) *cobra.Command {
var wg sync.WaitGroup
sem := semaphore.NewWeighted(int64(opts.maxThreads))

printInfo("Log4Shell CVE-2021-44228 Local Vulnerability Scan")
printInfo("Log4Shell Local Vulnerability Scan")

scanner := internal.NewLocalScanner(&internal.LocalOptions{
Excludes: opts.excludes,
IgnoreExts: opts.ignoreExts,
CheckCVE2021_45046: opts.checkCVE2021_45046,
Excludes: opts.excludes,
IgnoreExts: opts.ignoreExts,
IgnoreCVE2021_45046: opts.ignoreCVE2021_45046,
IgnoreCVE2021_45105: opts.ignoreCVE2021_45105,
})

go func() {
Expand Down Expand Up @@ -106,7 +108,8 @@ func newLocalCmd(noColor *bool, output *string, verbose *bool) *cobra.Command {
}

cmd.Flags().BoolVarP(&opts.ignoreV1, "ignore-v1", "", false, "ignore log4j 1.x versions")
cmd.Flags().BoolVarP(&opts.checkCVE2021_45046, "check-cve-2021-45046", "", false, "check for CVE-2021-45046")
cmd.Flags().BoolVarP(&opts.ignoreCVE2021_45046, "ignore-cve-2021-45046", "", false, "ignore CVE-2021-45046")
cmd.Flags().BoolVarP(&opts.ignoreCVE2021_45105, "ignore-cve-2021-45105", "", false, "ignore CVE-2021-45105")
cmd.Flags().StringArrayVarP(&opts.ignoreExts, "ignore-ext", "", []string{}, "ignore .jar | .zip | .war | .ear | .aar")
cmd.Flags().StringArrayVarP(&opts.excludes, "exclude", "e", []string{}, "path to exclude")
cmd.Flags().IntVarP(&opts.maxThreads, "max-threads", "", 5, "max number of concurrent threads")
Expand Down
2 changes: 1 addition & 1 deletion cmd/remote_cidr.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ func newRemoteCIDRCmd(noColor *bool, output *string, verbose *bool) *cobra.Comma

cidr := args[0]

printInfo("Log4Shell CVE-2021-44228 Remote Vulnerability Scan")
printInfo("Log4Shell Remote Vulnerability Scan")

ctx, cancel := context.WithCancel(context.Background())
defer cancel()
Expand Down
2 changes: 1 addition & 1 deletion cmd/remote_url.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ func newRemoteURLCmd(noColor *bool, output *string, verbose *bool) *cobra.Comman
color.NoColor = true
}

printInfo("Log4Shell CVE-2021-44228 Remote Vulnerability Scan")
printInfo("Log4Shell Remote Vulnerability Scan")

ctx, cancel := context.WithCancel(context.Background())
defer cancel()
Expand Down
126 changes: 96 additions & 30 deletions internal/local.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,11 @@ import (
)

type LocalOptions struct {
Excludes []string
IgnoreExts []string
IgnoreV1 bool
CheckCVE2021_45046 bool
Excludes []string
IgnoreExts []string
IgnoreV1 bool
IgnoreCVE2021_45046 bool
IgnoreCVE2021_45105 bool
}
type LocalScanner struct {
opts *LocalOptions
Expand Down Expand Up @@ -88,12 +89,63 @@ func (ls *LocalScanner) ArchieveWalk(root string, fn func(path string, ra io.Rea
}

type localScanState struct {
isLog4J1 bool
log4jIndicator int
hasJndiLookup bool
hasJndiManager bool
hasCVE2021_44228VulnJndiManager bool
hasCVE2021_45046VulnJndiManager bool
isLog4J1 bool
log4j2Confidence int
isLog4JGE2_10 bool
isLog4J2_12_2 bool
isLog4J2_15 bool
isLog4J2_16 bool
isLog4J2_17 bool
hasJndiLookup bool
hasJndiManager bool
}

func (ls *localScanState) IsLog4J1() bool {
return ls.isLog4J1
}

func (ls *localScanState) IsLog4j2_12() bool {
return ls.isLog4JGE2_10 && ls.isLog4J2_12_2
}

func (ls *localScanState) IsLog4J2() bool {
return ls.log4j2Confidence >= 5
}

func (ls *localScanState) Version() string {
if ls.IsLog4J1() {
return "log4j V1.x"
}

if ls.IsLog4J2() && !ls.isLog4JGE2_10 {
return "log4j >= V2.0-beta9 and < V2.10.0"
}

return ""
}

func (ls *localScanState) HasCVE2021_44228() bool {
if !ls.IsLog4J2() {
return false
}

if ls.IsLog4j2_12() || ls.isLog4J2_15 || ls.isLog4J2_16 || ls.isLog4J2_17 {
return false
}

return ls.hasJndiLookup
}

func (ls *localScanState) HasCVE2021_45046() bool {
if ls.IsLog4J1() || ls.IsLog4j2_12() || ls.isLog4J2_16 || ls.isLog4J2_17 {
return false
}

return ls.IsLog4J2()
}

func (ls *localScanState) HasCVE2021_45105() bool {
return ls.IsLog4J2() && !ls.isLog4J2_17
}

func (ls *LocalScanner) InspectJar(path string, ra io.ReaderAt, sz int64, opts *LocalOptions) {
Expand All @@ -105,23 +157,40 @@ func (ls *LocalScanner) InspectJar(path string, ra io.ReaderAt, sz int64, opts *

state := localScanState{}

LOOP:
// Fingerprints were taken from https://github.com/mergebase/log4j-detector
for _, file := range zr.File {
switch strings.ToLower(filepath.Ext(file.Name)) {
case ".class":
if hasSuffix(file.Name, "log4j/DailyRollingFileAppender.class") {
state.isLog4J1 = true
break LOOP
break
}

if hasSuffix(file.Name, "core/appender/nosql/NoSqlAppender.class") {
state.isLog4JGE2_10 = true
continue
}

if hasSuffix(file.Name, "core/lookup/JndiLookup.class") {
state.hasJndiLookup = true

buf, err := readArchiveMember(file)
if err != nil {
ls.errsChan <- fmt.Errorf("cannot read JAR file member: %s (%s): %v", path, file.Name, err)
return
}

if bytes.Contains(buf, []byte("JNDI must be enabled by setting log4j2.enableJndiLookup=true")) { // v2.17.0
state.isLog4J2_17 = true
} else if !bytes.Contains(buf, []byte("Error looking up JNDI resource [{}].")) {
state.isLog4J2_12_2 = true
}

continue
}

if hasSuffix(file.Name, "core/net/JndiManager.class") {
state.hasJndiManager = true
state.hasCVE2021_44228VulnJndiManager = true

buf, err := readArchiveMember(file)
if err != nil {
Expand All @@ -130,37 +199,36 @@ LOOP:
}

if bytes.Contains(buf, []byte("log4j2.enableJndi")) { // v2.16.0
state.hasCVE2021_44228VulnJndiManager = false
state.hasCVE2021_45046VulnJndiManager = false
state.isLog4J2_16 = true
} else if bytes.Contains(buf, []byte("Invalid JNDI URI - {}")) { // v2.15.0
state.hasCVE2021_44228VulnJndiManager = false
state.hasCVE2021_45046VulnJndiManager = true
state.isLog4J2_15 = true
}

continue
}

if hasSuffix(file.Name, "core/LogEvent.class") {
state.log4jIndicator++
state.log4j2Confidence++
continue
}

if hasSuffix(file.Name, "core/Appender.class") {
state.log4jIndicator++
state.log4j2Confidence++
continue
}

if hasSuffix(file.Name, "core/Filter.class") {
state.log4jIndicator++
state.log4j2Confidence++
continue
}

if hasSuffix(file.Name, "core/Layout.class") {
state.log4jIndicator++
state.log4j2Confidence++
continue
}

if hasSuffix(file.Name, "core/LoggerContext.class") {
state.log4jIndicator++
state.log4j2Confidence++
continue
}
case ".jar", ".war", ".ear", ".zip", ".aar":
Expand All @@ -174,22 +242,20 @@ LOOP:
}
}

if !opts.IgnoreV1 && state.isLog4J1 {
if !opts.IgnoreV1 && state.IsLog4J1() {
ls.hitsChan <- fmt.Sprintf("log4j V1 identified: %s", absFilepath(path))
return
}

if state.hasJndiLookup && state.hasCVE2021_44228VulnJndiManager {
ls.hitsChan <- fmt.Sprintf("possibly CVE-2021-44228 vulnerable file identified: %s", absFilepath(path))
return
if !opts.IgnoreCVE2021_45046 && state.HasCVE2021_45046() {
ls.hitsChan <- fmt.Sprintf("possibly CVE-2021-45046 vulnerable file identified: %s", absFilepath(path))
}

if ls.opts.CheckCVE2021_45046 && state.hasCVE2021_45046VulnJndiManager {
ls.hitsChan <- fmt.Sprintf("possibly CVE-2021-45046 vulnerable file identified: %s", absFilepath(path))
return
if !opts.IgnoreCVE2021_45105 && state.HasCVE2021_45105() {
ls.hitsChan <- fmt.Sprintf("possibly CVE-2021-45105 vulnerable file identified: %s", absFilepath(path))
}

if state.log4jIndicator > 4 && state.hasJndiLookup && !state.hasJndiManager {
if state.HasCVE2021_44228() {
ls.hitsChan <- fmt.Sprintf("possibly CVE-2021-44228 vulnerable file identified: %s", absFilepath(path))
return
}
Expand Down

0 comments on commit 285bded

Please sign in to comment.