Skip to content

Commit

Permalink
Merge pull request #10 from CodeShield-Security/improve/psParsing
Browse files Browse the repository at this point in the history
Improve/ps parsing
  • Loading branch information
anddann authored Jan 7, 2022
2 parents b5517d0 + 8c875e4 commit cdc532f
Show file tree
Hide file tree
Showing 10 changed files with 108 additions and 67 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Log4jShell Bytecode Detector is an open source tool that helps identify if a jar

## 📝 How to run

1. Download the [jar file](https://github.com/CodeShield-Security/Log4JShell-Bytecode-Detector/releases/download/v0.4/Log4JDetector-0.4-jar-with-dependencies.jar) under releases.
1. Download the [jar file](https://github.com/CodeShield-Security/Log4JShell-Bytecode-Detector/releases/download/v0.6.2/Log4JDetector-0.6.2-jar-with-dependencies.jar) under releases.
2. Run `java -cp <PATH_TO_DOWNLOADED_JAR> de.codeshield.log4jshell.Log4JDetector <ABSOLUTE_PATH_TO_JAR_TO_CHECK>`


Expand All @@ -21,7 +21,7 @@ CVE-2021-44228 found in class file org/apache/logging/log4j/core/net/JndiManager

## 📝 How to run on a live server (no need to stop your running Java instances)

1. Download the [jar file](https://github.com/CodeShield-Security/Log4JShell-Bytecode-Detector/releases/download/v0.5/Log4JDetector-0.5-jar-with-dependencies.jar) under releases.
1. Download the [jar file](https://github.com/CodeShield-Security/Log4JShell-Bytecode-Detector/releases/download/v0.6.2/Log4JDetector-0.6.2-jar-with-dependencies.jar) under releases.
2. Run `java -cp <PATH_TO_DOWNLOADED_JAR> de.codeshield.log4jshell.Log4JProcessDetector`
3. The jar searches the classpath of all running java processes for vulnerable log4j instances

Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

<groupId>de.codeshield.log4shell</groupId>
<artifactId>Log4JDetector</artifactId>
<version>0.6.1</version>
<version>0.6.2</version>

<name>cve-2021-44228-detector</name>
<url>https://codeshield.io</url>
Expand Down
3 changes: 2 additions & 1 deletion src/main/java/de/codeshield/log4jshell/ClassDetector.java
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
package de.codeshield.log4jshell;

import de.codeshield.log4jshell.data.VulnerableClassSHAData;
import org.apache.commons.codec.digest.DigestUtils;

import java.io.IOException;
import java.io.InputStream;
import java.util.Set;
import org.apache.commons.codec.digest.DigestUtils;

public class ClassDetector {

Expand Down
31 changes: 14 additions & 17 deletions src/main/java/de/codeshield/log4jshell/Log4JDetector.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
package de.codeshield.log4jshell;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.filefilter.DirectoryFileFilter;
import org.apache.commons.io.filefilter.RegexFileFilter;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
Expand All @@ -10,9 +14,6 @@
import java.util.Enumeration;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.filefilter.DirectoryFileFilter;
import org.apache.commons.io.filefilter.RegexFileFilter;

/**
* A simple command line tool that scans a jar file for the CVE-2021-44228 vulnerability that
Expand All @@ -37,7 +38,8 @@ public static void main(String[] args) throws IOException {
detector.run(args[0]);
}

//Taken from https://stackoverflow.com/questions/981578/how-to-unzip-files-recursively-in-java/7108813#7108813
// Taken from
// https://stackoverflow.com/questions/981578/how-to-unzip-files-recursively-in-java/7108813#7108813
public static String extractFolder(String zipFile) throws IOException {
int buffer = 2048;
File file = new File(zipFile);
Expand Down Expand Up @@ -89,30 +91,26 @@ public static String extractFolder(String zipFile) throws IOException {

public boolean run(String pathToJarFile) throws IOException {
String folder = extractFolder(pathToJarFile);
Collection<File> pomFiles = FileUtils.listFiles(
new File(folder),
new RegexFileFilter("^(pom.xml)"),
DirectoryFileFilter.DIRECTORY
);
Collection<File> pomFiles =
FileUtils.listFiles(
new File(folder), new RegexFileFilter("^(pom.xml)"), DirectoryFileFilter.DIRECTORY);
boolean isVulnerable = false;
for (File pomFile : pomFiles) {
try (FileInputStream is = new FileInputStream(pomFile)) {
//Check if a pom file matches one of the pre-computed groupId:artifactId:version
// Check if a pom file matches one of the pre-computed groupId:artifactId:version
if (POMDetector.isVulnerablePOM(is)) {
isVulnerable = true;
System.err.println("CVE-2021-44228 found declared as dependency in " + pomFile);
}
}
}
Collection<File> classFiles = FileUtils.listFiles(
new File(folder),
new RegexFileFilter(".*.class$"),
DirectoryFileFilter.DIRECTORY
);
Collection<File> classFiles =
FileUtils.listFiles(
new File(folder), new RegexFileFilter(".*.class$"), DirectoryFileFilter.DIRECTORY);

for (File classFile : classFiles) {
try (FileInputStream is = new FileInputStream(classFile)) {
//Check if a class file matches one of the pre-computed vulnerable SHAs.
// Check if a class file matches one of the pre-computed vulnerable SHAs.
if (ClassDetector.isVulnerableClass(is)) {
isVulnerable = true;
System.err.println("CVE-2021-44228 found declared as dependency in " + classFile);
Expand All @@ -125,5 +123,4 @@ public boolean run(String pathToJarFile) throws IOException {
FileUtils.deleteDirectory(new File(folder));
return isVulnerable;
}

}
51 changes: 31 additions & 20 deletions src/main/java/de/codeshield/log4jshell/Log4JProcessDetector.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package de.codeshield.log4jshell;

import edu.emory.mathcs.backport.java.util.Collections;
import org.apache.commons.lang.StringUtils;

import java.io.BufferedReader;
Expand Down Expand Up @@ -30,32 +31,16 @@ public static void main(String[] args) throws IOException {
}

// analyze each output
// search for the "-classpath" parameter
for (String outputLine : lines) {
String searchStr = "-classpath";
int i = StringUtils.indexOf(outputLine, searchStr);
if (i == -1) {
// check if someone used -cp
searchStr = "-cp";
i = StringUtils.indexOf(outputLine, searchStr);
}

if (i > 0) {
String cpArgs = outputLine.substring(i + searchStr.length() + 1);

// scan for jar files
String[] cpArgsSplit = cpArgs.split(File.pathSeparator);
final List<String> foundJarsOnCp =
Arrays.stream(cpArgsSplit)
.map(x -> StringUtils.substring(x, 0, StringUtils.indexOf(x, ".jar") + 4))
.collect(Collectors.toList());

final List<String> foundJarsOnCp = parsePSOutPutClassPath(outputLine);
if (!foundJarsOnCp.isEmpty()) {
for (String jarFile : foundJarsOnCp) {
try {
Log4JDetector detector = new Log4JDetector();
System.out.println("Scanning jar file " + jarFile);
// detector.run(jarFile);
} catch (Exception e){
detector.run(jarFile);
} catch (Exception e) {
System.out.println("Could not scan jar file " + jarFile);
}
}
Expand All @@ -66,4 +51,30 @@ public static void main(String[] args) throws IOException {
}
}
}

public static List<String> parsePSOutPutClassPath(String outputLine) {
// search for the "-classpath" parameter

String searchStr = "-classpath";
int i = StringUtils.indexOf(outputLine, searchStr);
if (i == -1) {
// check if someone used -cp
searchStr = "-cp";
i = StringUtils.indexOf(outputLine, searchStr);
}
if (i > 0) {
String cpArgs = outputLine.substring(i + searchStr.length() + 1);

// scan for jar files
String[] cpArgsSplit = cpArgs.split(File.pathSeparator);
final List<String> foundJarsOnCp =
Arrays.stream(cpArgsSplit)
.filter(x -> StringUtils.indexOf(x, ".jar") > -1)
.map(x -> StringUtils.substring(x, 0, StringUtils.indexOf(x, ".jar") + 4))
.collect(Collectors.toList());
return foundJarsOnCp;
}

return Collections.emptyList();
}
}
29 changes: 16 additions & 13 deletions src/main/java/de/codeshield/log4jshell/POMDetector.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,21 @@

import de.codeshield.log4jshell.data.GAVWithClassifier;
import de.codeshield.log4jshell.data.VulnerableGavsData;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Set;
import org.apache.maven.model.Dependency;
import org.apache.maven.model.Model;
import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
import org.apache.maven.project.MavenProject;
import org.codehaus.plexus.util.xml.pull.XmlPullParserException;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Set;

public class POMDetector {

private static final Set<GAVWithClassifier> VULNERABLE_GAV_DATA = VulnerableGavsData
.readDataFromCSV();
private static final Set<GAVWithClassifier> VULNERABLE_GAV_DATA =
VulnerableGavsData.readDataFromCSV();

public static boolean isVulnerablePOM(InputStream inputStream) {
final MavenXpp3Reader mavenreader = new MavenXpp3Reader();
Expand All @@ -24,21 +25,21 @@ public static boolean isVulnerablePOM(InputStream inputStream) {
model = mavenreader.read(inputStream);
MavenProject mavenProject = new MavenProject(model);

//Check whether the Maven Project or any of its parents is affected.
// Check whether the Maven Project or any of its parents is affected.
if (isVulnerableProject(mavenProject)) {
return true;
}

//Check if any of the dependencies is affected.
// Check if any of the dependencies is affected.
List dependencies = mavenProject.getDependencies();
for (Object dependency : dependencies) {
if (!(dependency instanceof Dependency)) {
continue;
}
Dependency dep = (Dependency) dependency;
if (VULNERABLE_GAV_DATA.contains(
new GAVWithClassifier(dep.getGroupId(), dep.getArtifactId(), dep.getVersion(),
dep.getClassifier()))) {
new GAVWithClassifier(
dep.getGroupId(), dep.getArtifactId(), dep.getVersion(), dep.getClassifier()))) {
return true;
}
}
Expand All @@ -50,11 +51,13 @@ public static boolean isVulnerablePOM(InputStream inputStream) {
return false;
}


private static boolean isVulnerableProject(MavenProject mavenProject) {
if (VULNERABLE_GAV_DATA.contains(
new GAVWithClassifier(mavenProject.getGroupId(), mavenProject.getArtifactId(),
mavenProject.getVersion(), ""))) {
new GAVWithClassifier(
mavenProject.getGroupId(),
mavenProject.getArtifactId(),
mavenProject.getVersion(),
""))) {
return true;
}
MavenProject parent = mavenProject.getParent();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@ public class GAVWithClassifier {
private final String version;
private final String classifier;

public GAVWithClassifier(String groupId, String artifactId, String version,
String classifier) {
public GAVWithClassifier(String groupId, String artifactId, String version, String classifier) {
this.groupId = groupId;
this.artifactId = artifactId;
this.version = version;
Expand All @@ -26,10 +25,10 @@ public boolean equals(Object o) {
return false;
}
GAVWithClassifier that = (GAVWithClassifier) o;
return Objects.equals(groupId, that.groupId) &&
Objects.equals(artifactId, that.artifactId) &&
Objects.equals(version, that.version) &&
Objects.equals(classifier, that.classifier);
return Objects.equals(groupId, that.groupId)
&& Objects.equals(artifactId, that.artifactId)
&& Objects.equals(version, that.version)
&& Objects.equals(classifier, that.classifier);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.opencsv.CSVReader;
import com.opencsv.exceptions.CsvException;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
Expand All @@ -22,12 +23,11 @@ public static Set<String> readDataFromCSV() {
vulnerableSHAs.add(vulnerableClassSha[1]);
}
csvReader.close();
} catch (IOException e) {
System.err.println("Error reading CSV file ("+CSV_DATA+")");
} catch (IOException e) {
System.err.println("Error reading CSV file (" + CSV_DATA + ")");
} catch (CsvException e) {
System.err.println("Error parsing CSV file ("+CSV_DATA+")");
System.err.println("Error parsing CSV file (" + CSV_DATA + ")");
}
return vulnerableSHAs;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.opencsv.CSVReader;
import com.opencsv.exceptions.CsvException;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
Expand All @@ -16,16 +17,16 @@ public class VulnerableGavsData {
public static Set<GAVWithClassifier> readDataFromCSV() {
InputStream resource = VulnerableGavsData.class.getResourceAsStream(CSV_DATA);
Set<GAVWithClassifier> vulnerableGavs = new HashSet<>();
try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(resource))) {
try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(resource))) {
CSVReader csvReader = new CSVReader(bufferedReader);
for (String[] dep : csvReader.readAll()) {
vulnerableGavs.add(new GAVWithClassifier(dep[0], dep[1], dep[2], dep[3]));
}
csvReader.close();
} catch (IOException e) {
System.err.println("Error reading CSV file ("+CSV_DATA+")");
System.err.println("Error reading CSV file (" + CSV_DATA + ")");
} catch (CsvException e) {
System.err.println("Error parsing CSV file ("+CSV_DATA+")");
System.err.println("Error parsing CSV file (" + CSV_DATA + ")");
}
return vulnerableGavs;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package de.codeshield.log4jshell;

import junit.framework.TestCase;
import org.junit.Test;

import java.util.List;

public class Log4JProcessDetectorTest extends TestCase {

@Test
public void testParsePSOutPutClassPathInvalid() {
String output =" 501 22825 22334 0 10:59am ttys000 0:00.01 grep --color=auto --exclude-dir=.bzr --exclude-dir=CVS --exclude-dir=.git --exclude-dir=.hg --exclude-dir=.svn --exclude-dir=.idea --exclude-dir=.tox java";

final List<String> strings = Log4JProcessDetector.parsePSOutPutClassPath(output);
assertNotNull(strings);
assertTrue(strings.isEmpty());

}


@Test
public void testParsePSOutPutClassPathValid() {
String output=" 501 21080 20151 0 10:49am ?? 0:09.00 /Applications/IntelliJ IDEA.app/Contents/jbr/Contents/Home/bin/java -Djava.awt.headless=true -Dmaven.defaultProjectBuilder.disableGlobalModelCache=true -Didea.version=2021.2.2 -Didea.maven.embedder.version=3.6.3 -Xmx768m -Dmaven.ext.class.path=/Applications/IntelliJ IDEA.app/Contents/plugins/maven/lib/maven-event-listener.jar -Dfile.encoding=UTF-8 -classpath /Applications/IntelliJ IDEA.app/Contents/lib/util.jar:/Applications/IntelliJ IDEA.app/Contents/lib/annotations.jar:/Applications/IntelliJ IDEA.app/Contents/plugins/maven/lib/lucene-core-2.4.1.jar:/Applications/IntelliJ IDEA.app/Contents/plugins/maven-model/lib/maven-model.jar:/Applications/IntelliJ IDEA.app/Contents/plugins/maven/lib/maven-server-api.jar:/Applications/IntelliJ IDEA.app/Contents/plugins/maven/lib/maven3-server-common.jar:/Applications/IntelliJ IDEA.app/Contents/plugins/maven/lib/maven3-server-lib/nexus-indexer-artifact-1.0.1.jar:/Applications/IntelliJ IDEA.app/Contents/plugins/maven/lib/maven3-server-lib/nexus-indexer-3.0.4.jar:/Applications/IntelliJ IDEA.app/Contents/plugins/maven/lib/maven3-server-lib/archetype-catalog-2.2.jar:/Applications/IntelliJ IDEA.app/Contents/plugins/maven/lib/maven3-server-lib/archetype-common-2.2.jar:/Applications/IntelliJ IDEA.app/Contents/plugins/maven/lib/maven3-server-lib/maven-dependency-tree-1.2.jar:/Applications/IntelliJ IDEA.app/Contents/plugins/maven/lib/maven3-server.jar:/Applications/IntelliJ IDEA.app/Contents/plugins/maven/lib/maven36-server.jar:/Applications/IntelliJ IDEA.app/Contents/plugins/maven/lib/maven3/lib/maven-plugin-api-3.6.3.jar:/Applications/IntelliJ IDEA.app/Contents/plugins/maven/lib/maven3/lib/maven-compat-3.6.3.jar:/Applications/IntelliJ IDEA.app/Contents/plugins/maven/lib/maven3/lib/maven-resolver-util-1.4.1.jar:/Applications/IntelliJ IDEA.app/Contents/plugins/maven/lib/maven3/lib/plexus-component-annotations-2.1.0.jar:/Applications/IntelliJ IDEA.app/Contents/plugins/maven/lib/maven3/lib/maven-settings-builder-3.6.3.jar:/Applications/IntelliJ IDEA.app/Contents/plugins/maven/lib/maven3/lib/plexus-cipher-1.7.jar:/Applications/IntelliJ IDEA.app/Contents/plugins/maven/lib/maven3/lib/plexus-interpolation-1.25.jar:/Applications/IntelliJ IDEA.app/Contents/plugins/maven/lib/maven3/lib/maven-resolver-spi-1.4.1.jar:/Applications/IntelliJ IDEA.app/Contents/plugins/maven/lib/maven3/lib/guice-4.2.1-no_aop.jar:/Applications/IntelliJ IDEA.app/Contents/plugins/maven/lib/maven3/lib/maven-artifact-3.6.3.jar:/Applications/IntelliJ IDEA.app/Contents/plugins/maven/lib/maven3/lib/maven-resolver-provider-3.6.3.jar:/Applications/IntelliJ IDEA.app/Contents/plugins/maven/lib/maven3/lib/cdi-api-1.0.jar:/Applications/IntelliJ IDEA.app/Contents/plugins/maven/lib/maven3/lib/plexus-utils-3.2.1.jar:/Applications/IntelliJ IDEA.app/Contents/plugins/maven/lib/maven3/lib/plexus-sec-dispatcher-1.4.jar:/Applications/IntelliJ IDEA.app/Contents/plugins/maven/lib/maven3/lib/maven-repository-metadata-3.6.3.jar:/Applications/IntelliJ IDEA.app/Contents/plugins/maven/lib/maven3/lib/commons-cli-1.4.jar:/Applications/IntelliJ IDEA.app/Contents/plugins/maven/lib/maven3/lib/maven-resolver-transport-wagon-1.4.1.jar:/Applications/IntelliJ IDEA.app/Contents/plugins/maven/lib/maven3/lib/commons-io-2.5.jar:/Applications/IntelliJ IDEA.app/Contents/plugins/maven/lib/maven3/lib/jansi-1.17.1.jar:/Applications/IntelliJ IDEA.app/Contents/plugins/maven/lib/maven3/lib/maven-model-builder-3.6.3.jar:/Applications/IntelliJ IDEA.app/Contents/plugins/maven/lib/maven3/lib/maven-embedder-3.6.3.jar:/Applications/IntelliJ IDEA.app/Contents/plugins/maven/lib/maven3/lib/wagon-file-3.3.4.jar:/Applications/IntelliJ IDEA.app/Contents/plugins/maven/lib/maven3/lib/org.eclipse.sisu.inject-0.3.4.jar:/Applications/IntelliJ IDEA.app/Contents/plugins/maven/lib/maven3/lib/maven-resolver-connector-basic-1.4.1.jar:/Applications/IntelliJ IDEA.app/Contents/plugins/maven/lib/maven3/lib/maven-settings-3.6.3.jar:/Applications/IntelliJ IDEA.app/Contents/plugins/maven/lib/maven3/lib/maven-model-3.6.3.jar:/Applications/IntelliJ IDEA.app/Contents/plugins/maven/lib/maven3/lib/maven-resolver-api-1.4.1.jar:/Applications/IntelliJ IDEA.app/Contents/plugins/maven/lib/maven3/lib/jcl-over-slf4j-1.7.29.jar:/Applications/IntelliJ IDEA.app/Contents/plugins/maven/lib/maven3/lib/wagon-http-3.3.4-shaded.jar:/Applications/IntelliJ IDEA.app/Contents/plugins/maven/lib/maven3/lib/org.eclipse.sisu.plexus-0.3.4.jar:/Applications/IntelliJ IDEA.app/Contents/plugins/maven/lib/maven3/lib/slf4j-api-1.7.29.jar:/Applications/IntelliJ IDEA.app/Contents/plugins/maven/lib/maven3/lib/maven-builder-support-3.6.3.jar:/Applications/IntelliJ IDEA.app/Contents/plugins/maven/lib/maven3/lib/jsoup-1.12.1.jar:/Applications/IntelliJ IDEA.app/Contents/plugins/maven/lib/maven3/lib/wagon-provider-api-3.3.4.jar:/Applications/IntelliJ IDEA.app/Contents/plugins/maven/lib/maven3/lib/maven-shared-utils-3.2.1.jar:/Applications/IntelliJ IDEA.app/Contents/plugins/maven/lib/maven3/lib/maven-resolver-impl-1.4.1.jar:/Applications/IntelliJ IDEA.app/Contents/plugins/maven/lib/maven3/lib/maven-core-3.6.3.jar:/Applications/IntelliJ IDEA.app/Contents/plugins/maven/lib/maven3/lib/javax.inject-1.jar:/Applications/IntelliJ IDEA.app/Contents/plugins/maven/lib/maven3/lib/commons-lang3-3.8.1.jar:/Applications/IntelliJ IDEA.app/Contents/plugins/maven/lib/maven3/lib/guava-25.1-android.jar:/Applications/IntelliJ IDEA.app/Contents/plugins/maven/lib/maven3/lib/jsr250-api-1.0.jar:/Applications/IntelliJ IDEA.app/Contents/plugins/maven/lib/maven3/boot/plexus-classworlds.license:/Applications/IntelliJ IDEA.app/Contents/plugins/maven/lib/maven3/boot/plexus-classworlds-2.6.0.jar org.jetbrains.idea.maven.server.RemoteMavenServer36";
final List<String> strings = Log4JProcessDetector.parsePSOutPutClassPath(output);
assertNotNull(strings);
assertFalse(strings.isEmpty());
assertEquals(55, strings.size());
}
}

0 comments on commit cdc532f

Please sign in to comment.