Skip to content

Commit a1eaa9c

Browse files
committed
Added --zip-charset option. v2.3.3
1 parent eeb3cb6 commit a1eaa9c

File tree

7 files changed

+77
-32
lines changed

7 files changed

+77
-32
lines changed

README.md

+10-8
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,16 @@
33
log4j2-scan is a single binary command-line tool for CVE-2021-44228 vulnerability scanning and mitigation patch. It also supports nested JAR file scanning and patch. It also detects CVE-2021-45046 (log4j 2.15.0), CVE-2021-45105 (log4j 2.16.0), CVE-2021-4104 (log4j 1.x), and CVE-2021-42550 (logback 0.9-1.2.7) vulnerabilities.
44

55
### Download
6-
* [log4j2-scan 2.3.2 (Windows x64, 7z)](https://github.com/logpresso/CVE-2021-44228-Scanner/releases/download/v2.3.2/logpresso-log4j2-scan-2.3.2-win64.7z)
7-
* [log4j2-scan 2.3.2 (Windows x64, zip)](https://github.com/logpresso/CVE-2021-44228-Scanner/releases/download/v2.3.2/logpresso-log4j2-scan-2.3.2-win64.zip)
6+
* [log4j2-scan 2.3.3 (Windows x64, 7z)](https://github.com/logpresso/CVE-2021-44228-Scanner/releases/download/v2.3.3/logpresso-log4j2-scan-2.3.3-win64.7z)
7+
* [log4j2-scan 2.3.3 (Windows x64, zip)](https://github.com/logpresso/CVE-2021-44228-Scanner/releases/download/v2.3.3/logpresso-log4j2-scan-2.3.3-win64.zip)
88
* If you get `VCRUNTIME140.dll not found` error, install [Visual C++ Redistributable](https://docs.microsoft.com/en-US/cpp/windows/latest-supported-vc-redist?view=msvc-170).
99
* If native executable doesn't work, use the JAR instead. 32bit is not supported.
1010
* 7zip is available from www.7zip.org, and is open source and free.
11-
* [log4j2-scan 2.3.2 (Linux x64)](https://github.com/logpresso/CVE-2021-44228-Scanner/releases/download/v2.3.2/logpresso-log4j2-scan-2.3.2-linux.tar.gz)
12-
* [log4j2-scan 2.3.2 (Linux aarch64)](https://github.com/logpresso/CVE-2021-44228-Scanner/releases/download/v2.3.2/logpresso-log4j2-scan-2.3.2-linux-aarch64.tar.gz)
11+
* [log4j2-scan 2.3.3 (Linux x64)](https://github.com/logpresso/CVE-2021-44228-Scanner/releases/download/v2.3.3/logpresso-log4j2-scan-2.3.3-linux.tar.gz)
12+
* [log4j2-scan 2.3.3 (Linux aarch64)](https://github.com/logpresso/CVE-2021-44228-Scanner/releases/download/v2.3.3/logpresso-log4j2-scan-2.3.3-linux-aarch64.tar.gz)
1313
* If native executable doesn't work, use the JAR instead. 32bit is not supported.
14-
* [log4j2-scan 2.3.2 (Mac OS)](https://github.com/logpresso/CVE-2021-44228-Scanner/releases/download/v2.3.2/logpresso-log4j2-scan-2.3.2-darwin.tar.gz)
15-
* [log4j2-scan 2.3.2 (Any OS, 20KB)](https://github.com/logpresso/CVE-2021-44228-Scanner/releases/download/v2.3.2/logpresso-log4j2-scan-2.3.2.jar)
14+
* [log4j2-scan 2.3.3 (Mac OS)](https://github.com/logpresso/CVE-2021-44228-Scanner/releases/download/v2.3.3/logpresso-log4j2-scan-2.3.3-darwin.tar.gz)
15+
* [log4j2-scan 2.3.3 (Any OS, 20KB)](https://github.com/logpresso/CVE-2021-44228-Scanner/releases/download/v2.3.3/logpresso-log4j2-scan-2.3.3.jar)
1616

1717
### Build
1818
* [How to build Native Image](https://github.com/logpresso/CVE-2021-44228-Scanner/wiki/FAQ#how-to-build-native-image)
@@ -22,7 +22,7 @@ Just run log4j2-scan.exe or log4j2-scan with target directory path. The logpress
2222

2323
Usage
2424
```
25-
Logpresso CVE-2021-44228 Vulnerability Scanner 2.3.2 (2021-12-19)
25+
Logpresso CVE-2021-44228 Vulnerability Scanner 2.3.3 (2021-12-19)
2626
Usage: log4j2-scan [--scan-log4j1] [--fix] target_path1 target_path2
2727
2828
-f [config_file_path]
@@ -34,6 +34,8 @@ Usage: log4j2-scan [--scan-log4j1] [--fix] target_path1 target_path2
3434
Enables scanning for logback CVE-2021-42550.
3535
--scan-zip
3636
Scan also .zip extension files. This option may slow down scanning.
37+
--zip-charset
38+
Specify an alternate zip encoding other than utf-8. System default charset is used if not specified.
3739
--fix
3840
Backup original file and remove JndiLookup.class from JAR recursively.
3941
With --scan-log4j1 option, it also removes JMSAppender.class, SocketServer.class, SMTPAppender.class, SMTPAppender$1.class
@@ -83,7 +85,7 @@ On Linux
8385
```
8486
On UNIX (AIX, Solaris, and so on)
8587
```
86-
java -jar logpresso-log4j2-scan-2.3.2.jar [--fix] target_path
88+
java -jar logpresso-log4j2-scan-2.3.3.jar [--fix] target_path
8789
```
8890

8991
If you add `--fix` option, this program will copy vulnerable original JAR file to .bak file, and create new JAR file without `org/apache/logging/log4j/core/lookup/JndiLookup.class` entry. In most environments, JNDI lookup feature will not be used. However, you must use this option at your own risk. Depending the Operating System:

pom.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
<modelVersion>4.0.0</modelVersion>
77
<groupId>com.logpresso</groupId>
88
<artifactId>log4j2-scanner</artifactId>
9-
<version>2.3.2</version>
9+
<version>2.3.3</version>
1010
<packaging>jar</packaging>
1111
<name>Logpresso Log4j2 Scanner</name>
1212

src/main/java/com/logpresso/scanner/Configuration.java

+14-3
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import java.io.FileInputStream;
66
import java.io.IOException;
77
import java.io.InputStreamReader;
8+
import java.nio.charset.Charset;
89
import java.util.ArrayList;
910
import java.util.HashSet;
1011
import java.util.LinkedList;
@@ -31,6 +32,7 @@ public class Configuration {
3132
private boolean scanForLogback = false;
3233
private boolean noEmptyReport = false;
3334
private boolean oldExitCode = false;
35+
private Charset zipCharset = null;
3436

3537
private String reportPath = null;
3638
private String reportDir = null;
@@ -52,6 +54,9 @@ public static void pringUsage() {
5254
System.out.println("\tEnables scanning for logback CVE-2021-42550.");
5355
System.out.println("--scan-zip");
5456
System.out.println("\tScan also .zip extension files. This option may slow down scanning.");
57+
System.out.println("--zip-charset");
58+
System.out.println(
59+
"\tSpecify an alternate zip encoding other than utf-8. System default charset is used if not specified.");
5560
System.out.println("--fix");
5661
System.out.println("\tBackup original file and remove JndiLookup.class from JAR recursively.");
5762
System.out.println(
@@ -113,6 +118,10 @@ public static Configuration parseArguments(String[] args) throws Exception {
113118
c.silent = true;
114119
} else if (args[i].equals("--scan-zip")) {
115120
c.scanZip = true;
121+
} else if (args[i].equals("--zip-charset")) {
122+
verifyArgument(args, i, "ZIP Charset", "Specify zip entry encoding.");
123+
c.zipCharset = Charset.forName(args[i + 1]);
124+
i++;
116125
} else if (args[i].equals("--no-symlink")) {
117126
c.noSymlink = true;
118127
} else if (args[i].equals("--scan-log4j1")) {
@@ -123,10 +132,8 @@ public static Configuration parseArguments(String[] args) throws Exception {
123132
pringUsage();
124133
System.exit(-1);
125134
} else if (args[i].equals("-f")) {
126-
verifyArgument(args, i, "Tarqget file path", "Specify target file path.");
135+
verifyArgument(args, i, "Input config file", "Specify input config file path.");
127136
c.includeFilePath = args[i + 1];
128-
if (c.includeFilePath.startsWith("--"))
129-
throw new IllegalArgumentException("Path should not starts with `--`. Specify include file path.");
130137

131138
File f = new File(c.includeFilePath);
132139
if (!f.exists())
@@ -376,6 +383,10 @@ public boolean isScanZip() {
376383
return scanZip;
377384
}
378385

386+
public Charset getZipCharset() {
387+
return zipCharset;
388+
}
389+
379390
public boolean isNoSymlink() {
380391
return noSymlink;
381392
}

src/main/java/com/logpresso/scanner/Detector.java

+28-6
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import java.io.FileInputStream;
55
import java.io.IOException;
66
import java.io.InputStream;
7+
import java.nio.charset.Charset;
78
import java.util.ArrayList;
89
import java.util.Arrays;
910
import java.util.HashSet;
@@ -107,9 +108,21 @@ public List<ReportEntry> getReportEntries(File f) {
107108
protected void scanJarFile(File jarFile, boolean fix) {
108109
InputStream is = null;
109110

111+
Charset altCharset = null;
110112
try {
111113
is = new FileInputStream(jarFile);
112-
DetectResult result = scanStream(jarFile, is, new ArrayList<String>());
114+
DetectResult result = null;
115+
116+
try {
117+
result = scanStream(jarFile, is, new ArrayList<String>(), Charset.forName("utf-8"));
118+
} catch (IllegalArgumentException e) {
119+
// second try with system encoding or alternative encoding
120+
altCharset = Charset.defaultCharset();
121+
if (config.getZipCharset() != null)
122+
altCharset = config.getZipCharset();
123+
124+
result = scanStream(jarFile, is, new ArrayList<String>(), altCharset);
125+
}
113126

114127
if (result.isVulnerable())
115128
vulnerableFileCount++;
@@ -119,7 +132,7 @@ else if (result.isPotentiallyVulnerable())
119132
potentiallyVulnerableFileCount++;
120133

121134
if (fix && result.isFixRequired())
122-
vulnerableFiles.add(new VulnerableFile(jarFile, result.hasNestedJar()));
135+
vulnerableFiles.add(new VulnerableFile(jarFile, result.hasNestedJar(), altCharset));
123136

124137
} catch (ZipException e) {
125138
// ignore broken zip file
@@ -145,7 +158,7 @@ else if (result.isPotentiallyVulnerable())
145158
}
146159
}
147160

148-
private DetectResult scanStream(File jarFile, InputStream is, List<String> pathChain) throws IOException {
161+
private DetectResult scanStream(File jarFile, InputStream is, List<String> pathChain, Charset charset) throws IOException {
149162
ZipInputStream zis = null;
150163
DetectResult result = new DetectResult();
151164
String log4j2Version = null;
@@ -203,7 +216,7 @@ private DetectResult scanStream(File jarFile, InputStream is, List<String> pathC
203216
if (ZipUtils.isScanTarget(entry.getName(), config.isScanZip())) {
204217
pathChain.add(entry.getName());
205218

206-
DetectResult nestedResult = scanStream(jarFile, zis, pathChain);
219+
DetectResult nestedResult = scanStream(jarFile, zis, pathChain, charset);
207220
result.merge(nestedResult);
208221

209222
pathChain.remove(pathChain.size() - 1);
@@ -247,8 +260,7 @@ private DetectResult scanStream(File jarFile, InputStream is, List<String> pathC
247260
return result;
248261
} catch (IOException e) {
249262
// ignore WinRAR
250-
String entryName = pathChain.get(pathChain.size() - 1);
251-
if (entryName.toLowerCase().endsWith(".rar"))
263+
if (isWinRarFile(jarFile, pathChain))
252264
return result;
253265

254266
throw e;
@@ -257,6 +269,16 @@ private DetectResult scanStream(File jarFile, InputStream is, List<String> pathC
257269
}
258270
}
259271

272+
private boolean isWinRarFile(File jarFile, List<String> pathChain) {
273+
String fileName = null;
274+
if (pathChain.isEmpty())
275+
fileName = jarFile.getName();
276+
else
277+
pathChain.get(pathChain.size() - 1);
278+
279+
return fileName.toLowerCase().endsWith(".rar");
280+
}
281+
260282
private String loadVulnerableLog4jVersion(InputStream is) throws IOException {
261283
Properties props = new Properties();
262284
props.load(is);

src/main/java/com/logpresso/scanner/Log4j2Scanner.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
import com.logpresso.scanner.utils.ZipUtils;
1616

1717
public class Log4j2Scanner {
18-
private static final String BANNER = "Logpresso CVE-2021-44228 Vulnerability Scanner 2.3.2 (2021-12-19)";
18+
private static final String BANNER = "Logpresso CVE-2021-44228 Vulnerability Scanner 2.3.3 (2021-12-19)";
1919

2020
private static final boolean isWindows = File.separatorChar == '\\';
2121

@@ -222,7 +222,7 @@ private void fix() {
222222
Set<String> shadePatterns = detector.getShadePatterns();
223223

224224
if (ZipUtils.repackage(backupFile, f, removeTargets, shadePatterns, config.isScanZip(), vf.isNestedJar(),
225-
config.isDebug())) {
225+
config.isDebug(), vf.getAltCharset())) {
226226
metrics.addFixedFileCount();
227227

228228
System.out.printf("Fixed: %s%s%n", f.getAbsolutePath(), symlinkMsg);

src/main/java/com/logpresso/scanner/VulnerableFile.java

+8-1
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,20 @@
11
package com.logpresso.scanner;
22

33
import java.io.File;
4+
import java.nio.charset.Charset;
45

56
public class VulnerableFile implements Comparable<VulnerableFile> {
67
private File file;
78
private boolean nestedJar;
9+
private Charset altCharset;
810

9-
public VulnerableFile(File file, boolean nestedJar) {
11+
public VulnerableFile(File file, boolean nestedJar, Charset altCharset) {
1012
if (file == null)
1113
throw new IllegalArgumentException("file should be not null");
1214

1315
this.file = file;
1416
this.nestedJar = nestedJar;
17+
this.altCharset = altCharset;
1518
}
1619

1720
@Override
@@ -47,4 +50,8 @@ public File getFile() {
4750
public boolean isNestedJar() {
4851
return nestedJar;
4952
}
53+
54+
public Charset getAltCharset() {
55+
return altCharset;
56+
}
5057
}

src/main/java/com/logpresso/scanner/utils/ZipUtils.java

+14-11
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import java.io.IOException;
88
import java.io.InputStream;
99
import java.io.OutputStream;
10+
import java.nio.charset.Charset;
1011
import java.util.Enumeration;
1112
import java.util.HashSet;
1213
import java.util.Set;
@@ -49,14 +50,15 @@ private static boolean isShaded(String s, Set<String> shadePatterns) {
4950
}
5051

5152
public static boolean repackage(File srcFile, File dstFile, Set<String> removeTargets, Set<String> shadePatterns,
52-
boolean scanZip, boolean nested, boolean debug) {
53+
boolean scanZip, boolean nested, boolean debug, Charset altCharset) {
5354
Set<String> entryNames = new HashSet<String>();
5455
ZipFile srcZipFile = null;
5556
ZipOutputStream zos = null;
5657

5758
try {
58-
srcZipFile = new ZipFile(srcFile);
59-
zos = new ZipOutputStream(new FileOutputStream(dstFile));
59+
Charset charset = altCharset != null ? altCharset : Charset.forName("utf-8");
60+
srcZipFile = new ZipFile(srcFile, charset);
61+
zos = new ZipOutputStream(new FileOutputStream(dstFile), charset);
6062

6163
if (nested) {
6264
zos.setMethod(ZipOutputStream.STORED);
@@ -91,7 +93,7 @@ public static boolean repackage(File srcFile, File dstFile, Set<String> removeTa
9193
continue;
9294
}
9395

94-
copyZipEntry(srcZipFile, entry, zos, removeTargets, scanZip, nested);
96+
copyZipEntry(srcZipFile, entry, zos, removeTargets, scanZip, nested, altCharset);
9597
}
9698

9799
return true;
@@ -109,15 +111,15 @@ public static boolean repackage(File srcFile, File dstFile, Set<String> removeTa
109111
}
110112

111113
private static void copyZipEntry(ZipFile srcZipFile, ZipEntry zipEntry, ZipOutputStream zos, Set<String> removeTargets,
112-
boolean scanZip, boolean nested) throws IOException {
114+
boolean scanZip, boolean nested, Charset altCharset) throws IOException {
113115
InputStream is = null;
114116
try {
115117
is = srcZipFile.getInputStream(zipEntry);
116118

117119
ByteArrayOutputStream bos = new ByteArrayOutputStream();
118120

119121
if (isScanTarget(zipEntry.getName(), scanZip)) {
120-
copyNestedJar(is, bos, removeTargets, scanZip);
122+
copyNestedJar(is, bos, removeTargets, scanZip, altCharset);
121123
} else {
122124
byte[] buf = new byte[32768];
123125
while (true) {
@@ -147,16 +149,17 @@ private static void copyZipEntry(ZipFile srcZipFile, ZipEntry zipEntry, ZipOutpu
147149
}
148150
}
149151

150-
private static void copyNestedJar(InputStream is, OutputStream os, Set<String> removeTargets, boolean scanZip)
151-
throws IOException {
152+
private static void copyNestedJar(InputStream is, OutputStream os, Set<String> removeTargets, boolean scanZip,
153+
Charset altCharset) throws IOException {
152154
// check duplicated entry exception
153155
Set<String> entryNames = new HashSet<String>();
154156

155157
ZipInputStream zis = null;
156158
ZipOutputStream zos = null;
157159
try {
158-
zis = new ZipInputStream(new DummyInputStream(is));
159-
zos = new ZipOutputStream(os);
160+
Charset charset = altCharset != null ? altCharset : Charset.forName("utf-8");
161+
zis = new ZipInputStream(new DummyInputStream(is), charset);
162+
zos = new ZipOutputStream(os, charset);
160163

161164
while (true) {
162165
ZipEntry zipEntry = zis.getNextEntry();
@@ -178,7 +181,7 @@ private static void copyNestedJar(InputStream is, OutputStream os, Set<String> r
178181
// fix recursively
179182
ByteArrayOutputStream bos = new ByteArrayOutputStream();
180183
if (isScanTarget(zipEntry.getName(), scanZip)) {
181-
copyNestedJar(zis, bos, removeTargets, scanZip);
184+
copyNestedJar(zis, bos, removeTargets, scanZip, altCharset);
182185
} else {
183186
byte[] buf = new byte[32768];
184187
while (true) {

0 commit comments

Comments
 (0)