|
| 1 | +/* |
| 2 | + * This file is licensed to the public under the terms of the GNU Public License 3.0 |
| 3 | + * (aka GPLv3). |
| 4 | + * |
| 5 | + * To be clear, for the purposes of copyright law, any program ["The Importing Program"] that |
| 6 | + * imports this file (via Java's "import" mechanism or via Java reflection or via any |
| 7 | + * other software technique for importing or referencing functionality) is considered |
| 8 | + * a derivative work of this work, and must also comply with the conditions of the GPLv3 |
| 9 | + * license in The Importing Program's totality to be granted a copyright license to this work, |
| 10 | + * and must also use the same definition as defined here for what constitutes a derivative work |
| 11 | + * of itself. |
| 12 | + * |
| 13 | + */ |
1 | 14 | package com.mergebase.log4j;
|
2 | 15 |
|
3 | 16 | import java.io.BufferedInputStream;
|
|
14 | 27 | import java.util.Iterator;
|
15 | 28 | import java.util.List;
|
16 | 29 | import java.util.Locale;
|
| 30 | +import java.util.Properties; |
17 | 31 | import java.util.zip.ZipEntry;
|
18 | 32 | import java.util.zip.ZipInputStream;
|
19 | 33 |
|
| 34 | +import static com.mergebase.log4j.VersionComparator.compare; |
| 35 | + |
20 | 36 | public class Log4JDetector {
|
21 | 37 |
|
| 38 | + private static final String POM_PROPERTIES = "log4j-core/pom.properties".toLowerCase(Locale.ROOT); |
22 | 39 | private static final String FILE_OLD_LOG4J = "log4j/DailyRollingFileAppender.class".toLowerCase(Locale.ROOT);
|
23 | 40 | private static final String FILE_LOG4J_1 = "core/LogEvent.class".toLowerCase(Locale.ROOT);
|
24 | 41 | private static final String FILE_LOG4J_2 = "core/Appender.class".toLowerCase(Locale.ROOT);
|
@@ -146,13 +163,20 @@ public int compare(File f1, File f2) {
|
146 | 163 |
|
147 | 164 | /**
|
148 | 165 | * @param fileName name to examine for type
|
149 |
| - * @return 0 == zip, 1 == class, -1 = who knows... |
| 166 | + * @return 0 == zip, 1 == class, 2 = log4j-core/pom.properties, -1 = who knows... |
150 | 167 | */
|
151 | 168 | private static int fileType(String fileName) {
|
152 | 169 | int c = fileName.lastIndexOf('.');
|
153 | 170 | if (c >= 0) {
|
154 | 171 | String suffix = fileName.substring(c + 1);
|
155 |
| - if ("class".equalsIgnoreCase(suffix)) { |
| 172 | + |
| 173 | + // Special logic for "log4j-core/pom.properties" last-resort version source. |
| 174 | + if ("properties".equalsIgnoreCase(suffix)) { |
| 175 | + String lower = fileName.toLowerCase(Locale.ROOT); |
| 176 | + if (lower.endsWith(POM_PROPERTIES)) { |
| 177 | + return 2; |
| 178 | + } |
| 179 | + } else if ("class".equalsIgnoreCase(suffix)) { |
156 | 180 | return 1;
|
157 | 181 | } else if ("zip".equalsIgnoreCase(suffix)
|
158 | 182 | || "jar".equalsIgnoreCase(suffix)
|
@@ -206,6 +230,7 @@ private static void findLog4jRecursive(
|
206 | 230 | boolean isLog4j2_15_override = false;
|
207 | 231 | boolean isLog4j2_12_2 = false;
|
208 | 232 | boolean isLog4j2_12_2_override = false;
|
| 233 | + byte[] pomProperties = null; |
209 | 234 | ZipEntry ze;
|
210 | 235 | while (true) {
|
211 | 236 | try {
|
@@ -233,9 +258,12 @@ private static void findLog4jRecursive(
|
233 | 258 | int fileType = fileType(path);
|
234 | 259 | boolean isSubZip = fileType == 0;
|
235 | 260 | boolean isClassEntry = fileType == 1;
|
| 261 | + boolean isPomProperties = fileType == 2; |
236 | 262 | boolean needClassBytes = false;
|
237 | 263 |
|
238 |
| - if (isClassEntry && pathLower.endsWith(FILE_LOG4J_VULNERABLE)) { |
| 264 | + if (isPomProperties) { |
| 265 | + needClassBytes = true; |
| 266 | + } else if (isClassEntry && pathLower.endsWith(FILE_LOG4J_VULNERABLE)) { |
239 | 267 | needClassBytes = true;
|
240 | 268 | } else if (isClassEntry && pathLower.endsWith(FILE_LOG4J_SAFE_CONDITION1)) {
|
241 | 269 | needClassBytes = true;
|
@@ -288,12 +316,14 @@ public void close() {
|
288 | 316 | findLog4jRecursive(fullPath, recursiveZipper);
|
289 | 317 | } catch (Exception e) {
|
290 | 318 | System.err.println(fullPath + " FAILED: " + e);
|
291 |
| - e.printStackTrace(System.out); |
| 319 | + e.printStackTrace(System.err); |
292 | 320 | }
|
293 | 321 |
|
294 | 322 |
|
295 | 323 | } else {
|
296 |
| - if (pathLower.endsWith(FILE_OLD_LOG4J)) { |
| 324 | + if (pathLower.endsWith(POM_PROPERTIES)) { |
| 325 | + pomProperties = bytes; |
| 326 | + } else if (pathLower.endsWith(FILE_OLD_LOG4J)) { |
297 | 327 | isLog4J1_X = true;
|
298 | 328 | } else if (pathLower.endsWith(FILE_LOG4J_1)) {
|
299 | 329 | log4jProbe[0] = true;
|
@@ -330,7 +360,42 @@ public void close() {
|
330 | 360 | }
|
331 | 361 | }
|
332 | 362 |
|
| 363 | + |
333 | 364 | if (conditionsChecked) {
|
| 365 | + if (!log4jProbe[0] || !log4jProbe[1] || !log4jProbe[2] || !log4jProbe[3] || !log4jProbe[4]) { |
| 366 | + if (pomProperties != null) { |
| 367 | + System.err.println("-- Warning: " + zipPath + " does not contain Log4J bytecode, but claims it does."); |
| 368 | + ByteArrayInputStream byteIn = new ByteArrayInputStream(pomProperties); |
| 369 | + Properties p = new Properties(); |
| 370 | + try { |
| 371 | + p.load(byteIn); |
| 372 | + String version = p.getProperty("version"); |
| 373 | + if (version != null) { |
| 374 | + boolean isLog4j2 = compare("2", version) <= 0; |
| 375 | + if (isLog4j2) { |
| 376 | + log4jProbe = new boolean[]{true, true, true, true, true}; |
| 377 | + hasJndiLookup = compare("2.0-beta9", version) <= 0; |
| 378 | + hasJndiManager = compare("2.1", version) <= 0; |
| 379 | + isLog4j2_10 = compare("2.10.0", version) <= 0; |
| 380 | + isLog4j2_12_2 = version.startsWith("2.12.") && compare("2.12.2", version) <= 0; |
| 381 | + if (isLog4j2_12_2) { |
| 382 | + isLog4j2_12_2_override = false; |
| 383 | + } |
| 384 | + isLog4j2_15 = version.startsWith("2.15."); |
| 385 | + isLog4j2_16 = version.startsWith("2.16."); |
| 386 | + isLog4j2_17 = compare("2.17.0", version) <= 0; |
| 387 | + if (isLog4j2_15 || isLog4j2_16 || isLog4j2_17) { |
| 388 | + isLog4j2_15_override = false; |
| 389 | + } |
| 390 | + } |
| 391 | + } |
| 392 | + } catch (IOException ioe) { |
| 393 | + // invalid properties file!?! |
| 394 | + } |
| 395 | + } |
| 396 | + } |
| 397 | + |
| 398 | + |
334 | 399 | boolean isLog4j = false;
|
335 | 400 | boolean isLog4j_2_10_0 = false;
|
336 | 401 | boolean isLog4j_2_12_2 = false;
|
@@ -460,7 +525,7 @@ public void close() {
|
460 | 525 | findLog4jRecursive(zip, myZipper);
|
461 | 526 | } catch (Exception e) {
|
462 | 527 | System.err.println("-- Problem: " + zipFile.getPath() + " FAILED: " + e);
|
463 |
| - e.printStackTrace(System.out); |
| 528 | + e.printStackTrace(System.err); |
464 | 529 | } finally {
|
465 | 530 | myZipper.close();
|
466 | 531 | }
|
|
0 commit comments