Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
xeraph committed Jan 27, 2022
1 parent c74f0f3 commit 8112f84
Show file tree
Hide file tree
Showing 7 changed files with 88 additions and 20 deletions.
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>com.logpresso</groupId>
<artifactId>log4j2-scanner</artifactId>
<version>2.7.2</version>
<version>2.8.0</version>
<packaging>jar</packaging>
<name>Logpresso Log4j2 Scanner</name>

Expand Down
3 changes: 2 additions & 1 deletion src/main/java/com/logpresso/scanner/Configuration.java
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,8 @@ public static void pringUsage() {
System.out.println("--fix");
System.out.println("\tBackup original file and remove JndiLookup.class from JAR recursively.");
System.out.println(
"\tWith --scan-log4j1 option, it also removes JMSAppender.class, SocketServer.class, SMTPAppender.class, SMTPAppender$1.class");
"\tWith --scan-log4j1 option, it also removes JMSAppender.class, SocketServer.class, SMTPAppender.class, SMTPAppender$1.class,\n"
+ "\tJMSSink.class, JDBCAppender.class, and all classes of org.apache.log4j.chainsaw package\t");
System.out.println("--force-fix");
System.out.println("\tDo not prompt confirmation. Don't use this option unless you know what you are doing.");
System.out.println("--restore [backup_file_path]");
Expand Down
5 changes: 5 additions & 0 deletions src/main/java/com/logpresso/scanner/DeleteTargetChecker.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.logpresso.scanner;

public interface DeleteTargetChecker {
boolean isTarget(String entryPath);
}
28 changes: 25 additions & 3 deletions src/main/java/com/logpresso/scanner/Detector.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,13 @@ public class Detector {
private static final String LOG4J_CORE_POM_PROPS = "META-INF/maven/org.apache.logging.log4j/log4j-core/pom.properties";

private static final String LOG4J_12_CORE_POM_PROPS = "META-INF/maven/log4j/log4j/pom.properties";
private static final String LOG4J_12_HUP = "org/apache/log4j/varia/HUP.class";
private static final String LOG4J_12_JMSAPPENDER = "org/apache/log4j/net/JMSAppender.class";
private static final String LOG4J_12_JMSAPPENDER_SHADE_PATH = "/log4j/net/JMSAppender.class";
private static final String LOG4J_12_JMSSINK = "org/apache/log4j/net/JMSSink.class";
private static final String LOG4J_12_JMSSINK_SHADE_PATH = "/log4j/net/JMSSink.class";
private static final String LOG4J_12_JDBC_APPENDER = "org/apache/log4j/jdbc/JDBCAppender.class";
private static final String LOG4J_12_CHAINSAW = "org/apache/log4j/chainsaw/";

// CVE-2021-42550 (published at 2021-12-16): vulnerable if version <= 1.2.7
// logback 1.2.9 moved JNDIUtil.class to core package
Expand Down Expand Up @@ -82,11 +85,17 @@ public List<ReportEntry> getErrorReports() {
return errorReports;
}

public DeleteTargetChecker getDeleteTargetChecker() {
return new Log4j2DeleteTargetChecker(config.isScanForLog4j1());
}

public Set<String> getVulnerableEntries() {
Set<String> targets = new HashSet<String>();
if (config.isScanForLog4j1()) {
targets.add("org/apache/log4j/chainsaw/");
targets.add("org/apache/log4j/jdbc/JDBCAppender.class");
for (String name : Arrays.asList("SocketServer.class", "JMSAppender.class", "SMTPAppender$1.class",
"SMTPAppender.class"))
"SMTPAppender.class", "JMSSink.class"))
targets.add("org/apache/log4j/net/" + name);
}

Expand Down Expand Up @@ -215,8 +224,11 @@ private DetectResult scanStream(File jarFile, ZipFileIterator it, List<String> p
boolean log4j2Mitigated = true;

// log4j1 class
boolean foundHUP = false;
boolean foundJmsAppender = false;
boolean foundJdbcAppender = false;
boolean foundJmsSink = false;
boolean foundChainsaw = false;

// logback class
boolean foundJndiUtil = false;
Expand Down Expand Up @@ -246,17 +258,26 @@ private DetectResult scanStream(File jarFile, ZipFileIterator it, List<String> p
if (entry.getName().equals(LOG4J_12_CORE_POM_PROPS))
log4j1Version = loadLog4j1Version(is);

if (entry.getName().equals(LOG4J_12_HUP))
foundHUP = true;

if (entry.getName().equals(LOG4J_12_JMSAPPENDER))
foundJmsAppender = true;

if (entry.getName().equals(LOG4J_12_JMSSINK))
foundJmsSink = true;

if (entry.getName().startsWith(LOG4J_12_CHAINSAW))
foundChainsaw = true;

if (entry.getName().endsWith(LOG4J_12_JMSAPPENDER_SHADE_PATH))
shadedJmsAppenderPaths.add(entry.getName());

if (entry.getName().endsWith(LOG4J_12_JMSSINK_SHADE_PATH))
foundJmsSink = true;

if (entry.getName().endsWith(LOG4J_12_JDBC_APPENDER))
foundJdbcAppender = true;
}

if (config.isScanForLogback()) {
Expand Down Expand Up @@ -300,8 +321,9 @@ private DetectResult scanStream(File jarFile, ZipFileIterator it, List<String> p
result.setPotentiallyVulnerableLog4j2();
}

boolean log4j1Found = log4j1Version != null || foundJmsAppender || foundJmsSink;
boolean log4j1Mitigated = !foundJmsAppender;
boolean log4j1Found = log4j1Version != null || foundJmsAppender || foundHUP;
boolean log4j1Mitigated = !(foundJmsAppender || foundJmsSink || foundJmsAppender || foundJdbcAppender
|| foundChainsaw);
log4j1Mitigated &= shadedJmsAppenderPaths.isEmpty();

if (log4j1Found) {
Expand Down
36 changes: 36 additions & 0 deletions src/main/java/com/logpresso/scanner/Log4j2DeleteTargetChecker.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.logpresso.scanner;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

public class Log4j2DeleteTargetChecker implements DeleteTargetChecker {

private static final String JNDI_LOOKUP_CLASS_PATH = "org/apache/logging/log4j/core/lookup/JndiLookup.class";
private boolean includeLog4j1;
private Set<String> targets;

public Log4j2DeleteTargetChecker(boolean includeLog4j1) {
this.includeLog4j1 = includeLog4j1;

targets = new HashSet<String>();
if (includeLog4j1) {
targets.add("org/apache/log4j/jdbc/JDBCAppender.class");
for (String name : Arrays.asList("SocketServer.class", "JMSAppender.class", "SMTPAppender$1.class",
"SMTPAppender.class", "JMSSink.class"))
targets.add("org/apache/log4j/net/" + name);
}

targets.add(JNDI_LOOKUP_CLASS_PATH);

}

@Override
public boolean isTarget(String entryPath) {
if (targets.contains(entryPath))
return true;

return includeLog4j1 && entryPath.startsWith("org/apache/log4j/chainsaw/");
}

}
14 changes: 8 additions & 6 deletions src/main/java/com/logpresso/scanner/Log4j2Scanner.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@
import com.logpresso.scanner.utils.ZipUtils;

public class Log4j2Scanner {
public static final String VERSION = "2.7.2";
public static final String RELEASE_DATE = "2022-01-11";
public static final String VERSION = "2.8.0";
public static final String RELEASE_DATE = "2022-01-27";
public static final String BANNER = "Logpresso CVE-2021-44228 Vulnerability Scanner " + VERSION + " (" + RELEASE_DATE + ")";

private static final boolean isWindows = File.separatorChar == '\\';
Expand Down Expand Up @@ -62,7 +62,7 @@ public int run(String[] args) throws Exception {
try {
if (config.isScanForLog4j1()) {
System.out.print("This command will remove JndiLookup.class from log4j2-core binaries and "
+ "remove JMSAppender.class, SocketServer.class, SMTPAppender.class, SMTPAppender$1.class "
+ "remove JMSSink.class, JMSAppender.class, SocketServer.class, SMTPAppender.class, SMTPAppender$1.class, JDBCAppender.class, org.apache.log4j.chainsaw package "
+ "from log4j1-core binaries. Are you sure [y/N]? ");
} else {
System.out.print("This command will remove JndiLookup.class from log4j2-core binaries. Are you sure [y/N]? ");
Expand Down Expand Up @@ -329,7 +329,9 @@ private void fix() {
needFix = true;
}

String except = " (except " + StringUtils.join(exceptCves, ", ") + ")";
String except = "";
if (!exceptCves.isEmpty())
except = " (except " + StringUtils.join(exceptCves, ", ") + ")";

if (!needFix) {
System.out.printf("Cannot fix " + StringUtils.join(exceptCves, ", ") + ", Upgrade it: %s%s%n",
Expand Down Expand Up @@ -364,8 +366,8 @@ private void fix() {
Set<String> shadePatterns = detector.getShadePatterns();

try {
ZipUtils.repackage(backupFile, f, removeTargets, shadePatterns, config.isScanZip(), vf.isNestedJar(),
config.isDebug(), vf.getAltCharset());
ZipUtils.repackage(backupFile, f, detector.getDeleteTargetChecker(), shadePatterns, config.isScanZip(),
vf.isNestedJar(), config.isDebug(), vf.getAltCharset());

// update fixed status
for (ReportEntry entry : entries) {
Expand Down
20 changes: 11 additions & 9 deletions src/main/java/com/logpresso/scanner/utils/ZipUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream;

import com.logpresso.scanner.DeleteTargetChecker;

public class ZipUtils {

public static boolean isZipFile(File f) {
Expand Down Expand Up @@ -63,7 +65,7 @@ private static boolean isShaded(String s, Set<String> shadePatterns) {
return false;
}

public static void repackage(File srcFile, File dstFile, Set<String> removeTargets, Set<String> shadePatterns,
public static void repackage(File srcFile, File dstFile, DeleteTargetChecker deleteTargetChecker, Set<String> shadePatterns,
boolean scanZip, boolean nested, boolean debug, Charset altCharset) throws IOException {
Set<String> entryNames = new HashSet<String>();
ZipFile srcZipFile = null;
Expand All @@ -83,7 +85,7 @@ public static void repackage(File srcFile, File dstFile, Set<String> removeTarge
while (e.hasMoreElements()) {
ZipEntry entry = (ZipEntry) e.nextElement();

if (removeTargets.contains(entry.getName()))
if (deleteTargetChecker.isTarget(entry.getName()))
continue;

if (isShaded(entry.getName(), shadePatterns))
Expand All @@ -107,7 +109,7 @@ public static void repackage(File srcFile, File dstFile, Set<String> removeTarge
continue;
}

copyZipEntry(srcZipFile, entry, zos, removeTargets, scanZip, nested, altCharset);
copyZipEntry(srcZipFile, entry, zos, deleteTargetChecker, scanZip, nested, altCharset);
}

} finally {
Expand All @@ -116,16 +118,16 @@ public static void repackage(File srcFile, File dstFile, Set<String> removeTarge
}
}

private static void copyZipEntry(ZipFile srcZipFile, ZipEntry zipEntry, ZipOutputStream zos, Set<String> removeTargets,
boolean scanZip, boolean nested, Charset altCharset) throws IOException {
private static void copyZipEntry(ZipFile srcZipFile, ZipEntry zipEntry, ZipOutputStream zos,
DeleteTargetChecker deleteTargetChecker, boolean scanZip, boolean nested, Charset altCharset) throws IOException {
InputStream is = null;
try {
is = srcZipFile.getInputStream(zipEntry);

ByteArrayOutputStream bos = new ByteArrayOutputStream();

if (isScanTarget(zipEntry.getName(), scanZip)) {
copyNestedJar(is, bos, removeTargets, scanZip, altCharset);
copyNestedJar(is, bos, deleteTargetChecker, scanZip, altCharset);
} else {
byte[] buf = new byte[32768];
while (true) {
Expand Down Expand Up @@ -155,7 +157,7 @@ private static void copyZipEntry(ZipFile srcZipFile, ZipEntry zipEntry, ZipOutpu
}
}

private static void copyNestedJar(InputStream is, OutputStream os, Set<String> removeTargets, boolean scanZip,
private static void copyNestedJar(InputStream is, OutputStream os, DeleteTargetChecker deleteTargetChecker, boolean scanZip,
Charset altCharset) throws IOException {
// check duplicated entry exception
Set<String> entryNames = new HashSet<String>();
Expand All @@ -172,7 +174,7 @@ private static void copyNestedJar(InputStream is, OutputStream os, Set<String> r
if (zipEntry == null)
break;

if (removeTargets.contains(zipEntry.getName()))
if (deleteTargetChecker.isTarget(zipEntry.getName()))
continue;

if (zipEntry.isDirectory()) {
Expand All @@ -187,7 +189,7 @@ private static void copyNestedJar(InputStream is, OutputStream os, Set<String> r
// fix recursively
ByteArrayOutputStream bos = new ByteArrayOutputStream();
if (isScanTarget(zipEntry.getName(), scanZip)) {
copyNestedJar(zis, bos, removeTargets, scanZip, altCharset);
copyNestedJar(zis, bos, deleteTargetChecker, scanZip, altCharset);
} else {
byte[] buf = new byte[32768];
while (true) {
Expand Down

0 comments on commit 8112f84

Please sign in to comment.