Skip to content

Commit e3f7592

Browse files
author
Johannes Spaeth
committed
Also inspecting packaged jar and war in folders
1 parent 39f791b commit e3f7592

File tree

3 files changed

+118
-47
lines changed

3 files changed

+118
-47
lines changed

pom.xml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
<groupId>de.codeshield.log4shell</groupId>
99
<artifactId>Log4JDetector</artifactId>
10-
<version>0.2</version>
10+
<version>0.3</version>
1111

1212
<name>cve-2021-44228-detector</name>
1313
<url>http://www.codeshield.io</url>
@@ -30,7 +30,11 @@
3030
<artifactId>maven-model</artifactId>
3131
<version>3.8.4</version>
3232
</dependency>
33-
33+
<dependency>
34+
<groupId>commons-io</groupId>
35+
<artifactId>commons-io</artifactId>
36+
<version>2.11.0</version>
37+
</dependency>
3438
<!-- https://mvnrepository.com/artifact/org.apache.maven/maven-project -->
3539
<dependency>
3640
<groupId>org.apache.maven</groupId>
Lines changed: 94 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,18 @@
11
package de.codeshield.log4jshell;
22

3+
import java.io.BufferedInputStream;
4+
import java.io.BufferedOutputStream;
35
import java.io.File;
6+
import java.io.FileInputStream;
7+
import java.io.FileOutputStream;
48
import java.io.IOException;
9+
import java.util.Collection;
510
import java.util.Enumeration;
611
import java.util.jar.JarEntry;
712
import java.util.jar.JarFile;
13+
import org.apache.commons.io.FileUtils;
14+
import org.apache.commons.io.filefilter.DirectoryFileFilter;
15+
import org.apache.commons.io.filefilter.RegexFileFilter;
816

917
/**
1018
* A simple command line tool that scans a jar file for the CVE-2021-44228 vulnerability that
@@ -14,49 +22,108 @@ public class Log4JDetector {
1422

1523
private static final String POM_FILE = "pom.xml";
1624
private static final String CLASS_FILE_NAME = ".class";
25+
private static final String JAR_FILE = ".jar";
26+
private static final String WAR_FILE = ".war";
1727

18-
public static void main(String[] args) {
19-
System.out.println("Analysing "+ args[0]);
28+
public static void main(String[] args) throws IOException {
29+
System.out.println("Analysing " + args[0]);
2030
File inputJarFile = new File(args[0]);
2131
if (!inputJarFile.exists()) {
22-
System.err.println("The file path " + args[0] + " does not exist. Ensure it is an absolute file paths");
32+
System.err.println(
33+
"The file path " + args[0] + " does not exist. Ensure it is an absolute file paths");
2334
return;
2435
}
2536
Log4JDetector detector = new Log4JDetector();
26-
detector.run(inputJarFile);
37+
detector.run(args[0]);
2738
}
2839

29-
public boolean run(File pathToJarFile) {
30-
JarFile jarFile = null;
31-
boolean isVulnerable = false;
32-
try {
33-
jarFile = new JarFile(pathToJarFile);
34-
Enumeration<JarEntry> entries = jarFile.entries();
35-
while (entries.hasMoreElements()) {
36-
JarEntry entry = entries.nextElement();
37-
//Check pom.xml files if a log4j dependency is declared
38-
if (entry.getName().endsWith(Log4JDetector.POM_FILE)) {
39-
if (POMDetector.isVulnerablePOM(jarFile.getInputStream(entry))) {
40-
isVulnerable = true;
41-
System.err.println("CVE-2021-44228 found declared as dependency in " + entry);
40+
//Taken from https://stackoverflow.com/questions/981578/how-to-unzip-files-recursively-in-java/7108813#7108813
41+
public static String extractFolder(String zipFile) throws IOException {
42+
int buffer = 2048;
43+
File file = new File(zipFile);
44+
String newPath = zipFile.substring(0, zipFile.length() - 4);
45+
46+
try (JarFile zip = new JarFile(file)) {
47+
48+
new File(newPath).mkdir();
49+
Enumeration<JarEntry> zipFileEntries = zip.entries();
50+
51+
// Process each entry
52+
while (zipFileEntries.hasMoreElements()) {
53+
// grab a zip file entry
54+
JarEntry entry = zipFileEntries.nextElement();
55+
String currentEntry = entry.getName();
56+
File destFile = new File(newPath, currentEntry);
57+
File destinationParent = destFile.getParentFile();
58+
59+
// create the parent directory structure if needed
60+
destinationParent.mkdirs();
61+
62+
if (!entry.isDirectory()) {
63+
BufferedInputStream is = new BufferedInputStream(zip.getInputStream(entry));
64+
int currentByte;
65+
// establish buffer for writing file
66+
byte[] data = new byte[buffer];
67+
68+
// write the current file to disk
69+
FileOutputStream fos = new FileOutputStream(destFile);
70+
try (BufferedOutputStream dest = new BufferedOutputStream(fos, buffer)) {
71+
72+
// read and write until last byte is encountered
73+
while ((currentByte = is.read(data, 0, buffer)) != -1) {
74+
dest.write(data, 0, currentByte);
75+
}
76+
dest.flush();
77+
is.close();
4278
}
4379
}
44-
//Check if a a class file matches one of the pre-computed vulnerable SHAs.
45-
if (entry.getName().endsWith(Log4JDetector.CLASS_FILE_NAME)) {
46-
if (ClassDetector.isVulnerableClass(jarFile.getInputStream(entry))) {
47-
isVulnerable = true;
48-
System.err.println("CVE-2021-44228 found in class file " + entry);
49-
}
80+
81+
if (currentEntry.endsWith(WAR_FILE) || currentEntry.endsWith(JAR_FILE)) {
82+
// found a zip file, try to open
83+
extractFolder(destFile.getAbsolutePath());
5084
}
5185
}
52-
} catch (IOException e) {
53-
System.err.println("Unable to open JarFile");
5486
}
55-
if(!isVulnerable){
87+
return newPath;
88+
}
89+
90+
public boolean run(String pathToJarFile) throws IOException {
91+
String folder = extractFolder(pathToJarFile);
92+
Collection<File> pomFiles = FileUtils.listFiles(
93+
new File(folder),
94+
new RegexFileFilter("^(pom.xml)"),
95+
DirectoryFileFilter.DIRECTORY
96+
);
97+
boolean isVulnerable = false;
98+
for (File pomFile : pomFiles) {
99+
try (FileInputStream is = new FileInputStream(pomFile)) {
100+
//Check if a pom file matches one of the pre-computed groupId:artifactId:version
101+
if (POMDetector.isVulnerablePOM(is)) {
102+
isVulnerable = true;
103+
System.err.println("CVE-2021-44228 found declared as dependency in " + pomFile);
104+
}
105+
}
106+
}
107+
Collection<File> classFiles = FileUtils.listFiles(
108+
new File(folder),
109+
new RegexFileFilter(".*.class$"),
110+
DirectoryFileFilter.DIRECTORY
111+
);
112+
113+
for (File pomFile : classFiles) {
114+
try (FileInputStream is = new FileInputStream(pomFile)) {
115+
//Check if a class file matches one of the pre-computed vulnerable SHAs.
116+
if (ClassDetector.isVulnerableClass(is)) {
117+
isVulnerable = true;
118+
System.err.println("CVE-2021-44228 found declared as dependency in " + pomFile);
119+
}
120+
}
121+
}
122+
if (!isVulnerable) {
56123
System.out.println("Jar file not affected by CVE-2021-44228!");
57124
}
125+
FileUtils.deleteDirectory(new File(folder));
58126
return isVulnerable;
59127
}
60128

61-
62129
}

src/test/java/de/codeshield/log4jshell/Log4JDetectorTests.java

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -9,25 +9,25 @@
99
import java.net.URL;
1010
import org.junit.Test;
1111

12-
public class Log4JDetectorTests
13-
{
14-
@Test
15-
public void checkVulnerables() throws IOException, URISyntaxException {
16-
assertTrue(checkResourceFile("/en16931-xml-validator-2.0.0-b2-jar-with-dependencies.jar"));
17-
assertTrue(checkResourceFile("/log4j-core-2.12.1.jar"));
18-
assertTrue(checkResourceFile("/log4j-core-2.14.1.jar"));
19-
}
12+
public class Log4JDetectorTests {
2013

21-
@Test
22-
public void checkSecure() throws IOException, URISyntaxException {
23-
assertFalse(checkResourceFile("/spring-boot-2.5.7.jar") );
24-
assertFalse(checkResourceFile("/log4j-core-2.15.0.jar"));
25-
}
14+
@Test
15+
public void checkVulnerables() throws IOException, URISyntaxException {
16+
assertTrue(checkResourceFile("/en16931-xml-validator-2.0.0-b2-jar-with-dependencies.jar"));
17+
assertTrue(checkResourceFile("/log4j-core-2.12.1.jar"));
18+
assertTrue(checkResourceFile("/log4j-core-2.14.1.jar"));
19+
}
2620

27-
private boolean checkResourceFile(String url) throws IOException, URISyntaxException {
28-
URL resource = Log4JDetectorTests.class.getResource(url);
21+
@Test
22+
public void checkSecure() throws IOException, URISyntaxException {
23+
assertFalse(checkResourceFile("/spring-boot-2.5.7.jar"));
24+
assertFalse(checkResourceFile("/log4j-core-2.15.0.jar"));
25+
}
2926

30-
Log4JDetector detector = new Log4JDetector();
31-
return detector.run(new File(resource.toURI()));
32-
}
27+
private boolean checkResourceFile(String url) throws IOException, URISyntaxException {
28+
URL resource = Log4JDetectorTests.class.getResource(url);
29+
30+
Log4JDetector detector = new Log4JDetector();
31+
return detector.run(new File(resource.toURI()).getAbsolutePath());
32+
}
3333
}

0 commit comments

Comments
 (0)