CVE-2018-8009
Description
Apache Hadoop 3.1.0, 3.0.0-alpha to 3.0.2, 2.9.0 to 2.9.1, 2.8.0 to 2.8.4, 2.0.0-alpha to 2.7.6, 0.23.0 to 0.23.11 is exploitable via the zip slip vulnerability in places that accept a zip file.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
Apache Hadoop is vulnerable to Zip Slip directory traversal, allowing an attacker to overwrite arbitrary files on the system.
Vulnerability
Apache Hadoop versions 3.1.0, 3.0.0-alpha to 3.0.2, 2.9.0 to 2.9.1, 2.8.0 to 2.8.4, 2.0.0-alpha to 2.7.6, and 0.23.0 to 0.23.11 are vulnerable to the Zip Slip vulnerability [1][3]. This directory traversal issue occurs when Hadoop processes a specially crafted zip file without validating the paths of entries within the archive. The flaw affects any component that accepts and extracts zip files.
Exploitation
An attacker must supply a malicious zip archive containing file entries with directory traversal sequences (e.g., ../../evil.sh). No special privileges are required beyond the ability to upload or provide a zip file to an affected Hadoop service. The extraction code does not sanitize entry paths, allowing files to be written outside the intended extraction directory [4].
Impact
Successful exploitation allows an attacker to overwrite arbitrary files on the Hadoop filesystem with attacker-controlled content. This can lead to remote code execution if executable files or configuration scripts are overwritten, or to denial of service by corrupting critical system files. The attacker gains write access at the privilege level of the Hadoop process [4].
Mitigation
Apache Hadoop released fixes in versions 2.7.7, 2.8.5, 2.9.2, 3.0.3, and 3.1.1 [1][3]. Users should upgrade to these or later versions. Red Hat Enterprise Linux users were provided an update via RHSA-2019:3892 [2]. No workarounds are documented for unpatched versions.
AI Insight generated on May 22, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
org.apache.hadoop:hadoop-mainMaven | >= 3.1.0, < 3.1.1 | 3.1.1 |
org.apache.hadoop:hadoop-mainMaven | >= 3.0.0, < 3.0.3 | 3.0.3 |
org.apache.hadoop:hadoop-mainMaven | >= 2.9.0, < 2.9.2 | 2.9.2 |
org.apache.hadoop:hadoop-mainMaven | >= 2.8.0, < 2.8.5 | 2.8.5 |
org.apache.hadoop:hadoop-mainMaven | < 2.7.7 | 2.7.7 |
Affected products
2- Apache Software Foundation/Apache Hadoopv5Range: Apache Hadoop 3.1.0, 3.0.0-alpha to 3.0.2, 2.9.0 to 2.9.1, 2.8.0 to 2.8.4, 2.0.0-alpha to 2.7.6, 0.23.0 to 0.23.11
Patches
106a4ae6f6eeedAdditional check when unpacking archives. Contributed by Wilfred Spiegelenburg.
2 files changed · +41 −0
hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/util/RunJar.java+5 −0 modified@@ -109,6 +109,7 @@ public static void unJar(File jarFile, File toDir, Pattern unpackRegex) throws IOException { try (JarFile jar = new JarFile(jarFile)) { int numOfFailedLastModifiedSet = 0; + String targetDirPath = toDir.getCanonicalPath() + File.separator; Enumeration<JarEntry> entries = jar.entries(); while (entries.hasMoreElements()) { final JarEntry entry = entries.nextElement(); @@ -117,6 +118,10 @@ public static void unJar(File jarFile, File toDir, Pattern unpackRegex) try (InputStream in = jar.getInputStream(entry)) { File file = new File(toDir, entry.getName()); ensureDirectory(file.getParentFile()); + if (!file.getCanonicalPath().startsWith(targetDirPath)) { + throw new IOException("expanding " + entry.getName() + + " would create file outside of " + toDir); + } try (OutputStream out = new FileOutputStream(file)) { IOUtils.copyBytes(in, out, BUFFER_SIZE); }
hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/util/TestRunJar.java+36 −0 modified@@ -20,12 +20,15 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.jar.JarEntry; import java.util.jar.JarOutputStream; import java.util.regex.Pattern; import java.util.zip.ZipEntry; @@ -165,4 +168,37 @@ public void testClientClassLoader() throws Throwable { runJar.run(args); // it should not throw an exception } + + @Test + public void testUnJar2() throws IOException { + // make a simple zip + File jarFile = new File(TEST_ROOT_DIR, TEST_JAR_NAME); + JarOutputStream jstream = + new JarOutputStream(new FileOutputStream(jarFile)); + JarEntry je = new JarEntry("META-INF/MANIFEST.MF"); + byte[] data = "Manifest-Version: 1.0\nCreated-By: 1.8.0_1 (Manual)" + .getBytes(StandardCharsets.UTF_8); + je.setSize(data.length); + jstream.putNextEntry(je); + jstream.write(data); + jstream.closeEntry(); + je = new JarEntry("../outside.path"); + data = "any data here".getBytes(StandardCharsets.UTF_8); + je.setSize(data.length); + jstream.putNextEntry(je); + jstream.write(data); + jstream.closeEntry(); + jstream.close(); + + File unjarDir = getUnjarDir("unjar-path"); + + // Unjar everything + try { + RunJar.unJar(jarFile, unjarDir); + fail("unJar should throw IOException."); + } catch (IOException e) { + GenericTestUtils.assertExceptionContains( + "would create file outside of", e); + } + } } \ No newline at end of file
6a4ae6f6eeedAdditional check when unpacking archives. Contributed by Wilfred Spiegelenburg.
2 files changed · +41 −0
hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/util/RunJar.java+5 −0 modified@@ -109,6 +109,7 @@ public static void unJar(File jarFile, File toDir, Pattern unpackRegex) throws IOException { try (JarFile jar = new JarFile(jarFile)) { int numOfFailedLastModifiedSet = 0; + String targetDirPath = toDir.getCanonicalPath() + File.separator; Enumeration<JarEntry> entries = jar.entries(); while (entries.hasMoreElements()) { final JarEntry entry = entries.nextElement(); @@ -117,6 +118,10 @@ public static void unJar(File jarFile, File toDir, Pattern unpackRegex) try (InputStream in = jar.getInputStream(entry)) { File file = new File(toDir, entry.getName()); ensureDirectory(file.getParentFile()); + if (!file.getCanonicalPath().startsWith(targetDirPath)) { + throw new IOException("expanding " + entry.getName() + + " would create file outside of " + toDir); + } try (OutputStream out = new FileOutputStream(file)) { IOUtils.copyBytes(in, out, BUFFER_SIZE); }
hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/util/TestRunJar.java+36 −0 modified@@ -20,12 +20,15 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.jar.JarEntry; import java.util.jar.JarOutputStream; import java.util.regex.Pattern; import java.util.zip.ZipEntry; @@ -165,4 +168,37 @@ public void testClientClassLoader() throws Throwable { runJar.run(args); // it should not throw an exception } + + @Test + public void testUnJar2() throws IOException { + // make a simple zip + File jarFile = new File(TEST_ROOT_DIR, TEST_JAR_NAME); + JarOutputStream jstream = + new JarOutputStream(new FileOutputStream(jarFile)); + JarEntry je = new JarEntry("META-INF/MANIFEST.MF"); + byte[] data = "Manifest-Version: 1.0\nCreated-By: 1.8.0_1 (Manual)" + .getBytes(StandardCharsets.UTF_8); + je.setSize(data.length); + jstream.putNextEntry(je); + jstream.write(data); + jstream.closeEntry(); + je = new JarEntry("../outside.path"); + data = "any data here".getBytes(StandardCharsets.UTF_8); + je.setSize(data.length); + jstream.putNextEntry(je); + jstream.write(data); + jstream.closeEntry(); + jstream.close(); + + File unjarDir = getUnjarDir("unjar-path"); + + // Unjar everything + try { + RunJar.unJar(jarFile, unjarDir); + fail("unJar should throw IOException."); + } catch (IOException e) { + GenericTestUtils.assertExceptionContains( + "would create file outside of", e); + } + } } \ No newline at end of file
65e55097da2bAdditional check when unpacking archives. Contributed by Wilfred Spiegelenburg.
2 files changed · +41 −0
hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/util/RunJar.java+5 −0 modified@@ -109,6 +109,7 @@ public static void unJar(File jarFile, File toDir, Pattern unpackRegex) throws IOException { try (JarFile jar = new JarFile(jarFile)) { int numOfFailedLastModifiedSet = 0; + String targetDirPath = toDir.getCanonicalPath() + File.separator; Enumeration<JarEntry> entries = jar.entries(); while (entries.hasMoreElements()) { final JarEntry entry = entries.nextElement(); @@ -117,6 +118,10 @@ public static void unJar(File jarFile, File toDir, Pattern unpackRegex) try (InputStream in = jar.getInputStream(entry)) { File file = new File(toDir, entry.getName()); ensureDirectory(file.getParentFile()); + if (!file.getCanonicalPath().startsWith(targetDirPath)) { + throw new IOException("expanding " + entry.getName() + + " would create file outside of " + toDir); + } try (OutputStream out = new FileOutputStream(file)) { IOUtils.copyBytes(in, out, BUFFER_SIZE); }
hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/util/TestRunJar.java+36 −0 modified@@ -20,12 +20,15 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.jar.JarEntry; import java.util.jar.JarOutputStream; import java.util.regex.Pattern; import java.util.zip.ZipEntry; @@ -165,4 +168,37 @@ public void testClientClassLoader() throws Throwable { runJar.run(args); // it should not throw an exception } + + @Test + public void testUnJar2() throws IOException { + // make a simple zip + File jarFile = new File(TEST_ROOT_DIR, TEST_JAR_NAME); + JarOutputStream jstream = + new JarOutputStream(new FileOutputStream(jarFile)); + JarEntry je = new JarEntry("META-INF/MANIFEST.MF"); + byte[] data = "Manifest-Version: 1.0\nCreated-By: 1.8.0_1 (Manual)" + .getBytes(StandardCharsets.UTF_8); + je.setSize(data.length); + jstream.putNextEntry(je); + jstream.write(data); + jstream.closeEntry(); + je = new JarEntry("../outside.path"); + data = "any data here".getBytes(StandardCharsets.UTF_8); + je.setSize(data.length); + jstream.putNextEntry(je); + jstream.write(data); + jstream.closeEntry(); + jstream.close(); + + File unjarDir = getUnjarDir("unjar-path"); + + // Unjar everything + try { + RunJar.unJar(jarFile, unjarDir); + fail("unJar should throw IOException."); + } catch (IOException e) { + GenericTestUtils.assertExceptionContains( + "would create file outside of", e); + } + } } \ No newline at end of file
65e55097da2bAdditional check when unpacking archives. Contributed by Wilfred Spiegelenburg.
2 files changed · +41 −0
hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/util/RunJar.java+5 −0 modified@@ -109,6 +109,7 @@ public static void unJar(File jarFile, File toDir, Pattern unpackRegex) throws IOException { try (JarFile jar = new JarFile(jarFile)) { int numOfFailedLastModifiedSet = 0; + String targetDirPath = toDir.getCanonicalPath() + File.separator; Enumeration<JarEntry> entries = jar.entries(); while (entries.hasMoreElements()) { final JarEntry entry = entries.nextElement(); @@ -117,6 +118,10 @@ public static void unJar(File jarFile, File toDir, Pattern unpackRegex) try (InputStream in = jar.getInputStream(entry)) { File file = new File(toDir, entry.getName()); ensureDirectory(file.getParentFile()); + if (!file.getCanonicalPath().startsWith(targetDirPath)) { + throw new IOException("expanding " + entry.getName() + + " would create file outside of " + toDir); + } try (OutputStream out = new FileOutputStream(file)) { IOUtils.copyBytes(in, out, BUFFER_SIZE); }
hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/util/TestRunJar.java+36 −0 modified@@ -20,12 +20,15 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.jar.JarEntry; import java.util.jar.JarOutputStream; import java.util.regex.Pattern; import java.util.zip.ZipEntry; @@ -165,4 +168,37 @@ public void testClientClassLoader() throws Throwable { runJar.run(args); // it should not throw an exception } + + @Test + public void testUnJar2() throws IOException { + // make a simple zip + File jarFile = new File(TEST_ROOT_DIR, TEST_JAR_NAME); + JarOutputStream jstream = + new JarOutputStream(new FileOutputStream(jarFile)); + JarEntry je = new JarEntry("META-INF/MANIFEST.MF"); + byte[] data = "Manifest-Version: 1.0\nCreated-By: 1.8.0_1 (Manual)" + .getBytes(StandardCharsets.UTF_8); + je.setSize(data.length); + jstream.putNextEntry(je); + jstream.write(data); + jstream.closeEntry(); + je = new JarEntry("../outside.path"); + data = "any data here".getBytes(StandardCharsets.UTF_8); + je.setSize(data.length); + jstream.putNextEntry(je); + jstream.write(data); + jstream.closeEntry(); + jstream.close(); + + File unjarDir = getUnjarDir("unjar-path"); + + // Unjar everything + try { + RunJar.unJar(jarFile, unjarDir); + fail("unJar should throw IOException."); + } catch (IOException e) { + GenericTestUtils.assertExceptionContains( + "would create file outside of", e); + } + } } \ No newline at end of file
45a1c680c276Additional check when unpacking archives. Contributed by Jason Lowe and Akira Ajisaka.
2 files changed · +49 −10
hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/FileUtil.java+14 −3 modified@@ -587,16 +587,21 @@ public static long getDU(File dir) { public static void unZip(File inFile, File unzipDir) throws IOException { Enumeration<? extends ZipEntry> entries; ZipFile zipFile = new ZipFile(inFile); + String targetDirPath = unzipDir.getCanonicalPath() + File.separator; try { entries = zipFile.entries(); while (entries.hasMoreElements()) { ZipEntry entry = entries.nextElement(); if (!entry.isDirectory()) { + File file = new File(unzipDir, entry.getName()); + if (!file.getCanonicalPath().startsWith(targetDirPath)) { + throw new IOException("expanding " + entry.getName() + + " would create file outside of " + unzipDir); + } InputStream in = zipFile.getInputStream(entry); try { - File file = new File(unzipDir, entry.getName()); - if (!file.getParentFile().mkdirs()) { + if (!file.getParentFile().mkdirs()) { if (!file.getParentFile().isDirectory()) { throw new IOException("Mkdirs failed to create " + file.getParentFile().toString()); @@ -705,6 +710,13 @@ private static void unTarUsingJava(File inFile, File untarDir, private static void unpackEntries(TarArchiveInputStream tis, TarArchiveEntry entry, File outputDir) throws IOException { + String targetDirPath = outputDir.getCanonicalPath() + File.separator; + File outputFile = new File(outputDir, entry.getName()); + if (!outputFile.getCanonicalPath().startsWith(targetDirPath)) { + throw new IOException("expanding " + entry.getName() + + " would create entry outside of " + outputDir); + } + if (entry.isDirectory()) { File subDir = new File(outputDir, entry.getName()); if (!subDir.mkdirs() && !subDir.isDirectory()) { @@ -719,7 +731,6 @@ private static void unpackEntries(TarArchiveInputStream tis, return; } - File outputFile = new File(outputDir, entry.getName()); if (!outputFile.getParentFile().exists()) { if (!outputFile.getParentFile().mkdirs()) { throw new IOException("Mkdirs failed to create tar internal dir "
hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/fs/TestFileUtil.java+35 −7 modified@@ -25,8 +25,9 @@ import java.io.FileReader; import java.io.IOException; import java.io.OutputStream; -import java.net.URI; import java.io.PrintWriter; +import java.net.URI; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -40,6 +41,7 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.test.GenericTestUtils; import org.apache.hadoop.util.Shell; import org.apache.hadoop.util.StringUtils; import org.apache.tools.tar.TarEntry; @@ -708,10 +710,8 @@ public void testCreateLocalTempFile() throws IOException { @Test (timeout = 30000) public void testUnZip() throws IOException { - // make sa simple zip setupDirs(); - - // make a simple tar: + // make a simple zip final File simpleZip = new File(del, FILE); OutputStream os = new FileOutputStream(simpleZip); ZipOutputStream tos = new ZipOutputStream(os); @@ -728,7 +728,7 @@ public void testUnZip() throws IOException { tos.close(); } - // successfully untar it into an existing dir: + // successfully unzip it into an existing dir: FileUtil.unZip(simpleZip, tmp); // check result: assertTrue(new File(tmp, "foo").exists()); @@ -743,8 +743,36 @@ public void testUnZip() throws IOException { } catch (IOException ioe) { // okay } - } - + } + + @Test (timeout = 30000) + public void testUnZip2() throws IOException { + setupDirs(); + // make a simple zip + final File simpleZip = new File(del, FILE); + OutputStream os = new FileOutputStream(simpleZip); + try (ZipOutputStream tos = new ZipOutputStream(os)) { + // Add an entry that contains invalid filename + ZipEntry ze = new ZipEntry("../foo"); + byte[] data = "some-content".getBytes(StandardCharsets.UTF_8); + ze.setSize(data.length); + tos.putNextEntry(ze); + tos.write(data); + tos.closeEntry(); + tos.flush(); + tos.finish(); + } + + // Unzip it into an existing dir + try { + FileUtil.unZip(simpleZip, tmp); + Assert.fail("unZip should throw IOException."); + } catch (IOException e) { + GenericTestUtils.assertExceptionContains( + "would create file outside of", e); + } + } + @Test (timeout = 30000) /* * Test method copy(FileSystem srcFS, Path src, File dst, boolean deleteSource, Configuration conf)
45a1c680c276Additional check when unpacking archives. Contributed by Jason Lowe and Akira Ajisaka.
2 files changed · +49 −10
hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/FileUtil.java+14 −3 modified@@ -587,16 +587,21 @@ public static long getDU(File dir) { public static void unZip(File inFile, File unzipDir) throws IOException { Enumeration<? extends ZipEntry> entries; ZipFile zipFile = new ZipFile(inFile); + String targetDirPath = unzipDir.getCanonicalPath() + File.separator; try { entries = zipFile.entries(); while (entries.hasMoreElements()) { ZipEntry entry = entries.nextElement(); if (!entry.isDirectory()) { + File file = new File(unzipDir, entry.getName()); + if (!file.getCanonicalPath().startsWith(targetDirPath)) { + throw new IOException("expanding " + entry.getName() + + " would create file outside of " + unzipDir); + } InputStream in = zipFile.getInputStream(entry); try { - File file = new File(unzipDir, entry.getName()); - if (!file.getParentFile().mkdirs()) { + if (!file.getParentFile().mkdirs()) { if (!file.getParentFile().isDirectory()) { throw new IOException("Mkdirs failed to create " + file.getParentFile().toString()); @@ -705,6 +710,13 @@ private static void unTarUsingJava(File inFile, File untarDir, private static void unpackEntries(TarArchiveInputStream tis, TarArchiveEntry entry, File outputDir) throws IOException { + String targetDirPath = outputDir.getCanonicalPath() + File.separator; + File outputFile = new File(outputDir, entry.getName()); + if (!outputFile.getCanonicalPath().startsWith(targetDirPath)) { + throw new IOException("expanding " + entry.getName() + + " would create entry outside of " + outputDir); + } + if (entry.isDirectory()) { File subDir = new File(outputDir, entry.getName()); if (!subDir.mkdirs() && !subDir.isDirectory()) { @@ -719,7 +731,6 @@ private static void unpackEntries(TarArchiveInputStream tis, return; } - File outputFile = new File(outputDir, entry.getName()); if (!outputFile.getParentFile().exists()) { if (!outputFile.getParentFile().mkdirs()) { throw new IOException("Mkdirs failed to create tar internal dir "
hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/fs/TestFileUtil.java+35 −7 modified@@ -25,8 +25,9 @@ import java.io.FileReader; import java.io.IOException; import java.io.OutputStream; -import java.net.URI; import java.io.PrintWriter; +import java.net.URI; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -40,6 +41,7 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.test.GenericTestUtils; import org.apache.hadoop.util.Shell; import org.apache.hadoop.util.StringUtils; import org.apache.tools.tar.TarEntry; @@ -708,10 +710,8 @@ public void testCreateLocalTempFile() throws IOException { @Test (timeout = 30000) public void testUnZip() throws IOException { - // make sa simple zip setupDirs(); - - // make a simple tar: + // make a simple zip final File simpleZip = new File(del, FILE); OutputStream os = new FileOutputStream(simpleZip); ZipOutputStream tos = new ZipOutputStream(os); @@ -728,7 +728,7 @@ public void testUnZip() throws IOException { tos.close(); } - // successfully untar it into an existing dir: + // successfully unzip it into an existing dir: FileUtil.unZip(simpleZip, tmp); // check result: assertTrue(new File(tmp, "foo").exists()); @@ -743,8 +743,36 @@ public void testUnZip() throws IOException { } catch (IOException ioe) { // okay } - } - + } + + @Test (timeout = 30000) + public void testUnZip2() throws IOException { + setupDirs(); + // make a simple zip + final File simpleZip = new File(del, FILE); + OutputStream os = new FileOutputStream(simpleZip); + try (ZipOutputStream tos = new ZipOutputStream(os)) { + // Add an entry that contains invalid filename + ZipEntry ze = new ZipEntry("../foo"); + byte[] data = "some-content".getBytes(StandardCharsets.UTF_8); + ze.setSize(data.length); + tos.putNextEntry(ze); + tos.write(data); + tos.closeEntry(); + tos.flush(); + tos.finish(); + } + + // Unzip it into an existing dir + try { + FileUtil.unZip(simpleZip, tmp); + Assert.fail("unZip should throw IOException."); + } catch (IOException e) { + GenericTestUtils.assertExceptionContains( + "would create file outside of", e); + } + } + @Test (timeout = 30000) /* * Test method copy(FileSystem srcFS, Path src, File dst, boolean deleteSource, Configuration conf)
12258c7cff8dAdditional check when unpacking archives. Contributed by Jason Lowe and Akira Ajisaka.
2 files changed · +47 −9
hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/FileUtil.java+14 −3 modified@@ -591,16 +591,21 @@ public static long getDU(File dir) { public static void unZip(File inFile, File unzipDir) throws IOException { Enumeration<? extends ZipEntry> entries; ZipFile zipFile = new ZipFile(inFile); + String targetDirPath = unzipDir.getCanonicalPath() + File.separator; try { entries = zipFile.entries(); while (entries.hasMoreElements()) { ZipEntry entry = entries.nextElement(); if (!entry.isDirectory()) { + File file = new File(unzipDir, entry.getName()); + if (!file.getCanonicalPath().startsWith(targetDirPath)) { + throw new IOException("expanding " + entry.getName() + + " would create file outside of " + unzipDir); + } InputStream in = zipFile.getInputStream(entry); try { - File file = new File(unzipDir, entry.getName()); - if (!file.getParentFile().mkdirs()) { + if (!file.getParentFile().mkdirs()) { if (!file.getParentFile().isDirectory()) { throw new IOException("Mkdirs failed to create " + file.getParentFile().toString()); @@ -709,6 +714,13 @@ private static void unTarUsingJava(File inFile, File untarDir, private static void unpackEntries(TarArchiveInputStream tis, TarArchiveEntry entry, File outputDir) throws IOException { + String targetDirPath = outputDir.getCanonicalPath() + File.separator; + File outputFile = new File(outputDir, entry.getName()); + if (!outputFile.getCanonicalPath().startsWith(targetDirPath)) { + throw new IOException("expanding " + entry.getName() + + " would create entry outside of " + outputDir); + } + if (entry.isDirectory()) { File subDir = new File(outputDir, entry.getName()); if (!subDir.mkdirs() && !subDir.isDirectory()) { @@ -723,7 +735,6 @@ private static void unpackEntries(TarArchiveInputStream tis, return; } - File outputFile = new File(outputDir, entry.getName()); if (!outputFile.getParentFile().exists()) { if (!outputFile.getParentFile().mkdirs()) { throw new IOException("Mkdirs failed to create tar internal dir "
hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/fs/TestFileUtil.java+33 −6 modified@@ -32,6 +32,7 @@ import java.io.PrintWriter; import java.net.URISyntaxException; import java.net.UnknownHostException; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -734,10 +735,8 @@ public void testCreateLocalTempFile() throws IOException { @Test (timeout = 30000) public void testUnZip() throws IOException { - // make sa simple zip setupDirs(); - - // make a simple tar: + // make a simple zip final File simpleZip = new File(del, FILE); OutputStream os = new FileOutputStream(simpleZip); ZipOutputStream tos = new ZipOutputStream(os); @@ -754,7 +753,7 @@ public void testUnZip() throws IOException { tos.close(); } - // successfully untar it into an existing dir: + // successfully unzip it into an existing dir: FileUtil.unZip(simpleZip, tmp); // check result: assertTrue(new File(tmp, "foo").exists()); @@ -769,8 +768,36 @@ public void testUnZip() throws IOException { } catch (IOException ioe) { // okay } - } - + } + + @Test (timeout = 30000) + public void testUnZip2() throws IOException { + setupDirs(); + // make a simple zip + final File simpleZip = new File(del, FILE); + OutputStream os = new FileOutputStream(simpleZip); + try (ZipOutputStream tos = new ZipOutputStream(os)) { + // Add an entry that contains invalid filename + ZipEntry ze = new ZipEntry("../foo"); + byte[] data = "some-content".getBytes(StandardCharsets.UTF_8); + ze.setSize(data.length); + tos.putNextEntry(ze); + tos.write(data); + tos.closeEntry(); + tos.flush(); + tos.finish(); + } + + // Unzip it into an existing dir + try { + FileUtil.unZip(simpleZip, tmp); + Assert.fail("unZip should throw IOException."); + } catch (IOException e) { + GenericTestUtils.assertExceptionContains( + "would create file outside of", e); + } + } + @Test (timeout = 30000) /* * Test method copy(FileSystem srcFS, Path src, File dst, boolean deleteSource, Configuration conf)
12258c7cff8dAdditional check when unpacking archives. Contributed by Jason Lowe and Akira Ajisaka.
2 files changed · +47 −9
hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/FileUtil.java+14 −3 modified@@ -591,16 +591,21 @@ public static long getDU(File dir) { public static void unZip(File inFile, File unzipDir) throws IOException { Enumeration<? extends ZipEntry> entries; ZipFile zipFile = new ZipFile(inFile); + String targetDirPath = unzipDir.getCanonicalPath() + File.separator; try { entries = zipFile.entries(); while (entries.hasMoreElements()) { ZipEntry entry = entries.nextElement(); if (!entry.isDirectory()) { + File file = new File(unzipDir, entry.getName()); + if (!file.getCanonicalPath().startsWith(targetDirPath)) { + throw new IOException("expanding " + entry.getName() + + " would create file outside of " + unzipDir); + } InputStream in = zipFile.getInputStream(entry); try { - File file = new File(unzipDir, entry.getName()); - if (!file.getParentFile().mkdirs()) { + if (!file.getParentFile().mkdirs()) { if (!file.getParentFile().isDirectory()) { throw new IOException("Mkdirs failed to create " + file.getParentFile().toString()); @@ -709,6 +714,13 @@ private static void unTarUsingJava(File inFile, File untarDir, private static void unpackEntries(TarArchiveInputStream tis, TarArchiveEntry entry, File outputDir) throws IOException { + String targetDirPath = outputDir.getCanonicalPath() + File.separator; + File outputFile = new File(outputDir, entry.getName()); + if (!outputFile.getCanonicalPath().startsWith(targetDirPath)) { + throw new IOException("expanding " + entry.getName() + + " would create entry outside of " + outputDir); + } + if (entry.isDirectory()) { File subDir = new File(outputDir, entry.getName()); if (!subDir.mkdirs() && !subDir.isDirectory()) { @@ -723,7 +735,6 @@ private static void unpackEntries(TarArchiveInputStream tis, return; } - File outputFile = new File(outputDir, entry.getName()); if (!outputFile.getParentFile().exists()) { if (!outputFile.getParentFile().mkdirs()) { throw new IOException("Mkdirs failed to create tar internal dir "
hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/fs/TestFileUtil.java+33 −6 modified@@ -32,6 +32,7 @@ import java.io.PrintWriter; import java.net.URISyntaxException; import java.net.UnknownHostException; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -734,10 +735,8 @@ public void testCreateLocalTempFile() throws IOException { @Test (timeout = 30000) public void testUnZip() throws IOException { - // make sa simple zip setupDirs(); - - // make a simple tar: + // make a simple zip final File simpleZip = new File(del, FILE); OutputStream os = new FileOutputStream(simpleZip); ZipOutputStream tos = new ZipOutputStream(os); @@ -754,7 +753,7 @@ public void testUnZip() throws IOException { tos.close(); } - // successfully untar it into an existing dir: + // successfully unzip it into an existing dir: FileUtil.unZip(simpleZip, tmp); // check result: assertTrue(new File(tmp, "foo").exists()); @@ -769,8 +768,36 @@ public void testUnZip() throws IOException { } catch (IOException ioe) { // okay } - } - + } + + @Test (timeout = 30000) + public void testUnZip2() throws IOException { + setupDirs(); + // make a simple zip + final File simpleZip = new File(del, FILE); + OutputStream os = new FileOutputStream(simpleZip); + try (ZipOutputStream tos = new ZipOutputStream(os)) { + // Add an entry that contains invalid filename + ZipEntry ze = new ZipEntry("../foo"); + byte[] data = "some-content".getBytes(StandardCharsets.UTF_8); + ze.setSize(data.length); + tos.putNextEntry(ze); + tos.write(data); + tos.closeEntry(); + tos.flush(); + tos.finish(); + } + + // Unzip it into an existing dir + try { + FileUtil.unZip(simpleZip, tmp); + Assert.fail("unZip should throw IOException."); + } catch (IOException e) { + GenericTestUtils.assertExceptionContains( + "would create file outside of", e); + } + } + @Test (timeout = 30000) /* * Test method copy(FileSystem srcFS, Path src, File dst, boolean deleteSource, Configuration conf)
fc4c20fc3469Additional check when unpacking archives. Contributed by Jason Lowe and Akira Ajisaka.
2 files changed · +51 −7
hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/FileUtil.java+17 −1 modified@@ -617,11 +617,16 @@ public static void unZip(InputStream inputStream, File toDir) throws IOException { try (ZipInputStream zip = new ZipInputStream(inputStream)) { int numOfFailedLastModifiedSet = 0; + String targetDirPath = toDir.getCanonicalPath() + File.separator; for(ZipEntry entry = zip.getNextEntry(); entry != null; entry = zip.getNextEntry()) { if (!entry.isDirectory()) { File file = new File(toDir, entry.getName()); + if (!file.getCanonicalPath().startsWith(targetDirPath)) { + throw new IOException("expanding " + entry.getName() + + " would create file outside of " + toDir); + } File parent = file.getParentFile(); if (!parent.mkdirs() && !parent.isDirectory()) { @@ -656,12 +661,17 @@ public static void unZip(File inFile, File unzipDir) throws IOException { try { entries = zipFile.entries(); + String targetDirPath = unzipDir.getCanonicalPath() + File.separator; while (entries.hasMoreElements()) { ZipEntry entry = entries.nextElement(); if (!entry.isDirectory()) { InputStream in = zipFile.getInputStream(entry); try { File file = new File(unzipDir, entry.getName()); + if (!file.getCanonicalPath().startsWith(targetDirPath)) { + throw new IOException("expanding " + entry.getName() + + " would create file outside of " + unzipDir); + } if (!file.getParentFile().mkdirs()) { if (!file.getParentFile().isDirectory()) { throw new IOException("Mkdirs failed to create " + @@ -944,6 +954,13 @@ private static void unTarUsingJava(InputStream inputStream, File untarDir, private static void unpackEntries(TarArchiveInputStream tis, TarArchiveEntry entry, File outputDir) throws IOException { + String targetDirPath = outputDir.getCanonicalPath() + File.separator; + File outputFile = new File(outputDir, entry.getName()); + if (!outputFile.getCanonicalPath().startsWith(targetDirPath)) { + throw new IOException("expanding " + entry.getName() + + " would create entry outside of " + outputDir); + } + if (entry.isDirectory()) { File subDir = new File(outputDir, entry.getName()); if (!subDir.mkdirs() && !subDir.isDirectory()) { @@ -966,7 +983,6 @@ private static void unpackEntries(TarArchiveInputStream tis, return; } - File outputFile = new File(outputDir, entry.getName()); if (!outputFile.getParentFile().exists()) { if (!outputFile.getParentFile().mkdirs()) { throw new IOException("Mkdirs failed to create tar internal dir "
hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/fs/TestFileUtil.java+34 −6 modified@@ -23,6 +23,7 @@ import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -38,6 +39,7 @@ import java.net.URISyntaxException; import java.net.URL; import java.net.UnknownHostException; +import java.nio.charset.StandardCharsets; import java.nio.file.FileSystems; import java.nio.file.Files; import java.util.ArrayList; @@ -685,10 +687,8 @@ public void testCreateLocalTempFile() throws IOException { @Test (timeout = 30000) public void testUnZip() throws IOException { - // make sa simple zip setupDirs(); - - // make a simple tar: + // make a simple zip final File simpleZip = new File(del, FILE); OutputStream os = new FileOutputStream(simpleZip); ZipOutputStream tos = new ZipOutputStream(os); @@ -705,7 +705,7 @@ public void testUnZip() throws IOException { tos.close(); } - // successfully untar it into an existing dir: + // successfully unzip it into an existing dir: FileUtil.unZip(simpleZip, tmp); // check result: assertTrue(new File(tmp, "foo").exists()); @@ -720,8 +720,36 @@ public void testUnZip() throws IOException { } catch (IOException ioe) { // okay } - } - + } + + @Test (timeout = 30000) + public void testUnZip2() throws IOException { + setupDirs(); + // make a simple zip + final File simpleZip = new File(del, FILE); + OutputStream os = new FileOutputStream(simpleZip); + try (ZipOutputStream tos = new ZipOutputStream(os)) { + // Add an entry that contains invalid filename + ZipEntry ze = new ZipEntry("../foo"); + byte[] data = "some-content".getBytes(StandardCharsets.UTF_8); + ze.setSize(data.length); + tos.putNextEntry(ze); + tos.write(data); + tos.closeEntry(); + tos.flush(); + tos.finish(); + } + + // Unzip it into an existing dir + try { + FileUtil.unZip(simpleZip, tmp); + fail("unZip should throw IOException."); + } catch (IOException e) { + GenericTestUtils.assertExceptionContains( + "would create file outside of", e); + } + } + @Test (timeout = 30000) /* * Test method copy(FileSystem srcFS, Path src, File dst, boolean deleteSource, Configuration conf)
fc4c20fc3469Additional check when unpacking archives. Contributed by Jason Lowe and Akira Ajisaka.
2 files changed · +51 −7
hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/FileUtil.java+17 −1 modified@@ -617,11 +617,16 @@ public static void unZip(InputStream inputStream, File toDir) throws IOException { try (ZipInputStream zip = new ZipInputStream(inputStream)) { int numOfFailedLastModifiedSet = 0; + String targetDirPath = toDir.getCanonicalPath() + File.separator; for(ZipEntry entry = zip.getNextEntry(); entry != null; entry = zip.getNextEntry()) { if (!entry.isDirectory()) { File file = new File(toDir, entry.getName()); + if (!file.getCanonicalPath().startsWith(targetDirPath)) { + throw new IOException("expanding " + entry.getName() + + " would create file outside of " + toDir); + } File parent = file.getParentFile(); if (!parent.mkdirs() && !parent.isDirectory()) { @@ -656,12 +661,17 @@ public static void unZip(File inFile, File unzipDir) throws IOException { try { entries = zipFile.entries(); + String targetDirPath = unzipDir.getCanonicalPath() + File.separator; while (entries.hasMoreElements()) { ZipEntry entry = entries.nextElement(); if (!entry.isDirectory()) { InputStream in = zipFile.getInputStream(entry); try { File file = new File(unzipDir, entry.getName()); + if (!file.getCanonicalPath().startsWith(targetDirPath)) { + throw new IOException("expanding " + entry.getName() + + " would create file outside of " + unzipDir); + } if (!file.getParentFile().mkdirs()) { if (!file.getParentFile().isDirectory()) { throw new IOException("Mkdirs failed to create " + @@ -944,6 +954,13 @@ private static void unTarUsingJava(InputStream inputStream, File untarDir, private static void unpackEntries(TarArchiveInputStream tis, TarArchiveEntry entry, File outputDir) throws IOException { + String targetDirPath = outputDir.getCanonicalPath() + File.separator; + File outputFile = new File(outputDir, entry.getName()); + if (!outputFile.getCanonicalPath().startsWith(targetDirPath)) { + throw new IOException("expanding " + entry.getName() + + " would create entry outside of " + outputDir); + } + if (entry.isDirectory()) { File subDir = new File(outputDir, entry.getName()); if (!subDir.mkdirs() && !subDir.isDirectory()) { @@ -966,7 +983,6 @@ private static void unpackEntries(TarArchiveInputStream tis, return; } - File outputFile = new File(outputDir, entry.getName()); if (!outputFile.getParentFile().exists()) { if (!outputFile.getParentFile().mkdirs()) { throw new IOException("Mkdirs failed to create tar internal dir "
hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/fs/TestFileUtil.java+34 −6 modified@@ -23,6 +23,7 @@ import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -38,6 +39,7 @@ import java.net.URISyntaxException; import java.net.URL; import java.net.UnknownHostException; +import java.nio.charset.StandardCharsets; import java.nio.file.FileSystems; import java.nio.file.Files; import java.util.ArrayList; @@ -685,10 +687,8 @@ public void testCreateLocalTempFile() throws IOException { @Test (timeout = 30000) public void testUnZip() throws IOException { - // make sa simple zip setupDirs(); - - // make a simple tar: + // make a simple zip final File simpleZip = new File(del, FILE); OutputStream os = new FileOutputStream(simpleZip); ZipOutputStream tos = new ZipOutputStream(os); @@ -705,7 +705,7 @@ public void testUnZip() throws IOException { tos.close(); } - // successfully untar it into an existing dir: + // successfully unzip it into an existing dir: FileUtil.unZip(simpleZip, tmp); // check result: assertTrue(new File(tmp, "foo").exists()); @@ -720,8 +720,36 @@ public void testUnZip() throws IOException { } catch (IOException ioe) { // okay } - } - + } + + @Test (timeout = 30000) + public void testUnZip2() throws IOException { + setupDirs(); + // make a simple zip + final File simpleZip = new File(del, FILE); + OutputStream os = new FileOutputStream(simpleZip); + try (ZipOutputStream tos = new ZipOutputStream(os)) { + // Add an entry that contains invalid filename + ZipEntry ze = new ZipEntry("../foo"); + byte[] data = "some-content".getBytes(StandardCharsets.UTF_8); + ze.setSize(data.length); + tos.putNextEntry(ze); + tos.write(data); + tos.closeEntry(); + tos.flush(); + tos.finish(); + } + + // Unzip it into an existing dir + try { + FileUtil.unZip(simpleZip, tmp); + fail("unZip should throw IOException."); + } catch (IOException e) { + GenericTestUtils.assertExceptionContains( + "would create file outside of", e); + } + } + @Test (timeout = 30000) /* * Test method copy(FileSystem srcFS, Path src, File dst, boolean deleteSource, Configuration conf)
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
19- access.redhat.com/errata/RHSA-2019:3892ghsavendor-advisoryx_refsource_REDHATWEB
- github.com/advisories/GHSA-6x48-j4x4-cqw3ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2018-8009ghsaADVISORY
- www.securityfocus.com/bid/105927ghsavdb-entryx_refsource_BIDWEB
- github.com/apache/hadoop/commit/12258c7cff8d32710fbd8b9088a930e3ce27432ghsaWEB
- github.com/apache/hadoop/commit/45a1c680c276c4501402f7bc4cebcf85a6fbc7fghsaWEB
- github.com/apache/hadoop/commit/65e55097da2bb3f2fbdf9ba1946da25fe58bec9ghsaWEB
- github.com/apache/hadoop/commit/6a4ae6f6eeed1392a4828a5721fa1499f65bddeghsaWEB
- github.com/apache/hadoop/commit/fc4c20fc3469674cb584a4fb98bac7e3c2277c9ghsaWEB
- hadoop.apache.org/cve_list.htmlghsax_refsource_MISCWEB
- lists.apache.org/thread.html/708d94141126eac03011144a971a6411fcac16d9c248d1d535a39451%40%3Csolr-user.lucene.apache.org%3Emitremailing-listx_refsource_MLIST
- lists.apache.org/thread.html/708d94141126eac03011144a971a6411fcac16d9c248d1d535a39451@%3Csolr-user.lucene.apache.org%3EghsaWEB
- lists.apache.org/thread.html/a1c227745ce30acbcf388c5b0cc8423e8bf495d619cd0fa973f7f38d%40%3Cuser.hadoop.apache.org%3Emitremailing-listx_refsource_MLIST
- lists.apache.org/thread.html/a1c227745ce30acbcf388c5b0cc8423e8bf495d619cd0fa973f7f38d@%3Cuser.hadoop.apache.org%3EghsaWEB
- lists.apache.org/thread.html/r4dddf1705dbedfa94392913b2dad1cd2d1d89040facd389eea0b3510%40%3Ccommits.druid.apache.org%3Emitremailing-listx_refsource_MLIST
- lists.apache.org/thread.html/r4dddf1705dbedfa94392913b2dad1cd2d1d89040facd389eea0b3510@%3Ccommits.druid.apache.org%3EghsaWEB
- lists.apache.org/thread.html/rb21df54a4e39732ce653d2aa5672e36a792b59eb6717f2a06bb8d02a%40%3Ccommits.druid.apache.org%3Emitremailing-listx_refsource_MLIST
- lists.apache.org/thread.html/rb21df54a4e39732ce653d2aa5672e36a792b59eb6717f2a06bb8d02a@%3Ccommits.druid.apache.org%3EghsaWEB
- snyk.io/research/zip-slip-vulnerabilityghsax_refsource_MISCWEB
News mentions
0No linked articles in our index yet.