|
26 | 26 | import org.junit.Test; |
27 | 27 |
|
28 | 28 | import org.apache.catalina.Context; |
| 29 | +import org.apache.catalina.Wrapper; |
| 30 | +import org.apache.catalina.servlets.CGIServlet; |
29 | 31 | import org.apache.catalina.servlets.DefaultServlet; |
30 | 32 | import org.apache.catalina.ssi.SSIFilter; |
31 | 33 | import org.apache.catalina.ssi.SSIServlet; |
32 | 34 | import org.apache.catalina.startup.Tomcat; |
33 | 35 | import org.apache.catalina.startup.TomcatBaseTest; |
34 | 36 | import org.apache.tomcat.util.buf.ByteChunk; |
| 37 | +import org.apache.tomcat.util.compat.JrePlatform; |
35 | 38 | import org.apache.tomcat.util.descriptor.web.FilterDef; |
36 | 39 | import org.apache.tomcat.util.descriptor.web.FilterMap; |
37 | 40 |
|
@@ -105,4 +108,78 @@ public void testCVE_2019_0221_02() throws Exception { |
105 | 108 | Assert.assertEquals(HttpServletResponse.SC_OK, rc); |
106 | 109 | Assert.assertFalse("SSI printenv should not render unescaped HTML ", res.toString().contains("<h1>")); |
107 | 110 | } |
| 111 | + |
| 112 | + // https://www.cve.org/CVERecord?id=CVE-2019-0232 |
| 113 | + @Test |
| 114 | + public void testCVE_2019_0232() throws Exception { |
| 115 | + Tomcat tomcat = getTomcatInstance(); |
| 116 | + |
| 117 | + File appDir = new File(getTemporaryDirectory(), "cgitest"); |
| 118 | + Assert.assertTrue(appDir.mkdirs()); |
| 119 | + addDeleteOnTearDown(appDir); |
| 120 | + |
| 121 | + File cgiDir = new File(appDir, "WEB-INF/cgi"); |
| 122 | + Assert.assertTrue(cgiDir.mkdirs()); |
| 123 | + |
| 124 | + File testScript; |
| 125 | + File maliciousScript; |
| 126 | + |
| 127 | + if (JrePlatform.IS_WINDOWS) { |
| 128 | + testScript = new File(cgiDir, "test.bat"); |
| 129 | + try (FileWriter fw = new FileWriter(testScript)) { |
| 130 | + fw.write("@echo off\r\n"); |
| 131 | + fw.write("echo Content-Type: text/plain\r\n"); |
| 132 | + fw.write("echo.\r\n"); |
| 133 | + fw.write("echo Query string: %QUERY_STRING%\r\n"); |
| 134 | + } |
| 135 | + |
| 136 | + maliciousScript = new File(cgiDir, "malicious.bat"); |
| 137 | + try (FileWriter fw = new FileWriter(maliciousScript)) { |
| 138 | + fw.write("@echo off\r\n"); |
| 139 | + fw.write("echo vulnerable > \"" + new File(appDir, "vulnerable").getAbsolutePath() + "\"\r\n"); |
| 140 | + } |
| 141 | + } else { |
| 142 | + testScript = new File(cgiDir, "test.sh"); |
| 143 | + try (FileWriter fw = new FileWriter(testScript)) { |
| 144 | + fw.write("#!/bin/sh\n"); |
| 145 | + fw.write("echo \"Content-Type: text/plain\"\n"); |
| 146 | + fw.write("echo\n"); |
| 147 | + fw.write("echo \"Query string: $QUERY_STRING\"\n"); |
| 148 | + } |
| 149 | + |
| 150 | + maliciousScript = new File(cgiDir, "malicious.sh"); |
| 151 | + try (FileWriter fw = new FileWriter(maliciousScript)) { |
| 152 | + fw.write("#!/bin/sh\n"); |
| 153 | + fw.write("touch " + new File(appDir, "vulnerable").getAbsolutePath() + "\n"); |
| 154 | + } |
| 155 | + } |
| 156 | + |
| 157 | + Assert.assertTrue(testScript.setExecutable(true)); |
| 158 | + Assert.assertTrue(maliciousScript.setExecutable(true)); |
| 159 | + |
| 160 | + Context ctx = tomcat.addContext("", appDir.getAbsolutePath()); |
| 161 | + ctx.setPrivileged(true); |
| 162 | + |
| 163 | + Wrapper cgi = Tomcat.addServlet(ctx, "cgi", new CGIServlet()); |
| 164 | + cgi.addInitParameter("cgiPathPrefix", "WEB-INF/cgi"); |
| 165 | + cgi.addInitParameter("executable", ""); |
| 166 | + cgi.addInitParameter("enableCmdLineArguments", "true"); |
| 167 | + ctx.addServletMappingDecoded("/cgi/*", "cgi"); |
| 168 | + |
| 169 | + tomcat.start(); |
| 170 | + |
| 171 | + String scriptName = JrePlatform.IS_WINDOWS ? "test.bat" : "test.sh"; |
| 172 | + String maliciousName = JrePlatform.IS_WINDOWS ? "malicious.bat" : "malicious.sh"; |
| 173 | + |
| 174 | + ByteChunk res = new ByteChunk(); |
| 175 | + int rc = getUrl("http://localhost:" + getPort() + "/cgi/" + scriptName + "?firstName=Dimitris", |
| 176 | + res, null); |
| 177 | + Assert.assertEquals(HttpServletResponse.SC_OK, rc); |
| 178 | + Assert.assertTrue(res.toString().contains("Query string:")); |
| 179 | + |
| 180 | + res.recycle(); |
| 181 | + getUrl("http://localhost:" + getPort() + "/cgi/" + scriptName + "?&" + maliciousName, res, null); |
| 182 | + Assert.assertFalse("CGI command injection succeeded", new File(appDir, "vulnerable").exists()); |
| 183 | + } |
| 184 | + |
108 | 185 | } |
0 commit comments