Skip to content

Commit 76ea79d

Browse files
committed
Add a unit test for CVE-2023-41080
1 parent 907d5b2 commit 76ea79d

File tree

1 file changed

+99
-0
lines changed

1 file changed

+99
-0
lines changed

test/org/apache/tomcat/security/TestSecurity2023.java

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@
2424
import java.net.HttpURLConnection;
2525
import java.net.URI;
2626
import java.net.URISyntaxException;
27+
import java.nio.charset.StandardCharsets;
28+
import java.util.HashMap;
29+
import java.util.List;
30+
import java.util.Map;
2731

2832
import jakarta.servlet.MultipartConfigElement;
2933
import jakarta.servlet.ServletException;
@@ -37,8 +41,14 @@
3741
import static org.apache.catalina.startup.SimpleHttpClient.CRLF;
3842
import org.apache.catalina.Context;
3943
import org.apache.catalina.Wrapper;
44+
import org.apache.catalina.authenticator.FormAuthenticator;
4045
import org.apache.catalina.startup.Tomcat;
4146
import org.apache.catalina.startup.TomcatBaseTest;
47+
import org.apache.tomcat.util.buf.ByteChunk;
48+
import org.apache.tomcat.util.descriptor.web.LoginConfig;
49+
import org.apache.tomcat.util.descriptor.web.SecurityCollection;
50+
import org.apache.tomcat.util.descriptor.web.SecurityConstraint;
51+
import org.apache.tomcat.util.http.Method;
4252

4353
public class TestSecurity2023 extends TomcatBaseTest {
4454
/*
@@ -81,6 +91,90 @@ public void testCVE_2023_24998_CVE_2023_28709() throws Exception {
8191
Assert.assertEquals(HttpServletResponse.SC_REQUEST_ENTITY_TOO_LARGE, status);
8292
}
8393

94+
/*
95+
* https://www.cve.org/CVERecord?id=CVE-2023-41080
96+
*
97+
* Fixed in
98+
* 11.0.0-M11 https://github.com/apache/tomcat/commit/e3703c9abb8fe0d5602f6ba8a8f11d4b6940815a
99+
* 10.1.13 https://github.com/apache/tomcat/commit/bb4624a9f3e69d495182ebfa68d7983076407a27
100+
* 9.0.80 https://github.com/apache/tomcat/commit/77c0ce2d169efa248b64b992e547aad549ec906b
101+
*/
102+
@Test
103+
public void testCVE_2023_41080() throws Exception {
104+
Tomcat tomcat = getTomcatInstance();
105+
Context context = tomcat.addContext("", null);
106+
107+
LoginConfig loginConfig = new LoginConfig();
108+
loginConfig.setAuthMethod("FORM");
109+
loginConfig.setLoginPage("/login");
110+
context.setLoginConfig(loginConfig);
111+
context.getPipeline().addValve(new FormAuthenticator());
112+
113+
SecurityConstraint securityConstraint = new SecurityConstraint();
114+
securityConstraint.addAuthRole("admin");
115+
SecurityCollection securityCollection = new SecurityCollection();
116+
securityCollection.addPattern("/secret.html");
117+
securityCollection.addPattern("/example.com");
118+
securityConstraint.addCollection(securityCollection);
119+
context.addConstraint(securityConstraint);
120+
121+
tomcat.addUser("admin", "admin");
122+
tomcat.addRole("admin", "admin");
123+
124+
Tomcat.addServlet(context, "login", new TestServlet());
125+
context.addServletMappingDecoded("/login", "login");
126+
Tomcat.addServlet(context, "secret", new TestServlet());
127+
context.addServletMappingDecoded("/secret.html", "secret");
128+
Tomcat.addServlet(context, "example", new TestServlet());
129+
context.addServletMappingDecoded("/example.com", "example");
130+
131+
tomcat.start();
132+
133+
String location = doFormLoginAndGetRedirectLocation("http://localhost:" + getPort(), "/secret.html;@example.com");
134+
Assert.assertNotNull(location);
135+
Assert.assertFalse(location.startsWith("//"));
136+
URI locationUri = new URI(location);
137+
Assert.assertNull(locationUri.getHost());
138+
Assert.assertTrue(locationUri.getPath().contains("secret.html"));
139+
140+
location = doFormLoginAndGetRedirectLocation("http://localhost:" + getPort(), "//example.com");
141+
Assert.assertNotNull(location);
142+
Assert.assertFalse(location.startsWith("//"));
143+
}
144+
145+
private static String doFormLoginAndGetRedirectLocation(String basePath, String targetPath) throws Exception {
146+
String url = basePath + targetPath;
147+
Map<String, List<String>> resHead = new HashMap<>();
148+
int rc = getUrl(url, new ByteChunk(), resHead);
149+
Assert.assertEquals(HttpServletResponse.SC_OK, rc);
150+
151+
String sessionCookie = null;
152+
List<String> cookies = resHead.get("Set-Cookie");
153+
if (cookies != null) {
154+
for (String cookie : cookies) {
155+
if (cookie.startsWith("JSESSIONID=")) {
156+
sessionCookie = cookie.split(";")[0];
157+
break;
158+
}
159+
}
160+
}
161+
Assert.assertNotNull("JSESSIONID not found in response", sessionCookie);
162+
String loginUrl = basePath + "/j_security_check";
163+
HttpURLConnection conn = (HttpURLConnection) new URI(loginUrl).toURL().openConnection();
164+
conn.setRequestMethod(Method.POST);
165+
conn.setDoOutput(true);
166+
conn.setInstanceFollowRedirects(false); // want to check the redirect location manually
167+
conn.setRequestProperty("Cookie", sessionCookie);
168+
conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
169+
String params = "j_username=admin&j_password=admin";
170+
try (OutputStream os = conn.getOutputStream()) {
171+
os.write(params.getBytes(StandardCharsets.UTF_8));
172+
}
173+
int loginRc = conn.getResponseCode();
174+
Assert.assertEquals(HttpServletResponse.SC_SEE_OTHER, loginRc);
175+
return conn.getHeaderField("Location");
176+
}
177+
84178
private static int postMultipart(String path, String queryStringParams, int parts) throws IOException, URISyntaxException {
85179
String urlStr = path + (queryStringParams == null || queryStringParams.isEmpty() ? "" : "?" + queryStringParams);
86180
String boundary = "--simpleboundary";
@@ -104,6 +198,7 @@ private static int postMultipart(String path, String queryStringParams, int part
104198
inputStream = conn.getErrorStream();
105199
}
106200
if (inputStream != null) {
201+
//noinspection StatementWithEmptyBody
107202
while (inputStream.read() != -1) {}
108203
inputStream.close();
109204
}
@@ -138,5 +233,9 @@ public static class TestServlet extends HttpServlet {
138233
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException {
139234
req.getParameterMap();
140235
}
236+
237+
@Override
238+
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException {
239+
}
141240
}
142241
}

0 commit comments

Comments
 (0)