11package de .codeshield .log4jshell ;
22
3+ import java .io .BufferedInputStream ;
4+ import java .io .BufferedOutputStream ;
35import java .io .File ;
6+ import java .io .FileInputStream ;
7+ import java .io .FileOutputStream ;
48import java .io .IOException ;
9+ import java .util .Collection ;
510import java .util .Enumeration ;
611import java .util .jar .JarEntry ;
712import 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}
0 commit comments