diff --git a/ChangeLog b/ChangeLog index 0c63dd98..d8e560e0 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,29 @@ +2019-06-26 Jiri Vanek + + All files, except signaturre files, are now checked for signatures - CVE-2019-10181 + * b/netx/net/sourceforge/jnlp/tools/JarCertVerifier.java: (isMetaInfFile) fixed bug, when anything in META-INF was not + checked for signature. Now only signature files are skipped + * tests/netx/unit/net/sourceforge/jnlp/tools/JarCertVerifierTest.java: added tests for check if file should be skipped from + signature check + +2019-06-26 Jiri Vanek + + Nested jar, if by relative path point up, is stored as hashed - CVE-2019-10185 + * tests/netx/unit/net/sourceforge/jnlp/runtime/jar03_dotdotN1.jar: crafted jar with hacked zip entries to be named like ".." + * tests/netx/unit/net/sourceforge/jnlp/runtime/jar_03_dotdot_jarN1.jnlp: jnlp to call jar03_dotdotN1.jar + * netx/net/sourceforge/jnlp/cache/CacheUtil.jsava: (hex) made public to be reused in JNLPClassLoader + * netx/net/sourceforge/jnlp/runtime/JNLPClassLoader.java: if nested jar contains .. in path, is extracted as hashed + +2019-06-26 Jiri Vanek + + Fixed bug when relative path (..) could leak up (even out of cache) - CVE-2019-10182 + * netx/net/sourceforge/jnlp/cache/CacheUtil.java: if path or query contains .. is saved to cache via its hash + * netx/net/sourceforge/jnlp/util/FileUtils.java: added warning about different behavior on win/linux + * tests/netx/unit/net/sourceforge/jnlp/cache/CacheUtilTest.java: added tests for hashing + * tests/netx/unit/net/sourceforge/jnlp/runtime/JNLPClassLoaderTest.java: added test for .. in path. Added test + that verifies encoded .. (%2E%2E) do not leak from cahce + * tests/netx/unit/net/sourceforge/jnlp/runtime/up.jnlp: example jnlp with .. full url + 2018-05-14 Jiri Vanek * netx/net/sourceforge/jnlp/runtime/AppletEnvironment.java: getDocumentBase now returns codeBase as fallback when diff --git a/tests/netx/unit/net/sourceforge/jnlp/cache/CacheUtilTest.java b/tests/netx/unit/net/sourceforge/jnlp/cache/CacheUtilTest.java index 6b0cd256..5dbf2d69 100644 --- a/tests/netx/unit/net/sourceforge/jnlp/cache/CacheUtilTest.java +++ b/tests/netx/unit/net/sourceforge/jnlp/cache/CacheUtilTest.java @@ -135,6 +135,14 @@ public class CacheUtilTest { File r = CacheUtil.urlToPath(u, "/tmp/"); Assert.assertEquals(expected, r); } + + @Test + public void testQueryGotHAshedToo() throws Exception { + final URL u = new URL("https://example2.com/something/my.jar?../../harm"); + final File expected = new File("/tmp/https/example2.com/2844b3c690ea355159ed61de6e727f2e9169ab55bf58b8fa3f4b64f6a25bd7.jar"); + File r = CacheUtil.urlToPath(u, "/tmp/"); + Assert.assertEquals(expected, r); + } @Test diff --git a/tests/netx/unit/net/sourceforge/jnlp/runtime/JNLPClassLoaderTest.java b/tests/netx/unit/net/sourceforge/jnlp/runtime/JNLPClassLoaderTest.java index 2b28fb93..d86786ab 100644 --- a/tests/netx/unit/net/sourceforge/jnlp/runtime/JNLPClassLoaderTest.java +++ b/tests/netx/unit/net/sourceforge/jnlp/runtime/JNLPClassLoaderTest.java @@ -405,6 +405,8 @@ public class JNLPClassLoaderTest extends NoStdOutErrTest { JNLPRuntime.setTrustAll(true); JNLPRuntime.setSecurityEnabled(false); JNLPRuntime.setDebug(true); + String manifestAttsBackup = JNLPRuntime.getConfiguration().getProperty(DeploymentConfiguration.KEY_ENABLE_MANIFEST_ATTRIBUTES_CHECK); + JNLPRuntime.getConfiguration().setProperty(DeploymentConfiguration.KEY_ENABLE_MANIFEST_ATTRIBUTES_CHECK, "NONE"); try { final JNLPFile jnlpFile1 = new JNLPFile(new URL("http://localhost:" + port + "/up.jnlp")); final JNLPClassLoader classLoader1 = JNLPClassLoader.getInstance(jnlpFile1, UpdatePolicy.ALWAYS, false); @@ -419,6 +421,7 @@ public class JNLPClassLoaderTest extends NoStdOutErrTest { JNLPRuntime.setTrustAll(trustBackup); JNLPRuntime.setSecurityEnabled(securityBAckup); JNLPRuntime.setDebug(verbose); + JNLPRuntime.getConfiguration().setProperty(DeploymentConfiguration.KEY_ENABLE_MANIFEST_ATTRIBUTES_CHECK, manifestAttsBackup); as.stop(); } @@ -451,6 +454,11 @@ public class JNLPClassLoaderTest extends NoStdOutErrTest { JNLPRuntime.setTrustAll(true); JNLPRuntime.setSecurityEnabled(false); JNLPRuntime.setDebug(true); + //fix of "All files, except signaturre files, are now checked for signatures" make this actually correctly failing ahead of time + String ignoreBackup = JNLPRuntime.getConfiguration().getProperty(DeploymentConfiguration.KEY_SECURITY_ITW_IGNORECERTISSUES); + JNLPRuntime.getConfiguration().setProperty(DeploymentConfiguration.KEY_SECURITY_ITW_IGNORECERTISSUES, "true"); + String manifestAttsBackup = JNLPRuntime.getConfiguration().getProperty(DeploymentConfiguration.KEY_ENABLE_MANIFEST_ATTRIBUTES_CHECK); + JNLPRuntime.getConfiguration().setProperty(DeploymentConfiguration.KEY_ENABLE_MANIFEST_ATTRIBUTES_CHECK, "NONE"); try { //it is invalid jar, so we have to disable checks first final JNLPFile jnlpFile = new JNLPFile(new URL("http://localhost:" + port + "/jar_03_dotdot_jarN1.jnlp")); @@ -488,10 +496,102 @@ public class JNLPClassLoaderTest extends NoStdOutErrTest { JNLPRuntime.setTrustAll(trustBackup); JNLPRuntime.setSecurityEnabled(securityBAckup); JNLPRuntime.setDebug(verbose); + JNLPRuntime.getConfiguration().setProperty(DeploymentConfiguration.KEY_SECURITY_ITW_IGNORECERTISSUES, ignoreBackup); + JNLPRuntime.getConfiguration().setProperty(DeploymentConfiguration.KEY_ENABLE_MANIFEST_ATTRIBUTES_CHECK, manifestAttsBackup); as.stop(); } } + @Test(expected = Exception.class) + public void testDifferentSignatureInManifestMf() throws Exception { + CacheUtil.clearCache(); + int port = ServerAccess.findFreePort(); + File dir = FileTestUtils.createTempDirectory(); + dir.deleteOnExit(); + File jar = new File(dir,"jar03_dotdotN1.jar"); + File jnlp = new File(dir,"jar_03_dotdot_jarN1.jnlp"); + InputStream is1 = this.getClass().getClassLoader().getResourceAsStream("net/sourceforge/jnlp/runtime/jar_03_dotdot_jarN1.jnlp"); + InputStream is2 = this.getClass().getClassLoader().getResourceAsStream("net/sourceforge/jnlp/runtime/jar03_dotdotN1.jar"); + OutputStream fos1 = new FileOutputStream(jnlp); + OutputStream fos2 = new FileOutputStream(jar); + StreamUtils.copyStream(is1, fos1); + StreamUtils.copyStream(is2, fos2); + fos1.flush();; + fos2.flush(); + fos1.close(); + fos2.close(); + ServerLauncher as = ServerAccess.getIndependentInstance(dir.getAbsolutePath(), port); + boolean verifyBackup = JNLPRuntime.isVerifying(); + boolean trustBackup= JNLPRuntime.isTrustAll(); + boolean securityBAckup= JNLPRuntime.isSecurityEnabled(); + boolean verbose= JNLPRuntime.isDebug(); + JNLPRuntime.setVerify(false); + JNLPRuntime.setTrustAll(true); + JNLPRuntime.setSecurityEnabled(false); + JNLPRuntime.setDebug(true); + String ignoreBackup = JNLPRuntime.getConfiguration().getProperty(DeploymentConfiguration.KEY_SECURITY_ITW_IGNORECERTISSUES); + JNLPRuntime.getConfiguration().setProperty(DeploymentConfiguration.KEY_SECURITY_ITW_IGNORECERTISSUES, "false"); + try { + //it is invalid jar, so we have to disable checks first + final JNLPFile jnlpFile = new JNLPFile(new URL("http://localhost:" + port + "/jar_03_dotdot_jarN1.jnlp")); + final JNLPClassLoader classLoader = JNLPClassLoader.getInstance(jnlpFile, UpdatePolicy.ALWAYS, false); + } finally { + JNLPRuntime.setVerify(verifyBackup); + JNLPRuntime.setTrustAll(trustBackup); + JNLPRuntime.setSecurityEnabled(securityBAckup); + JNLPRuntime.setDebug(verbose); + JNLPRuntime.getConfiguration().setProperty(DeploymentConfiguration.KEY_SECURITY_ITW_IGNORECERTISSUES, ignoreBackup); + as.stop(); + } + + } + + @Test + public void testEncodedPathIsNotDecodedForCache() throws Exception { + CacheUtil.clearCache(); + int port = ServerAccess.findFreePort(); + File dir = FileTestUtils.createTempDirectory(); + dir.deleteOnExit(); + dir = new File(dir,"base"); + dir.mkdir(); + File jar = new File(dir,"j1.jar"); + File jnlp = new File(dir+"/a/b/upEncoded.jnlp"); + jnlp.getParentFile().mkdirs(); + InputStream is = this.getClass().getClassLoader().getResourceAsStream("net/sourceforge/jnlp/runtime/upEncoded.jnlp"); + String jnlpString = StreamUtils.readStreamAsString(is, true, "utf-8"); + is.close(); + jnlpString = jnlpString.replaceAll("8080", ""+port); + is = this.getClass().getClassLoader().getResourceAsStream("net/sourceforge/jnlp/runtime/j1.jar"); + StreamUtils.copyStream(is, new FileOutputStream(jar)); + Files.write(jnlp.toPath(),jnlpString.getBytes("utf-8")); + ServerLauncher as = ServerAccess.getIndependentInstance(jnlp.getParent(), port); + boolean verifyBackup = JNLPRuntime.isVerifying(); + boolean trustBackup= JNLPRuntime.isTrustAll(); + boolean securityBAckup= JNLPRuntime.isSecurityEnabled(); + boolean verbose= JNLPRuntime.isDebug(); + JNLPRuntime.setVerify(false); + JNLPRuntime.setTrustAll(true); + JNLPRuntime.setSecurityEnabled(false); + JNLPRuntime.setDebug(true); + try { + final JNLPFile jnlpFile1 = new JNLPFile(new URL("http://localhost:" + port + "/upEncoded.jnlp")); + final JNLPClassLoader classLoader1 = JNLPClassLoader.getInstance(jnlpFile1, UpdatePolicy.ALWAYS, false); + InputStream is1 = classLoader1.getResourceAsStream("Hello1.class"); + is1.close(); + is1 = classLoader1.getResourceAsStream("META-INF/MANIFEST.MF"); + is1.close(); + Assert.assertTrue(new File(PathsAndFiles.CACHE_DIR.getFullPath()+"/0/http/localhost/"+port+"/upEncoded.jnlp").exists()); + //be aware; if decoding ever come in play here, thios will leak out of cache folder. Thus harm user system. See fix for " Fixed bug when relative path (..) could leak up (even out of cache)" + Assert.assertTrue(new File(PathsAndFiles.CACHE_DIR.getFullPath()+"/1/http/localhost/"+port+"/%2E%2E/%2E%2E/%2E%2E/base").exists()); + } finally { + JNLPRuntime.setVerify(verifyBackup); + JNLPRuntime.setTrustAll(trustBackup); + JNLPRuntime.setSecurityEnabled(securityBAckup); + JNLPRuntime.setDebug(verbose); + as.stop(); + } + + } } diff --git a/tests/netx/unit/net/sourceforge/jnlp/runtime/upEncoded.jnlp b/tests/netx/unit/net/sourceforge/jnlp/runtime/upEncoded.jnlp new file mode 100644 index 00000000..f0658bbc --- /dev/null +++ b/tests/netx/unit/net/sourceforge/jnlp/runtime/upEncoded.jnlp @@ -0,0 +1,15 @@ + + + + 1965Nemzeti Ado- es Vamhivatal + + + + + + + + + + +