Skip to content

Commit

Permalink
analyzer has better semver version checking
Browse files Browse the repository at this point in the history
  • Loading branch information
breadchris committed Dec 17, 2021
1 parent b4751d1 commit 2dd8391
Show file tree
Hide file tree
Showing 7 changed files with 897 additions and 53 deletions.
107 changes: 59 additions & 48 deletions tools/log4shell/analyze/analyze.go
Expand Up @@ -22,34 +22,23 @@ import (
"github.com/rs/zerolog/log"
"io"
"path"
"regexp"
"strings"
)

func isVersionALog4ShellVersion(semverVersion string) bool {
version, _ := semver.Make(semverVersion)
var alphaRegex = regexp.MustCompile("([a-z]+)")

vulnerableRange, _ := semver.ParseRange(">=2.0.0-beta9 <=2.14.1")
if vulnerableRange(version) {
return true
}
return false
}

func isVersionACVE202145046Version(semverVersion string) bool {
version, _ := semver.Make(semverVersion)

vulnerableRange, _ := semver.ParseRange("=2.15.0")
if vulnerableRange(version) {
return true
func versionIsInRange(fileName string, semverVersion string, semverRange semver.Range) bool {
version, err := semver.Make(semverVersion)
if err != nil {
log.Warn().
Str("fileName", fileName).
Str("semverVersion", semverVersion).
Msg("Unable to parse semver version")
return false
}
return false
}

func isVersionACVE201917571Version(semverVersion string) bool {
version, _ := semver.Make(semverVersion)

vulnerableRange, _ := semver.ParseRange(">=1.2.0 <=1.2.17")
if vulnerableRange(version) {
if semverRange(version) {
return true
}
return false
Expand All @@ -68,39 +57,61 @@ func adjustMissingPatchVersion(semverVersion string) string {
return semverVersion
}

func fileNameToSemver(fileNameNoExt string) string {
fileNameParts := strings.Split(fileNameNoExt, "-")

var tag, semverVersion string
for i := len(fileNameParts) - 1; i >= 0; i-- {
fileNamePart := fileNameParts[i]
if (
strings.HasPrefix(fileNamePart, "1") ||
strings.HasPrefix(fileNamePart, "2")) &&
strings.Contains(fileNamePart, ".") {

tagPart := alphaRegex.FindString(fileNamePart)
if tagPart != "" {
fileNamePart = strings.Replace(fileNamePart, tagPart, "", 1)
if tag == "" {
tag = tagPart
} else {
tag = tagPart + "-" + tag
}
}

fileNamePart = adjustMissingPatchVersion(fileNamePart)

if tag == "" {
semverVersion = fileNamePart
break
}
semverVersion = fileNamePart + "-" + tag
break
}
if tag == "" {
tag = fileNamePart
continue
}
tag = fileNamePart + "-" + tag
}
return semverVersion
}

func ProcessArchiveFile(reader io.Reader, filePath, fileName string) (finding *types.Finding) {
_, file := path.Split(filePath)
version := strings.TrimSuffix(file, path.Ext(file))
fileNameNoExt := strings.TrimSuffix(file, path.Ext(file))

// small adjustments to the version so that it can be parsed as semver
semverVersion := strings.Replace(version, "log4j-core-", "", -1)
semverVersion = strings.Replace(semverVersion, "logging-log4j-", "", -1)
semverVersion = strings.Replace(semverVersion, "jakarta-log4j-", "", -1)
semverVersion = strings.Replace(semverVersion, "log4j-", "", -1)

semverVersion = adjustMissingPatchVersion(semverVersion)
semverVersion := fileNameToSemver(fileNameNoExt)

versionCve := ""

if isVersionALog4ShellVersion(semverVersion) {
if !strings.Contains(fileName, "JndiManager.class") {
return
}
versionCve = constants.Log4ShellCve
}

if isVersionACVE202145046Version(semverVersion) {
if !strings.Contains(fileName, "JndiManager.class") {
return
}
versionCve = constants.CtxCve
}

if isVersionACVE201917571Version(semverVersion) {
if !strings.Contains(fileName, "SocketNode.class") {
return
for _, fileVersionCheck := range constants.FileVersionChecks {
if versionIsInRange(fileNameNoExt, semverVersion, fileVersionCheck.SemverRange) {
if !strings.Contains(fileName, fileVersionCheck.LibraryFile) {
return
}
versionCve = fileVersionCheck.Cve
}
versionCve = constants.Log4j1RceCve
}

if versionCve == "" {
Expand All @@ -126,7 +137,7 @@ func ProcessArchiveFile(reader io.Reader, filePath, fileName string) (finding *t
if versionCve == "" {
log.Debug().
Str("hash", fileHash).
Str("version", version).
Str("version", semverVersion).
Msg("Skipping version as it is not vulnerable to any known CVE")
return nil
}
Expand Down
34 changes: 29 additions & 5 deletions tools/log4shell/constants/vulnerablehashes.go
Expand Up @@ -14,10 +14,13 @@
//
package constants

import "github.com/lunasec-io/lunasec/tools/log4shell/types"
import (
"github.com/blang/semver/v4"
"github.com/lunasec-io/lunasec/tools/log4shell/types"
)

var (
NotVulnerable = "Not Vulnerable"

const (
Log4ShellCve = "CVE-2021-44228"
CtxCve = "CVE-2021-45046"
Log4j1RceCve = "CVE-2019-17571"
Expand All @@ -29,9 +32,30 @@ var (
CtxCve: "3.7",
Log4j1RceCve: "9.8",
}
)

type Log4jVersion string
FileVersionChecks = []types.LibraryFileVersionCheck{
{
Cve: Log4ShellCve,
SemverRange: semver.MustParseRange(">=2.0.0-beta9 <2.1.0"),
LibraryFile: "JndiLookup.class",
},
{
Cve: Log4ShellCve,
SemverRange: semver.MustParseRange(">=2.1.0 <=2.14.1"),
LibraryFile: "JndiManager.class",
},
{
Cve: CtxCve,
SemverRange: semver.MustParseRange("=2.15.0"),
LibraryFile: "JndiManager.class",
},
{
Cve: Log4j1RceCve,
SemverRange: semver.MustParseRange(">=1.2.0 <=1.2.17"),
LibraryFile: "SocketNode.class",
},
}
)

const (
Log4j1x = "1"
Expand Down

0 comments on commit 2dd8391

Please sign in to comment.