Skip to content

Commit

Permalink
begin to support nested zips when patching
Browse files Browse the repository at this point in the history
  • Loading branch information
breadchris committed Dec 27, 2021
1 parent fbab2cf commit 7e8c146
Show file tree
Hide file tree
Showing 6 changed files with 99 additions and 31 deletions.
2 changes: 1 addition & 1 deletion tools/log4shell/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ payload:

cli:
touch ${LIBRARY_HASHES}
go build -o ${BINARY_NAME} .
CGO_ENABLED=0 GOOS=linux go build -o ${BINARY_NAME} .

library-hashes: cli
./log4shell analyze --output ${LIBRARY_HASHES} test/vulnerable-log4j2-versions/apache test/vulnerable-log4j2-versions/target/dependency
Expand Down
13 changes: 6 additions & 7 deletions tools/log4shell/analyze/analyze.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,11 +97,11 @@ func fileNameToSemver(fileNameNoExt string) string {
return semverVersion
}

func GetJndiLookupHash(zipReader *zip.Reader, filePath string) (fileName, fileHash string) {
func GetJndiLookupHash(zipReader *zip.Reader, filePath string) (fileHash string) {
reader, err := zipReader.Open(constants.JndiLookupClasspath)
if err != nil {
log.Debug().
Str("fieName", fileName).
Str("fieName", constants.JndiLookupClasspath).
Str("path", filePath).
Err(err).
Msg("cannot find file in zip")
Expand All @@ -112,7 +112,7 @@ func GetJndiLookupHash(zipReader *zip.Reader, filePath string) (fileName, fileHa
fileHash, err = util.HexEncodedSha256FromReader(reader)
if err != nil {
log.Debug().
Str("fieName", fileName).
Str("fieName", constants.JndiLookupClasspath).
Str("path", filePath).
Err(err).
Msg("unable to hash JndiLookup.class file")
Expand All @@ -123,7 +123,6 @@ func GetJndiLookupHash(zipReader *zip.Reader, filePath string) (fileName, fileHa

func ProcessArchiveFile(zipReader *zip.Reader, reader io.Reader, filePath, fileName string) (finding *types.Finding) {
var (
jndiLookupFileName string
jndiLookupFileHash string
)

Expand Down Expand Up @@ -167,22 +166,22 @@ func ProcessArchiveFile(zipReader *zip.Reader, reader io.Reader, filePath, fileN
}

if versionIsInRange(fileNameNoExt, semverVersion, constants.JndiLookupPatchFileVersions) {
jndiLookupFileName, jndiLookupFileHash = GetJndiLookupHash(zipReader, filePath)
jndiLookupFileHash = GetJndiLookupHash(zipReader, filePath)
}

log.Log().
Str("path", filePath).
Str("fileName", fileName).
Str("fileHash", fileHash).
Str("jndiLookupFileName", jndiLookupFileName).
Str("jndiLookupFileName", constants.JndiLookupClasspath).
Str("jndiLookupFileHash", jndiLookupFileHash).
Msg("identified library version")

finding = &types.Finding{
Path: filePath,
FileName: fileName,
Hash: fileHash,
JndiLookupFileName: jndiLookupFileName,
JndiLookupFileName: constants.JndiLookupClasspath,
JndiLookupHash: jndiLookupFileHash,
Version: semverVersion,
CVE: versionCve,
Expand Down
94 changes: 81 additions & 13 deletions tools/log4shell/commands/patch.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
"github.com/urfave/cli/v2"
"io/ioutil"
"os"
"strings"
)

func scanForFindings(
Expand Down Expand Up @@ -96,7 +97,7 @@ func loadOrScanForFindings(
return scanForFindings(log4jLibraryHashes, searchDirs, excludeDirs, noFollowSymlinks)
}

func askIfShouldSkipLibrary(msg string) (shouldSkip, forcePatch bool) {
func askIfShouldSkipPatch(msg string) (shouldSkip, forcePatch bool) {
var (
patchPromptResp string
)
Expand Down Expand Up @@ -128,20 +129,67 @@ func askIfShouldSkipLibrary(msg string) (shouldSkip, forcePatch bool) {
return
}

func getHashOfZipMember(member *zip.File) (hash string) {
memberReader, err := member.Open()
if err != nil {
log.Warn().
Err(err).
Str("name", member.Name).
Msg("Unable to open zip member")
return
}
defer memberReader.Close()

hash, err = util.HexEncodedSha256FromReader(memberReader)
if err != nil {
log.Warn().
Err(err).
Str("name", member.Name).
Msg("Unable to hash zip member")
return
}
return
}

func filterOutJndiLookupFromZip(
finding types.Finding,
zipReader *zip.Reader,
writer *zip.Writer,
) error {
for _, member := range zipReader.File {
if member.Name == finding.JndiLookupFileName {
shouldSkip := false

log.Debug().
Str("path", finding.Path).
Str("zipFilePath", finding.JndiLookupFileName).
Msg("Found file to remove in order to patch log4j library.")
continue

hash := getHashOfZipMember(member)
if hash != finding.JndiLookupHash {
shouldSkip, _ = askIfShouldSkipPatch(
fmt.Sprintf(
"located JndiLookup.class file hash does not match expected finding hash: \"%s\" != \"%s\" . Patching might result in unintended side effects.",
hash, finding.JndiLookupHash,
),
)
}

if !shouldSkip {
continue
}

log.Info().
Str("findingPath", finding.Path).
Msg("Skipping library for patching")
}

if member.FileInfo().IsDir() {
fmt.Println(member.Name, member.FileInfo().IsDir())
fmt.Printf("%+v\n", member.FileHeader)
}


if err := writer.Copy(member); err != nil {
log.Error().
Err(err).
Expand All @@ -152,13 +200,16 @@ func filterOutJndiLookupFromZip(
return nil
}

func patchJavaArchive(finding types.Finding) (err error) {
func patchJavaArchive(finding types.Finding, dryRun bool) (err error) {
var (
libraryFile *os.File
zipReader *zip.Reader
)

libraryFile, err = os.Open(finding.Path)
zipPaths := strings.Split(finding.Path, "::")
var zipReaders []*zip.Reader

libraryFile, err = os.Open(zipPaths[0])
if err != nil {
log.Error().
Str("path", finding.Path).
Expand All @@ -169,14 +220,23 @@ func patchJavaArchive(finding types.Finding) (err error) {
defer libraryFile.Close()

info, _ := os.Stat(finding.Path)
zipSize := info.Size()

zipReader, err = zip.NewReader(libraryFile, info.Size())
if err != nil {
log.Error().
Str("path", finding.Path).
Err(err).
Msg("Unable to open archive for patching")
return
for _, zipPath := range zipPaths {
nestedZip, err := zipReader.Open(zipPath)

nestedZip.

zipReader, err = zip.NewReader(libraryFile, zipSize)
if err != nil {
log.Error().
Str("path", finding.Path).
Str("zipPath", zipPath).
Err(err).
Msg("Unable to open archive for patching")
return
}
zipReaders = append(zipReaders, zipReader)
}

outZip, err := ioutil.TempFile(os.TempDir(), "*.zip")
Expand Down Expand Up @@ -217,6 +277,13 @@ func patchJavaArchive(finding types.Finding) (err error) {
return
}

if dryRun {
log.Info().
Str("library", finding.Path).
Msg("[Dry Run] Not completing patch process of overwriting existing library.")
return
}

_, err = util.CopyFile(outZip.Name(), finding.Path)
if err != nil {
log.Error().
Expand Down Expand Up @@ -246,6 +313,7 @@ func JavaArchivePatchCommand(
Msg("Patching found vulnerable Log4j libraries.")

forcePatch := c.Bool("force-patch")
dryRun := c.Bool("dry-run")

var patchedLibraries []string

Expand All @@ -263,7 +331,7 @@ func JavaArchivePatchCommand(
}

if !forcePatch {
shouldSkip, forcePatch = askIfShouldSkipLibrary(finding.Path)
shouldSkip, forcePatch = askIfShouldSkipPatch(finding.Path)
if !forcePatch && shouldSkip {
log.Info().
Str("findingPath", finding.Path).
Expand All @@ -272,7 +340,7 @@ func JavaArchivePatchCommand(
}
}

err = patchJavaArchive(finding)
err = patchJavaArchive(finding, dryRun)
if err != nil {
continue
}
Expand Down
4 changes: 4 additions & 0 deletions tools/log4shell/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,10 @@ func main() {
Name: "force-patch",
Usage: "Force patch all libraries reported in findings or scanned at runtime. Do not prompt each time a library is about to be patched.",
},
&cli.BoolFlag{
Name: "dry-run",
Usage: "Perform a dry run of the patching process by only logging out actions which would be performed.",
},
&cli.StringFlag{
Name: "findings",
Usage: "Patches all vulnerable Java archives which have been identified.",
Expand Down
16 changes: 6 additions & 10 deletions tools/log4shell/scan/scanfile.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,6 @@ func identifyPotentiallyVulnerableFile(
path, fileName string,
hashLookup types.VulnerableHashLookup,
) (finding *types.Finding) {
var (
jndiLookupFileName string
jndiLookupFileHash string
)

fileHash, err := util.HexEncodedSha256FromReader(reader)
if err != nil {
log.Warn().
Expand Down Expand Up @@ -88,20 +83,21 @@ func identifyPotentiallyVulnerableFile(
versions := strings.Split(vulnerableFile.Version, ", ")
patchableVersion := isVulnerableIfContainsJndiLookup(versions)

jndiLookupFileName, jndiLookupFileHash = analyze.GetJndiLookupHash(zipReader, path)

jndiLookupFileHash := analyze.GetJndiLookupHash(zipReader, path)
if jndiLookupFileHash != "" {
if _, ok := vulnerableFile.VulnerableFileHashLookup[jndiLookupFileHash]; !ok {
log.Warn().
Str("path", path).
Str("jndiLookupFileName", jndiLookupFileName).
Str("jndiLookupFileName", constants.JndiLookupClasspath).
Str("jndiLookupHash", jndiLookupFileHash).
Msg("Discovered JndiLookup.class file is not a known vulnerable file. Patching this file out might have some unintended side effects.")
}
} else {
if patchableVersion {
log.Warn().
Str("path", path).
Str("jndiLookupFileName", constants.JndiLookupClasspath).
Str("jndiLookupHash", jndiLookupFileHash).
Msg("Library has been patched of the Log4Shell vulnerability.")
return
}
Expand All @@ -112,7 +108,7 @@ func identifyPotentiallyVulnerableFile(
Str("path", path).
Str("versionIndicatorFileName", fileName).
Str("versionIndicatorHash", fileHash).
Str("jndiLookupFileName", jndiLookupFileName).
Str("jndiLookupFileName", constants.JndiLookupClasspath).
Str("jndiLookupHash", jndiLookupFileHash).
Str("versionInfo", vulnerableFile.Version).
Str("cve", vulnerableFile.CVE).
Expand All @@ -131,7 +127,7 @@ func identifyPotentiallyVulnerableFile(
Path: absolutePath,
FileName: fileName,
Hash: fileHash,
JndiLookupFileName: jndiLookupFileName,
JndiLookupFileName: constants.JndiLookupClasspath,
JndiLookupHash: jndiLookupFileHash,
Version: vulnerableFile.Version,
CVE: vulnerableFile.CVE,
Expand Down
1 change: 1 addition & 0 deletions tools/log4shell/test/vulnerable-log4j2-versions/.gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
target/
pom.xml
apache/
apache-patch/

0 comments on commit 7e8c146

Please sign in to comment.