2424import java .net .HttpURLConnection ;
2525import java .net .URI ;
2626import 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
2832import jakarta .servlet .MultipartConfigElement ;
2933import jakarta .servlet .ServletException ;
3741import static org .apache .catalina .startup .SimpleHttpClient .CRLF ;
3842import org .apache .catalina .Context ;
3943import org .apache .catalina .Wrapper ;
44+ import org .apache .catalina .authenticator .FormAuthenticator ;
4045import org .apache .catalina .startup .Tomcat ;
4146import 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
4353public 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