VYPR
High severity8.1NVD Advisory· Published Jul 10, 2013· Updated Apr 29, 2026

CVE-2013-2115

CVE-2013-2115

Description

Apache Struts 2 before 2.3.14.2 allows remote attackers to execute arbitrary OGNL code via a crafted request that is not properly handled when using the includeParams attribute in the (1) URL or (2) A tag. NOTE: this issue is due to an incomplete fix for CVE-2013-1966.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
org.apache.struts:struts2-coreMaven
>= 2.0.0, < 2.3.14.22.3.14.2
org.apache.struts.xwork:xwork-coreMaven
>= 2.0.0, < 2.3.14.22.3.14.2

Affected products

1
  • cpe:2.3:a:apache:struts:*:*:*:*:*:*:*:*
    Range: >=2.0.0,<=2.3.14.1

Patches

4
d934c6e7430b

Merged from STRUTS_2_3_14_X

https://github.com/apache/strutsRené GielenMay 27, 2013via ghsa
4 files changed · +127 68
  • core/src/main/java/org/apache/struts2/views/util/DefaultUrlHelper.java+37 39 modified
    @@ -241,47 +241,45 @@ public void buildParametersString(Map<String, Object> params, StringBuilder link
     
         private String buildParameterSubstring(String name, String value) {
             StringBuilder builder = new StringBuilder();
    -        builder.append(translateAndEncode(name));
    +        builder.append(encode(name));
             builder.append('=');
    -        builder.append(translateAndEncode(value));
    +        builder.append(encode(value));
             return builder.toString();
         }
     
    -    /**
    -     * Translates any script expressions using {@link com.opensymphony.xwork2.util.TextParseUtil#translateVariables} and
    -     * encodes the URL using {@link java.net.URLEncoder#encode} with the encoding specified in the configuration.
    -     *
    -     * @param input
    -     * @return the translated and encoded string
    -     */
    -    public String translateAndEncode(String input) {
    -        String translatedInput = translateVariable(input);
    -        try {
    -            return URLEncoder.encode(translatedInput, encoding);
    -        } catch (UnsupportedEncodingException e) {
    -            if (LOG.isWarnEnabled()) {
    -                LOG.warn("Could not encode URL parameter '#0', returning value un-encoded", input);
    -            }
    -            return translatedInput;
    -        }
    -    }
    -
    -    public String translateAndDecode(String input) {
    -        String translatedInput = translateVariable(input);
    -        try {
    -            return URLDecoder.decode(translatedInput, encoding);
    -        } catch (UnsupportedEncodingException e) {
    -            if (LOG.isWarnEnabled()) {
    -                LOG.warn("Could not encode URL parameter '#0', returning value un-encoded", input);
    -            }
    -            return translatedInput;
    -        }
    -    }
    -
    -    private String translateVariable(String input) {
    -        ValueStack valueStack = ServletActionContext.getContext().getValueStack();
    -        return TextParseUtil.translateVariables(input, valueStack);
    -    }
    +	/**
    +	 * Encodes the URL using {@link java.net.URLEncoder#encode} with the encoding specified in the configuration.
    +	 *
    +	 * @param input the input to encode
    +	 * @return the encoded string
    +	 */
    +	public String encode( String input ) {
    +		try {
    +			return URLEncoder.encode(input, encoding);
    +		} catch (UnsupportedEncodingException e) {
    +			if (LOG.isWarnEnabled()) {
    +				LOG.warn("Could not encode URL parameter '#0', returning value un-encoded", input);
    +			}
    +			return input;
    +		}
    +	}
    +
    +	/**
    +	 * Decodes the URL using {@link java.net.URLDecoder#decode(String, String)} with the encoding specified in the configuration.
    +	 *
    +	 * @param input the input to decode
    +	 * @return the encoded string
    +	 */
    +	public String decode( String input ) {
    +		try {
    +			return URLDecoder.decode(input, encoding);
    +		} catch (UnsupportedEncodingException e) {
    +			if (LOG.isWarnEnabled()) {
    +				LOG.warn("Could not decode URL parameter '#0', returning value un-decoded", input);
    +			}
    +			return input;
    +		}
    +	}
     
         public Map<String, Object> parseQueryString(String queryString, boolean forceValueArray) {
             Map<String, Object> queryParams = new LinkedHashMap<String, Object>();
    @@ -299,8 +297,8 @@ public Map<String, Object> parseQueryString(String queryString, boolean forceVal
                             paramValue = tmpParams[1];
                         }
                         if (paramName != null) {
    -                        paramName = translateAndDecode(paramName);
    -                        String translatedParamValue = translateAndDecode(paramValue);
    +                        paramName = decode(paramName);
    +                        String translatedParamValue = decode(paramValue);
     
                             if (queryParams.containsKey(paramName) || forceValueArray) {
                                 // WW-1619 append new param value to existing value(s)
    
  • core/src/test/java/org/apache/struts2/views/jsp/URLTagTest.java+85 20 modified
    @@ -21,16 +21,12 @@
     
     package org.apache.struts2.views.jsp;
     
    -import java.io.File;
    -import java.io.StringWriter;
    -import java.util.ArrayList;
    -import java.util.HashMap;
    -import java.util.List;
    -import java.util.Map;
    -
    -import javax.servlet.http.HttpSession;
    -import javax.servlet.jsp.JspWriter;
    -
    +import com.mockobjects.dynamic.Mock;
    +import com.opensymphony.xwork2.ActionContext;
    +import com.opensymphony.xwork2.ActionProxy;
    +import com.opensymphony.xwork2.DefaultActionInvocation;
    +import com.opensymphony.xwork2.DefaultActionProxyFactory;
    +import com.opensymphony.xwork2.inject.Container;
     import org.apache.struts2.ServletActionContext;
     import org.apache.struts2.components.URL;
     import org.apache.struts2.dispatcher.ApplicationMap;
    @@ -40,15 +36,14 @@
     import org.apache.struts2.dispatcher.mapper.ActionMapping;
     import org.apache.struts2.dispatcher.mapper.DefaultActionMapper;
     
    -import com.mockobjects.dynamic.Mock;
    -import com.opensymphony.xwork2.ActionContext;
    -import com.opensymphony.xwork2.ActionProxy;
    -import com.opensymphony.xwork2.DefaultActionInvocation;
    -import com.opensymphony.xwork2.DefaultActionProxy;
    -import com.opensymphony.xwork2.DefaultActionProxyFactory;
    -import com.opensymphony.xwork2.config.providers.XWorkConfigurationProvider;
    -import com.opensymphony.xwork2.config.providers.XmlConfigurationProvider;
    -import com.opensymphony.xwork2.inject.Container;
    +import javax.servlet.http.HttpSession;
    +import javax.servlet.jsp.JspWriter;
    +import java.io.File;
    +import java.io.StringWriter;
    +import java.util.ArrayList;
    +import java.util.HashMap;
    +import java.util.List;
    +import java.util.Map;
     
     /**
      * Unit test for {@link URLTag}.
    @@ -619,6 +614,69 @@ public void testEmptyActionCustomMapper() throws Exception {
             
         }
     
    +	public void testEmbeddedParamTagExpressionGetsEvaluatedCorrectly() throws Exception {
    +		request.setRequestURI("/public/about");
    +		request.setQueryString("section=team&company=acme inc");
    +
    +		tag.setAction("team");
    +		tag.setIncludeParams("all");
    +
    +		tag.doStartTag();
    +
    +		Foo foo = new Foo("test");
    +		stack.push(foo);
    +
    +		// include nested param tag
    +		ParamTag paramTag = new ParamTag();
    +		paramTag.setPageContext(pageContext);
    +		paramTag.setName("title");
    +		paramTag.setValue("%{title}");
    +		paramTag.doStartTag();
    +		paramTag.doEndTag();
    +
    +		tag.doEndTag();
    +
    +		assertEquals("/team.action?section=team&amp;company=acme+inc&amp;title=test", writer.toString());
    +	}
    +
    +	public void testAccessToStackInternalsGetsHandledCorrectly() throws Exception {
    +		Map<String, Object> params = new HashMap<String, Object>();
    +		params.put("aaa", new String[] {"1${#session[\"foo\"]='true'}"});
    +		params.put("aab", new String[] {"1${#session[\"bar\"]}"});
    +		params.put("aac", new String[] {"1${#_memberAccess[\"allowStaticMethodAccess\"]='true'}"});
    +		params.put("aad", new String[] {"1${#_memberAccess[\"allowStaticMethodAccess\"]}"});
    +
    +		request.setParameterMap(params);
    +		request.setRequestURI("/public/about");
    +		request.setQueryString("aae${%23session[\"bar\"]}=1%24%7B%23session%5B%22bar%22%5D%7D");
    +		session.put("bar", "rab");
    +
    +		tag.setAction("team");
    +		tag.setIncludeParams("all");
    +
    +		tag.doStartTag();
    +		tag.doEndTag();
    +
    +		Object allowMethodAccess = stack.findValue("\u0023_memberAccess['allowStaticMethodAccess']");
    +		assertNotNull(allowMethodAccess);
    +		assertEquals(Boolean.FALSE, allowMethodAccess);
    +
    +		assertNull(session.get("foo"));
    +
    +		assertEquals("/team.action?" +
    +							 "aab=1%24%7B%23session%5B%22bar%22%5D%7D" +
    +							 "&amp;" +
    +							 "aac=1%24%7B%23_memberAccess%5B%22allowStaticMethodAccess%22%5D%3D%27true%27%7D" +
    +							 "&amp;" +
    +							 "aaa=1%24%7B%23session%5B%22foo%22%5D%3D%27true%27%7D" +
    +							 "&amp;" +
    +							 "aad=1%24%7B%23_memberAccess%5B%22allowStaticMethodAccess%22%5D%7D" +
    +							 "&amp;"+
    +						     "aae%24%7B%23session%5B%22bar%22%5D%7D=1%24%7B%23session%5B%22bar%22%5D%7D"
    +				, writer.toString()
    +		);
    +	}
    +
         protected void setUp() throws Exception {
             super.setUp();
     
    @@ -635,7 +693,14 @@ protected void setUp() throws Exception {
         public static class Foo {
             private String title;
     
    -        public void setTitle(String title) {
    +		public Foo() {
    +		}
    +
    +		public Foo( String title ) {
    +			this.title = title;
    +		}
    +
    +		public void setTitle(String title) {
                 this.title = title;
             }
     
    
  • core/src/test/java/org/apache/struts2/views/util/DefaultUrlHelperTest.java+4 4 modified
    @@ -378,17 +378,17 @@ public void testParseNullQuery() throws Exception {
         }
     
     
    -    public void testTranslateAndEncode() throws Exception {
    +    public void testEncode() throws Exception {
             setProp(StrutsConstants.STRUTS_I18N_ENCODING, "UTF-8");
    -        String result = urlHelper.translateAndEncode("\u65b0\u805e");
    +        String result = urlHelper.encode("\u65b0\u805e");
             String expectedResult = "%E6%96%B0%E8%81%9E";
     
             assertEquals(result, expectedResult);
         }
     
    -    public void testTranslateAndDecode() throws Exception {
    +    public void testDecode() throws Exception {
             setProp(StrutsConstants.STRUTS_I18N_ENCODING, "UTF-8");
    -        String result = urlHelper.translateAndDecode("%E6%96%B0%E8%81%9E");
    +        String result = urlHelper.decode("%E6%96%B0%E8%81%9E");
             String expectedResult = "\u65b0\u805e";
     
             assertEquals(result, expectedResult);
    
  • xwork-core/src/main/java/com/opensymphony/xwork2/ognl/SecurityMemberAccess.java+1 5 modified
    @@ -32,7 +32,7 @@
      */
     public class SecurityMemberAccess extends DefaultMemberAccess {
     
    -    private boolean allowStaticMethodAccess;
    +    private final boolean allowStaticMethodAccess;
         Set<Pattern> excludeProperties = Collections.emptySet();
         Set<Pattern> acceptProperties = Collections.emptySet();
     
    @@ -45,10 +45,6 @@ public boolean getAllowStaticMethodAccess() {
             return allowStaticMethodAccess;
         }
     
    -    public void setAllowStaticMethodAccess(boolean allowStaticMethodAccess) {
    -        this.allowStaticMethodAccess = allowStaticMethodAccess;
    -    }
    -
         @Override
         public boolean isAccessible(Map context, Object target, Member member,
                                     String propertyName) {
    
ea96d18d0f75

WW-4063

https://github.com/apache/strutsRené GielenMay 24, 2013via ghsa
1 file changed · +2 2
  • core/src/test/java/org/apache/struts2/views/util/DefaultUrlHelperTest.java+2 2 modified
    @@ -380,15 +380,15 @@ public void testParseNullQuery() throws Exception {
     
         public void testTranslateAndEncode() throws Exception {
             setProp(StrutsConstants.STRUTS_I18N_ENCODING, "UTF-8");
    -        String result = urlHelper.translateAndEncode("\u65b0\u805e");
    +        String result = urlHelper.encode("\u65b0\u805e");
             String expectedResult = "%E6%96%B0%E8%81%9E";
     
             assertEquals(result, expectedResult);
         }
     
         public void testTranslateAndDecode() throws Exception {
             setProp(StrutsConstants.STRUTS_I18N_ENCODING, "UTF-8");
    -        String result = urlHelper.translateAndDecode("%E6%96%B0%E8%81%9E");
    +        String result = urlHelper.decode("%E6%96%B0%E8%81%9E");
             String expectedResult = "\u65b0\u805e";
     
             assertEquals(result, expectedResult);
    
fed4f8e8a4ec

WW-4063

https://github.com/apache/strutsRené GielenMay 24, 2013via ghsa
1 file changed · +37 39
  • core/src/main/java/org/apache/struts2/views/util/DefaultUrlHelper.java+37 39 modified
    @@ -241,47 +241,45 @@ public void buildParametersString(Map<String, Object> params, StringBuilder link
     
         private String buildParameterSubstring(String name, String value) {
             StringBuilder builder = new StringBuilder();
    -        builder.append(translateAndEncode(name));
    +        builder.append(encode(name));
             builder.append('=');
    -        builder.append(translateAndEncode(value));
    +        builder.append(encode(value));
             return builder.toString();
         }
     
    -    /**
    -     * Translates any script expressions using {@link com.opensymphony.xwork2.util.TextParseUtil#translateVariables} and
    -     * encodes the URL using {@link java.net.URLEncoder#encode} with the encoding specified in the configuration.
    -     *
    -     * @param input
    -     * @return the translated and encoded string
    -     */
    -    public String translateAndEncode(String input) {
    -        String translatedInput = translateVariable(input);
    -        try {
    -            return URLEncoder.encode(translatedInput, encoding);
    -        } catch (UnsupportedEncodingException e) {
    -            if (LOG.isWarnEnabled()) {
    -                LOG.warn("Could not encode URL parameter '#0', returning value un-encoded", input);
    -            }
    -            return translatedInput;
    -        }
    -    }
    -
    -    public String translateAndDecode(String input) {
    -        String translatedInput = translateVariable(input);
    -        try {
    -            return URLDecoder.decode(translatedInput, encoding);
    -        } catch (UnsupportedEncodingException e) {
    -            if (LOG.isWarnEnabled()) {
    -                LOG.warn("Could not encode URL parameter '#0', returning value un-encoded", input);
    -            }
    -            return translatedInput;
    -        }
    -    }
    -
    -    private String translateVariable(String input) {
    -        ValueStack valueStack = ServletActionContext.getContext().getValueStack();
    -        return TextParseUtil.translateVariables(input, valueStack);
    -    }
    +	/**
    +	 * Encodes the URL using {@link java.net.URLEncoder#encode} with the encoding specified in the configuration.
    +	 *
    +	 * @param input the input to encode
    +	 * @return the encoded string
    +	 */
    +	public String encode( String input ) {
    +		try {
    +			return URLEncoder.encode(input, encoding);
    +		} catch (UnsupportedEncodingException e) {
    +			if (LOG.isWarnEnabled()) {
    +				LOG.warn("Could not encode URL parameter '#0', returning value un-encoded", input);
    +			}
    +			return input;
    +		}
    +	}
    +
    +	/**
    +	 * Decodes the URL using {@link java.net.URLDecoder#decode(String, String)} with the encoding specified in the configuration.
    +	 *
    +	 * @param input the input to decode
    +	 * @return the encoded string
    +	 */
    +	public String decode( String input ) {
    +		try {
    +			return URLDecoder.decode(input, encoding);
    +		} catch (UnsupportedEncodingException e) {
    +			if (LOG.isWarnEnabled()) {
    +				LOG.warn("Could not decode URL parameter '#0', returning value un-decoded", input);
    +			}
    +			return input;
    +		}
    +	}
     
         public Map<String, Object> parseQueryString(String queryString, boolean forceValueArray) {
             Map<String, Object> queryParams = new LinkedHashMap<String, Object>();
    @@ -299,8 +297,8 @@ public Map<String, Object> parseQueryString(String queryString, boolean forceVal
                             paramValue = tmpParams[1];
                         }
                         if (paramName != null) {
    -                        paramName = translateAndDecode(paramName);
    -                        String translatedParamValue = translateAndDecode(paramValue);
    +                        paramName = decode(paramName);
    +                        String translatedParamValue = decode(paramValue);
     
                             if (queryParams.containsKey(paramName) || forceValueArray) {
                                 // WW-1619 append new param value to existing value(s)
    
d7804297e319

WW-4063

https://github.com/apache/strutsRené GielenMay 24, 2013via ghsa
1 file changed · +1 5
  • xwork-core/src/main/java/com/opensymphony/xwork2/ognl/SecurityMemberAccess.java+1 5 modified
    @@ -32,7 +32,7 @@
      */
     public class SecurityMemberAccess extends DefaultMemberAccess {
     
    -    private boolean allowStaticMethodAccess;
    +    private final boolean allowStaticMethodAccess;
         Set<Pattern> excludeProperties = Collections.emptySet();
         Set<Pattern> acceptProperties = Collections.emptySet();
     
    @@ -45,10 +45,6 @@ public boolean getAllowStaticMethodAccess() {
             return allowStaticMethodAccess;
         }
     
    -    public void setAllowStaticMethodAccess(boolean allowStaticMethodAccess) {
    -        this.allowStaticMethodAccess = allowStaticMethodAccess;
    -    }
    -
         @Override
         public boolean isAccessible(Map context, Object target, Member member,
                                     String propertyName) {
    

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

13

News mentions

0

No linked articles in our index yet.