Skip to content

Commit 84589f7

Browse files
vuln-fix: Zip Slip Vulnerability
This fixes a Zip-Slip vulnerability. This change does one of two things. This change either 1. Inserts a guard to protect against Zip Slip. OR 2. Replaces `dir.getCanonicalPath().startsWith(parent.getCanonicalPath())`, which is vulnerable to partial path traversal attacks, with the more secure `dir.getCanonicalFile().toPath().startsWith(parent.getCanonicalFile().toPath())`. For number 2, consider `"/usr/outnot".startsWith("/usr/out")`. The check is bypassed although `/outnot` is not under the `/out` directory. It's important to understand that the terminating slash may be removed when using various `String` representations of the `File` object. For example, on Linux, `println(new File("/var"))` will print `/var`, but `println(new File("/var", "/")` will print `/var/`; however, `println(new File("/var", "/").getCanonicalPath())` will print `/var`. Weakness: CWE-22: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal') Severity: High CVSSS: 7.4 Detection: CodeQL (https://codeql.github.com/codeql-query-help/java/java-zipslip/) & OpenRewrite (https://public.moderne.io/recipes/org.openrewrite.java.security.ZipSlip) Reported-by: Jonathan Leitschuh <[email protected]> Signed-off-by: Jonathan Leitschuh <[email protected]> Bug-tracker: JLLeitschuh/security-research#16 Co-authored-by: Moderne <[email protected]>
1 parent e2d5331 commit 84589f7

File tree

4 files changed

+26
-5
lines changed

4 files changed

+26
-5
lines changed

src/test/java/org/apache/commons/compress/archivers/JarTestCase.java

+3
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,9 @@ public void testJarUnarchive() throws Exception {
6161

6262
ZipArchiveEntry entry = (ZipArchiveEntry)in.getNextEntry();
6363
File o = new File(dir, entry.getName());
64+
if (!o.toPath().normalize().startsWith(dir.toPath().normalize())) {
65+
throw new RuntimeException("Bad zip entry");
66+
}
6467
o.getParentFile().mkdirs();
6568
OutputStream out = Files.newOutputStream(o.toPath());
6669
IOUtils.copy(in, out);

src/test/java/org/apache/commons/compress/archivers/ZipTestCase.java

+14-3
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,10 @@ public void testZipArchiveCreation() throws Exception {
9999
fileInputStream)) {
100100
ZipArchiveEntry entry = null;
101101
while ((entry = (ZipArchiveEntry) archiveInputStream.getNextEntry()) != null) {
102-
final File outfile = new File(resultDir.getCanonicalPath() + "/result/" + entry.getName());
102+
final File outfile = new File(resultDir.getCanonicalPath() + "/result/", entry.getName());
103+
if (!outfile.toPath().normalize().startsWith(resultDir.getCanonicalPath() + "/result/")) {
104+
throw new RuntimeException("Bad zip entry");
105+
}
103106
outfile.getParentFile().mkdirs();
104107
try (OutputStream o = Files.newOutputStream(outfile.toPath())) {
105108
IOUtils.copy(archiveInputStream, o);
@@ -168,7 +171,11 @@ public void testZipUnarchive() throws Exception {
168171
try (final InputStream is = Files.newInputStream(input.toPath());
169172
final ArchiveInputStream in = ArchiveStreamFactory.DEFAULT.createArchiveInputStream("zip", is)) {
170173
final ZipArchiveEntry entry = (ZipArchiveEntry) in.getNextEntry();
171-
try (final OutputStream out = Files.newOutputStream(new File(dir, entry.getName()).toPath())) {
174+
final File zipEntryFile = new File(dir, entry.getName());
175+
if (!zipEntryFile.toPath().normalize().startsWith(dir.toPath().normalize())) {
176+
throw new RuntimeException("Bad zip entry");
177+
}
178+
try (final OutputStream out = Files.newOutputStream(zipEntryFile.toPath())) {
172179
IOUtils.copy(in, out);
173180
}
174181
}
@@ -828,7 +835,11 @@ private File getFilesToZip() throws IOException {
828835
if (!outputFile.getParentFile().exists()) {
829836
outputFile.getParentFile().mkdirs();
830837
}
831-
outputFile = new File(dir, zipEntry.getName());
838+
final File zipEntryFile = new File(dir, zipEntry.getName());
839+
if (!zipEntryFile.toPath().normalize().startsWith(dir.toPath().normalize())) {
840+
throw new RuntimeException("Bad zip entry");
841+
}
842+
outputFile =zipEntryFile;
832843

833844
try (InputStream inputStream = zipFile.getInputStream(zipEntry);
834845
OutputStream outputStream = Files.newOutputStream(outputFile.toPath())) {

src/test/java/org/apache/commons/compress/archivers/zip/Lister.java

+4-1
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,9 @@ private static void list(final ZipArchiveEntry entry) {
9191
private static void extract(final String dir, final ZipArchiveEntry entry,
9292
final InputStream is) throws IOException {
9393
final File f = new File(dir, entry.getName());
94+
if (!f.toPath().normalize().startsWith(dir)) {
95+
throw new RuntimeException("Bad zip entry");
96+
}
9497
if (!f.getParentFile().exists()) {
9598
f.getParentFile().mkdirs();
9699
}
@@ -142,4 +145,4 @@ private static void usage() {
142145
+ " [+storeddd] [-extract dir] archive");
143146
System.exit(1);
144147
}
145-
}
148+
}

src/test/java/org/apache/commons/compress/archivers/zip/ZipFileIgnoringLocalFileHeaderTest.java

+5-1
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,11 @@ public void testZipUnarchive() throws Exception {
5555
try (final ZipFile zf = openZipWithoutLFH("bla.zip")) {
5656
for (final Enumeration<ZipArchiveEntry> e = zf.getEntries(); e.hasMoreElements(); ) {
5757
final ZipArchiveEntry entry = e.nextElement();
58-
try (final OutputStream out = Files.newOutputStream(new File(dir, entry.getName()).toPath())) {
58+
final File zipEntryFile = new File(dir, entry.getName());
59+
if (!zipEntryFile.toPath().normalize().startsWith(dir.toPath().normalize())) {
60+
throw new RuntimeException("Bad zip entry");
61+
}
62+
try (final OutputStream out = Files.newOutputStream(zipEntryFile.toPath())) {
5963
IOUtils.copy(zf.getInputStream(entry), out);
6064
}
6165
}

0 commit comments

Comments
 (0)