Moderate severityNVD Advisory· Published Jul 20, 2013· Updated Apr 29, 2026
CVE-2013-2248
CVE-2013-2248
Description
Multiple open redirect vulnerabilities in Apache Struts 2.0.0 through 2.3.15 allow remote attackers to redirect users to arbitrary web sites and conduct phishing attacks via a URL in a parameter using the (1) redirect: or (2) redirectAction: prefix.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
org.apache.struts:struts2-coreMaven | < 2.3.15.1 | 2.3.15.1 |
Affected products
44cpe:2.3:a:apache:struts:2.0.0:*:*:*:*:*:*:*+ 43 more
- cpe:2.3:a:apache:struts:2.0.0:*:*:*:*:*:*:*
- cpe:2.3:a:apache:struts:2.0.1:*:*:*:*:*:*:*
- cpe:2.3:a:apache:struts:2.0.10:*:*:*:*:*:*:*
- cpe:2.3:a:apache:struts:2.0.11:*:*:*:*:*:*:*
- cpe:2.3:a:apache:struts:2.0.11.1:*:*:*:*:*:*:*
- cpe:2.3:a:apache:struts:2.0.11.2:*:*:*:*:*:*:*
- cpe:2.3:a:apache:struts:2.0.12:*:*:*:*:*:*:*
- cpe:2.3:a:apache:struts:2.0.13:*:*:*:*:*:*:*
- cpe:2.3:a:apache:struts:2.0.14:*:*:*:*:*:*:*
- cpe:2.3:a:apache:struts:2.0.2:*:*:*:*:*:*:*
- cpe:2.3:a:apache:struts:2.0.3:*:*:*:*:*:*:*
- cpe:2.3:a:apache:struts:2.0.4:*:*:*:*:*:*:*
- cpe:2.3:a:apache:struts:2.0.5:*:*:*:*:*:*:*
- cpe:2.3:a:apache:struts:2.0.6:*:*:*:*:*:*:*
- cpe:2.3:a:apache:struts:2.0.7:*:*:*:*:*:*:*
- cpe:2.3:a:apache:struts:2.0.8:*:*:*:*:*:*:*
- cpe:2.3:a:apache:struts:2.0.9:*:*:*:*:*:*:*
- cpe:2.3:a:apache:struts:2.1.0:*:*:*:*:*:*:*
- cpe:2.3:a:apache:struts:2.1.1:*:*:*:*:*:*:*
- cpe:2.3:a:apache:struts:2.1.2:*:*:*:*:*:*:*
- cpe:2.3:a:apache:struts:2.1.3:*:*:*:*:*:*:*
- cpe:2.3:a:apache:struts:2.1.4:*:*:*:*:*:*:*
- cpe:2.3:a:apache:struts:2.1.5:*:*:*:*:*:*:*
- cpe:2.3:a:apache:struts:2.1.6:*:*:*:*:*:*:*
- cpe:2.3:a:apache:struts:2.1.8:*:*:*:*:*:*:*
- cpe:2.3:a:apache:struts:2.1.8.1:*:*:*:*:*:*:*
- cpe:2.3:a:apache:struts:2.2.1:*:*:*:*:*:*:*
- cpe:2.3:a:apache:struts:2.2.1.1:*:*:*:*:*:*:*
- cpe:2.3:a:apache:struts:2.2.3:*:*:*:*:*:*:*
- cpe:2.3:a:apache:struts:2.2.3.1:*:*:*:*:*:*:*
- cpe:2.3:a:apache:struts:2.3.1:*:*:*:*:*:*:*
- cpe:2.3:a:apache:struts:2.3.1.1:*:*:*:*:*:*:*
- cpe:2.3:a:apache:struts:2.3.1.2:*:*:*:*:*:*:*
- cpe:2.3:a:apache:struts:2.3.12:*:*:*:*:*:*:*
- cpe:2.3:a:apache:struts:2.3.14:*:*:*:*:*:*:*
- cpe:2.3:a:apache:struts:2.3.14.1:*:*:*:*:*:*:*
- cpe:2.3:a:apache:struts:2.3.14.2:*:*:*:*:*:*:*
- cpe:2.3:a:apache:struts:2.3.14.3:*:*:*:*:*:*:*
- cpe:2.3:a:apache:struts:2.3.15:*:*:*:*:*:*:*
- cpe:2.3:a:apache:struts:2.3.3:*:*:*:*:*:*:*
- cpe:2.3:a:apache:struts:2.3.4:*:*:*:*:*:*:*
- cpe:2.3:a:apache:struts:2.3.4.1:*:*:*:*:*:*:*
- cpe:2.3:a:apache:struts:2.3.7:*:*:*:*:*:*:*
- cpe:2.3:a:apache:struts:2.3.8:*:*:*:*:*:*:*
Patches
2630e1ba065a8Merged from STRUTS_2_3_15_X
3 files changed · +48 −69
apps/showcase/src/main/webapp/tags/non-ui/actionPrefix/actionPrefixExample.ftl+3 −7 modified@@ -37,17 +37,13 @@ <@s.textfield label="Enter Some Text" name="text" /> - <@s.submit name="action:actionPrefix" value="%{'action prefix'}" cssClass="btn" /> + <@s.submit action="actionPrefix" value="%{'action prefix'}" cssClass="btn" /> - <@s.submit name="method:alternateMethod" value="%{'method prefix'}" cssClass="btn" /> - - <@s.submit name="redirect:http://www.google.com" value="%{'redirect prefix'}" cssClass="btn" /> - - <@s.submit name="redirect-action:redirectActionPrefix" value="%{'redirect-action prefix'}" cssClass="btn" /> + <@s.submit method="alternateMethod" value="%{'method prefix'}" cssClass="btn" /> <@s.submit value="Normal Submit" cssClass="btn" /> - <@s.submit name="action:redirectActionPrefixAction" value="%{'redirect-action without prefix'}" cssClass="btn" /> + <@s.submit action="redirectActionPrefixAction" value="%{'redirectAction without prefix'}" cssClass="btn" /> </@s.form> </div>
core/src/main/java/org/apache/struts2/dispatcher/mapper/DefaultActionMapper.java+8 −31 modified@@ -33,11 +33,15 @@ import org.apache.struts2.RequestUtils; import org.apache.struts2.ServletActionContext; import org.apache.struts2.StrutsConstants; -import org.apache.struts2.dispatcher.ServletRedirectResult; import org.apache.struts2.util.PrefixTrie; import javax.servlet.http.HttpServletRequest; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; import java.util.regex.Pattern; /** @@ -164,8 +168,6 @@ public class DefaultActionMapper implements ActionMapper { protected static final String METHOD_PREFIX = "method:"; protected static final String ACTION_PREFIX = "action:"; - protected static final String REDIRECT_PREFIX = "redirect:"; - protected static final String REDIRECT_ACTION_PREFIX = "redirectAction:"; protected boolean allowDynamicMethodCalls = true; protected boolean allowSlashesInActionNames = false; @@ -186,8 +188,7 @@ public DefaultActionMapper() { put(METHOD_PREFIX, new ParameterAction() { public void execute(String key, ActionMapping mapping) { if (allowDynamicMethodCalls) { - mapping.setMethod(key.substring( - METHOD_PREFIX.length())); + mapping.setMethod(key.substring(METHOD_PREFIX.length())); } } }); @@ -203,34 +204,10 @@ public void execute(String key, ActionMapping mapping) { name = name.substring(0, bang); } } - mapping.setName(name); + mapping.setName(cleanupActionName(name)); } }); - put(REDIRECT_PREFIX, new ParameterAction() { - public void execute(String key, ActionMapping mapping) { - ServletRedirectResult redirect = new ServletRedirectResult(); - container.inject(redirect); - redirect.setLocation(key.substring(REDIRECT_PREFIX - .length())); - mapping.setResult(redirect); - } - }); - - put(REDIRECT_ACTION_PREFIX, new ParameterAction() { - public void execute(String key, ActionMapping mapping) { - String location = key.substring(REDIRECT_ACTION_PREFIX - .length()); - ServletRedirectResult redirect = new ServletRedirectResult(); - container.inject(redirect); - String extension = getDefaultExtension(); - if (extension != null && extension.length() > 0) { - location += "." + extension; - } - redirect.setLocation(location); - mapping.setResult(redirect); - } - }); } }; }
core/src/test/java/org/apache/struts2/dispatcher/mapper/DefaultActionMapperTest.java+37 −31 modified@@ -21,21 +21,17 @@ package org.apache.struts2.dispatcher.mapper; -import com.mockobjects.dynamic.Mock; import com.mockobjects.servlet.MockHttpServletRequest; import com.opensymphony.xwork2.ActionContext; -import com.opensymphony.xwork2.ActionInvocation; import com.opensymphony.xwork2.Result; import com.opensymphony.xwork2.config.Configuration; import com.opensymphony.xwork2.config.ConfigurationManager; import com.opensymphony.xwork2.config.entities.PackageConfig; import com.opensymphony.xwork2.config.impl.DefaultConfiguration; import org.apache.struts2.ServletActionContext; import org.apache.struts2.StrutsTestCase; -import org.apache.struts2.dispatcher.ServletRedirectResult; import org.apache.struts2.dispatcher.StrutsResultSupport; import org.apache.struts2.views.jsp.StrutsMockHttpServletRequest; -import org.apache.struts2.views.jsp.StrutsMockHttpServletResponse; import java.util.Arrays; import java.util.HashMap; @@ -447,7 +443,7 @@ public void testActionPrefix_fromIEImageButton() throws Exception { public void testRedirectPrefix() throws Exception { Map parameterMap = new HashMap(); - parameterMap.put(DefaultActionMapper.REDIRECT_PREFIX + "http://www.google.com", ""); + parameterMap.put("redirect:" + "http://www.google.com", ""); StrutsMockHttpServletRequest request = new StrutsMockHttpServletRequest(); request.setupGetServletPath("/someServletPath.action"); @@ -458,25 +454,28 @@ public void testRedirectPrefix() throws Exception { ActionMapping actionMapping = defaultActionMapper.getMapping(request, configManager); Result result = actionMapping.getResult(); - assertNotNull(result); - assertTrue(result instanceof ServletRedirectResult); - - Mock invMock = new Mock(ActionInvocation.class); - ActionInvocation inv = (ActionInvocation) invMock.proxy(); - ActionContext ctx = ActionContext.getContext(); - ctx.put(ServletActionContext.HTTP_REQUEST, request); - StrutsMockHttpServletResponse response = new StrutsMockHttpServletResponse(); - ctx.put(ServletActionContext.HTTP_RESPONSE, response); - invMock.expectAndReturn("getInvocationContext", ctx); - invMock.expectAndReturn("getStack", ctx.getValueStack()); - result.execute(inv); - assertEquals("http://www.google.com", response.getRedirectURL()); - //TODO: need to test location but there's noaccess to the property/method, unless we use reflection + assertNull(result); + } + + public void testUnsafeRedirectPrefix() throws Exception { + Map parameterMap = new HashMap(); + parameterMap.put("redirect:" + "http://%{3*4}", ""); + + StrutsMockHttpServletRequest request = new StrutsMockHttpServletRequest(); + request.setupGetServletPath("/someServletPath.action"); + request.setParameterMap(parameterMap); + + DefaultActionMapper defaultActionMapper = new DefaultActionMapper(); + defaultActionMapper.setContainer(container); + ActionMapping actionMapping = defaultActionMapper.getMapping(request, configManager); + + Result result = actionMapping.getResult(); + assertNull(result); } public void testRedirectActionPrefix() throws Exception { Map parameterMap = new HashMap(); - parameterMap.put(DefaultActionMapper.REDIRECT_ACTION_PREFIX + "myAction", ""); + parameterMap.put("redirectAction:" + "myAction", ""); StrutsMockHttpServletRequest request = new StrutsMockHttpServletRequest(); request.setupGetServletPath("/someServletPath.action"); @@ -488,17 +487,29 @@ public void testRedirectActionPrefix() throws Exception { StrutsResultSupport result = (StrutsResultSupport) actionMapping.getResult(); - assertNotNull(result); - assertTrue(result instanceof ServletRedirectResult); + assertNull(result); + } - assertEquals("myAction.action", result.getLocation()); + public void testUnsafeRedirectActionPrefix() throws Exception { + Map parameterMap = new HashMap(); + parameterMap.put("redirectAction:" + "%{3*4}", ""); - // TODO: need to test location but there's noaccess to the property/method, unless we use reflection + StrutsMockHttpServletRequest request = new StrutsMockHttpServletRequest(); + request.setupGetServletPath("/someServletPath.action"); + request.setParameterMap(parameterMap); + + DefaultActionMapper defaultActionMapper = new DefaultActionMapper(); + defaultActionMapper.setContainer(container); + ActionMapping actionMapping = defaultActionMapper.getMapping(request, configManager); + + + StrutsResultSupport result = (StrutsResultSupport) actionMapping.getResult(); + assertNull(result); } public void testRedirectActionPrefixWithEmptyExtension() throws Exception { Map parameterMap = new HashMap(); - parameterMap.put(DefaultActionMapper.REDIRECT_ACTION_PREFIX + "myAction", ""); + parameterMap.put("redirectAction:" + "myAction", ""); StrutsMockHttpServletRequest request = new StrutsMockHttpServletRequest(); request.setupGetServletPath("/someServletPath"); @@ -511,12 +522,7 @@ public void testRedirectActionPrefixWithEmptyExtension() throws Exception { StrutsResultSupport result = (StrutsResultSupport) actionMapping.getResult(); - assertNotNull(result); - assertTrue(result instanceof ServletRedirectResult); - - assertEquals("myAction", result.getLocation()); - - // TODO: need to test location but there's noaccess to the property/method, unless we use reflection + assertNull(result); } public void testCustomActionPrefix() throws Exception {
3 files changed · +48 −69
apps/showcase/src/main/webapp/tags/non-ui/actionPrefix/actionPrefixExample.ftl+3 −7 modified@@ -37,17 +37,13 @@ <@s.textfield label="Enter Some Text" name="text" /> - <@s.submit name="action:actionPrefix" value="%{'action prefix'}" cssClass="btn" /> + <@s.submit action="actionPrefix" value="%{'action prefix'}" cssClass="btn" /> - <@s.submit name="method:alternateMethod" value="%{'method prefix'}" cssClass="btn" /> - - <@s.submit name="redirect:http://www.google.com" value="%{'redirect prefix'}" cssClass="btn" /> - - <@s.submit name="redirect-action:redirectActionPrefix" value="%{'redirect-action prefix'}" cssClass="btn" /> + <@s.submit method="alternateMethod" value="%{'method prefix'}" cssClass="btn" /> <@s.submit value="Normal Submit" cssClass="btn" /> - <@s.submit name="action:redirectActionPrefixAction" value="%{'redirect-action without prefix'}" cssClass="btn" /> + <@s.submit action="redirectActionPrefixAction" value="%{'redirectAction without prefix'}" cssClass="btn" /> </@s.form> </div>
core/src/main/java/org/apache/struts2/dispatcher/mapper/DefaultActionMapper.java+8 −31 modified@@ -33,11 +33,15 @@ import org.apache.struts2.RequestUtils; import org.apache.struts2.ServletActionContext; import org.apache.struts2.StrutsConstants; -import org.apache.struts2.dispatcher.ServletRedirectResult; import org.apache.struts2.util.PrefixTrie; import javax.servlet.http.HttpServletRequest; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; import java.util.regex.Pattern; /** @@ -164,8 +168,6 @@ public class DefaultActionMapper implements ActionMapper { protected static final String METHOD_PREFIX = "method:"; protected static final String ACTION_PREFIX = "action:"; - protected static final String REDIRECT_PREFIX = "redirect:"; - protected static final String REDIRECT_ACTION_PREFIX = "redirectAction:"; protected boolean allowDynamicMethodCalls = true; protected boolean allowSlashesInActionNames = false; @@ -186,8 +188,7 @@ public DefaultActionMapper() { put(METHOD_PREFIX, new ParameterAction() { public void execute(String key, ActionMapping mapping) { if (allowDynamicMethodCalls) { - mapping.setMethod(key.substring( - METHOD_PREFIX.length())); + mapping.setMethod(key.substring(METHOD_PREFIX.length())); } } }); @@ -203,34 +204,10 @@ public void execute(String key, ActionMapping mapping) { name = name.substring(0, bang); } } - mapping.setName(name); + mapping.setName(cleanupActionName(name)); } }); - put(REDIRECT_PREFIX, new ParameterAction() { - public void execute(String key, ActionMapping mapping) { - ServletRedirectResult redirect = new ServletRedirectResult(); - container.inject(redirect); - redirect.setLocation(key.substring(REDIRECT_PREFIX - .length())); - mapping.setResult(redirect); - } - }); - - put(REDIRECT_ACTION_PREFIX, new ParameterAction() { - public void execute(String key, ActionMapping mapping) { - String location = key.substring(REDIRECT_ACTION_PREFIX - .length()); - ServletRedirectResult redirect = new ServletRedirectResult(); - container.inject(redirect); - String extension = getDefaultExtension(); - if (extension != null && extension.length() > 0) { - location += "." + extension; - } - redirect.setLocation(location); - mapping.setResult(redirect); - } - }); } }; }
core/src/test/java/org/apache/struts2/dispatcher/mapper/DefaultActionMapperTest.java+37 −31 modified@@ -21,21 +21,17 @@ package org.apache.struts2.dispatcher.mapper; -import com.mockobjects.dynamic.Mock; import com.mockobjects.servlet.MockHttpServletRequest; import com.opensymphony.xwork2.ActionContext; -import com.opensymphony.xwork2.ActionInvocation; import com.opensymphony.xwork2.Result; import com.opensymphony.xwork2.config.Configuration; import com.opensymphony.xwork2.config.ConfigurationManager; import com.opensymphony.xwork2.config.entities.PackageConfig; import com.opensymphony.xwork2.config.impl.DefaultConfiguration; import org.apache.struts2.ServletActionContext; import org.apache.struts2.StrutsTestCase; -import org.apache.struts2.dispatcher.ServletRedirectResult; import org.apache.struts2.dispatcher.StrutsResultSupport; import org.apache.struts2.views.jsp.StrutsMockHttpServletRequest; -import org.apache.struts2.views.jsp.StrutsMockHttpServletResponse; import java.util.Arrays; import java.util.HashMap; @@ -447,7 +443,7 @@ public void testActionPrefix_fromIEImageButton() throws Exception { public void testRedirectPrefix() throws Exception { Map parameterMap = new HashMap(); - parameterMap.put(DefaultActionMapper.REDIRECT_PREFIX + "http://www.google.com", ""); + parameterMap.put("redirect:" + "http://www.google.com", ""); StrutsMockHttpServletRequest request = new StrutsMockHttpServletRequest(); request.setupGetServletPath("/someServletPath.action"); @@ -458,25 +454,28 @@ public void testRedirectPrefix() throws Exception { ActionMapping actionMapping = defaultActionMapper.getMapping(request, configManager); Result result = actionMapping.getResult(); - assertNotNull(result); - assertTrue(result instanceof ServletRedirectResult); - - Mock invMock = new Mock(ActionInvocation.class); - ActionInvocation inv = (ActionInvocation) invMock.proxy(); - ActionContext ctx = ActionContext.getContext(); - ctx.put(ServletActionContext.HTTP_REQUEST, request); - StrutsMockHttpServletResponse response = new StrutsMockHttpServletResponse(); - ctx.put(ServletActionContext.HTTP_RESPONSE, response); - invMock.expectAndReturn("getInvocationContext", ctx); - invMock.expectAndReturn("getStack", ctx.getValueStack()); - result.execute(inv); - assertEquals("http://www.google.com", response.getRedirectURL()); - //TODO: need to test location but there's noaccess to the property/method, unless we use reflection + assertNull(result); + } + + public void testUnsafeRedirectPrefix() throws Exception { + Map parameterMap = new HashMap(); + parameterMap.put("redirect:" + "http://%{3*4}", ""); + + StrutsMockHttpServletRequest request = new StrutsMockHttpServletRequest(); + request.setupGetServletPath("/someServletPath.action"); + request.setParameterMap(parameterMap); + + DefaultActionMapper defaultActionMapper = new DefaultActionMapper(); + defaultActionMapper.setContainer(container); + ActionMapping actionMapping = defaultActionMapper.getMapping(request, configManager); + + Result result = actionMapping.getResult(); + assertNull(result); } public void testRedirectActionPrefix() throws Exception { Map parameterMap = new HashMap(); - parameterMap.put(DefaultActionMapper.REDIRECT_ACTION_PREFIX + "myAction", ""); + parameterMap.put("redirectAction:" + "myAction", ""); StrutsMockHttpServletRequest request = new StrutsMockHttpServletRequest(); request.setupGetServletPath("/someServletPath.action"); @@ -488,17 +487,29 @@ public void testRedirectActionPrefix() throws Exception { StrutsResultSupport result = (StrutsResultSupport) actionMapping.getResult(); - assertNotNull(result); - assertTrue(result instanceof ServletRedirectResult); + assertNull(result); + } - assertEquals("myAction.action", result.getLocation()); + public void testUnsafeRedirectActionPrefix() throws Exception { + Map parameterMap = new HashMap(); + parameterMap.put("redirectAction:" + "%{3*4}", ""); - // TODO: need to test location but there's noaccess to the property/method, unless we use reflection + StrutsMockHttpServletRequest request = new StrutsMockHttpServletRequest(); + request.setupGetServletPath("/someServletPath.action"); + request.setParameterMap(parameterMap); + + DefaultActionMapper defaultActionMapper = new DefaultActionMapper(); + defaultActionMapper.setContainer(container); + ActionMapping actionMapping = defaultActionMapper.getMapping(request, configManager); + + + StrutsResultSupport result = (StrutsResultSupport) actionMapping.getResult(); + assertNull(result); } public void testRedirectActionPrefixWithEmptyExtension() throws Exception { Map parameterMap = new HashMap(); - parameterMap.put(DefaultActionMapper.REDIRECT_ACTION_PREFIX + "myAction", ""); + parameterMap.put("redirectAction:" + "myAction", ""); StrutsMockHttpServletRequest request = new StrutsMockHttpServletRequest(); request.setupGetServletPath("/someServletPath"); @@ -511,12 +522,7 @@ public void testRedirectActionPrefixWithEmptyExtension() throws Exception { StrutsResultSupport result = (StrutsResultSupport) actionMapping.getResult(); - assertNotNull(result); - assertTrue(result instanceof ServletRedirectResult); - - assertEquals("myAction", result.getLocation()); - - // TODO: need to test location but there's noaccess to the property/method, unless we use reflection + assertNull(result); } public void testCustomActionPrefix() throws Exception {
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
11- struts.apache.org/release/2.3.x/docs/s2-017.htmlnvdVendor AdvisoryWEB
- github.com/advisories/GHSA-rpj9-r897-wc6qghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2013-2248ghsaADVISORY
- github.com/apache/struts/commit/3cfe34fefedcf0fdcfcb061c0aea34a715b7de6ghsaWEB
- github.com/apache/struts/commit/630e1ba065a8215c4e9ac03bfb09be9d655c2b6eghsaWEB
- issues.apache.org/jira/browse/WW-4140ghsaWEB
- www.fujitsu.com/global/support/software/security/products-f/interstage-bpm-analytics-201301e.htmlnvd
- www.oracle.com/technetwork/topics/security/cpujan2014-1972949.htmlnvd
- www.oracle.com/technetwork/topics/security/cpuoct2013-1899837.htmlnvd
- www.securityfocus.com/bid/61196nvd
- www.securityfocus.com/bid/64758nvd
News mentions
0No linked articles in our index yet.