- These are diffs of Java classes referenced in the Atlassian advisory for CVE-2022-26134 here
- They are not true diffs of the full patch, they are only diffs of the hot-fix/temporary mitigations provided by Atlassian
- Depending on the version of Confluence, changes in the webwork and xwork classes are relevant
- ActionChanResult (part of xwork) appears to prevent a value from being treating as OGNL (makes sense)
- SafeExpressionUtil (part of webwork) appears to add explicit logic for null checking (may be the most useful clue)
Last active
June 3, 2022 22:25
-
-
Save mzpqnxow/6b3832ecea14350e449d83cb2fb7a9a3 to your computer and use it in GitHub Desktop.
Diff between decompiled class files for xwork-1.0.3-atlassian-7 and xwork-1.0.3-atlassian-10 (CVE-2022-26134)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// For context, the full "old" version of the class | |
// Taken from a decompiled xwork-1.0.3-atlassian-7-m08.jar | |
// - AG | |
// | |
package com.opensymphony.xwork; | |
import com.opensymphony.xwork.util.OgnlValueStack; | |
import com.opensymphony.xwork.util.TextParseUtil; | |
import java.util.HashMap; | |
import java.util.HashSet; | |
import java.util.Set; | |
import org.apache.commons.logging.Log; | |
import org.apache.commons.logging.LogFactory; | |
public class ActionChainResult | |
implements Result | |
{ | |
private static final Log log = LogFactory.getLog(ActionChainResult.class); | |
public static final String DEFAULT_PARAM = "actionName"; | |
private static final String CHAIN_HISTORY = "CHAIN_HISTORY"; | |
private ActionProxy proxy; | |
private String actionName; | |
private String namespace; | |
public void setActionName(String actionName) { this.actionName = actionName; } | |
public void setNamespace(String namespace) { this.namespace = namespace; } | |
public ActionProxy getProxy() { return this.proxy; } | |
public boolean equals(Object o) { | |
if (this == o) { | |
return true; | |
} | |
if (!(o instanceof ActionChainResult)) { | |
return false; | |
} | |
ActionChainResult actionChainResult = (ActionChainResult)o; | |
if ((this.actionName != null) ? !this.actionName.equals(actionChainResult.actionName) : (actionChainResult.actionName != null)) { | |
return false; | |
} | |
return true; | |
} | |
public void execute(ActionInvocation invocation) throws Exception { | |
if (this.namespace == null) { | |
this.namespace = invocation.getProxy().getNamespace(); | |
} | |
OgnlValueStack stack = ActionContext.getContext().getValueStack(); | |
String finalNamespace = TextParseUtil.translateVariables(this.namespace, stack); | |
String finalActionName = TextParseUtil.translateVariables(this.actionName, stack); | |
if (isInChainHistory(finalNamespace, finalActionName)) { | |
throw new XworkException("infinite recursion detected"); | |
} | |
addToHistory(finalNamespace, finalActionName); | |
HashMap extraContext = new HashMap(); | |
extraContext.put("com.opensymphony.xwork.util.OgnlValueStack.ValueStack", ActionContext.getContext().getValueStack()); | |
extraContext.put("com.opensymphony.xwork.ActionContext.parameters", ActionContext.getContext().getParameters()); | |
extraContext.put("com.opensymphony.xwork.interceptor.component.ComponentManager", ActionContext.getContext().get("com.opensymphony.xwork.interceptor.component.ComponentManager")); | |
extraContext.put("CHAIN_HISTORY", ActionContext.getContext().get("CHAIN_HISTORY")); | |
if (log.isDebugEnabled()) { | |
log.debug("Chaining to action " + finalActionName); | |
} | |
this.proxy = ActionProxyFactory.getFactory().createActionProxy(finalNamespace, finalActionName, extraContext); | |
this.proxy.execute(); | |
} | |
public int hashCode() { return (this.actionName != null) ? this.actionName.hashCode() : 0; } | |
private boolean isInChainHistory(String namespace, String actionName) { | |
Set chainHistory = (Set)ActionContext.getContext().get("CHAIN_HISTORY"); | |
if (chainHistory == null) { | |
return false; | |
} | |
return chainHistory.contains(makeKey(namespace, actionName)); | |
} | |
private void addToHistory(String namespace, String actionName) { | |
Set chainHistory = (Set)ActionContext.getContext().get("CHAIN_HISTORY"); | |
if (chainHistory == null) { | |
chainHistory = new HashSet(); | |
} | |
ActionContext.getContext().put("CHAIN_HISTORY", chainHistory); | |
chainHistory.add(makeKey(namespace, actionName)); | |
} | |
private String makeKey(String namespace, String actionName) { return namespace + "/" + actionName; } | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--- ParametersInterceptor-old.java 2022-06-03 18:24:26.358589585 -0400 | |
+++ ParametersInterceptor-new.java 2022-06-03 18:24:43.586589640 -0400 | |
@@ -38,11 +38,19 @@ | |
- | |
public class ParametersInterceptor | |
extends AroundInterceptor | |
{ | |
+ protected boolean acceptableName(String name) { | |
+ if (name.indexOf('=') != -1 || name.indexOf(',') != -1 || name.indexOf('#') != -1 || name.indexOf(':') != -1 || name.indexOf("\\u0023") != -1) { | |
+ return false; | |
+ } | |
+ return true; | |
+ } | |
+ | |
+ | |
protected void after(ActionInvocation dispatcher, String result) throws Exception {} | |
+ | |
protected void before(ActionInvocation invocation) throws Exception { | |
if (!(invocation.getAction() instanceof NoParameters)) { | |
@@ -52,7 +60,7 @@ | |
this.log.debug("Setting params " + parameters); | |
} | |
- ActionContext invocationContext = invocation.getInvocationContext(); | |
+ invocationContext = invocation.getInvocationContext(); | |
try { | |
invocationContext.put("xwork.NullHandler.createNullObjects", Boolean.TRUE); | |
@@ -65,7 +73,11 @@ | |
Iterator iterator = parameters.entrySet().iterator(); | |
while (iterator.hasNext()) { | |
Map.Entry entry = (Map.Entry)iterator.next(); | |
- stack.setValue(entry.getKey().toString(), entry.getValue()); | |
+ String name = entry.getKey().toString(); | |
+ | |
+ if (acceptableName(name)) { | |
+ stack.setValue(name, entry.getValue()); | |
+ } | |
} | |
} | |
} finally { |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--- SafeExpressionUtil.java 2022-06-03 17:18:39.126576907 -0400 | |
+++ SafeExpressionUtil-new.java 2022-06-03 17:19:02.690576982 -0400 | |
@@ -8,6 +8,7 @@ | |
import java.util.Collections; | |
import java.util.HashSet; | |
import java.util.List; | |
+import java.util.Objects; | |
import java.util.Optional; | |
import java.util.Set; | |
import javax.lang.model.SourceVersion; | |
@@ -33,7 +34,6 @@ | |
- | |
class SafeExpressionUtil | |
{ | |
private static final Set<String> UNSAFE_VARIABLE_NAMES; | |
@@ -247,7 +247,7 @@ | |
} | |
if (SourceVersion.isName(trimmedClassName)) { | |
List<String> parentPackageNames = populateParentPackages(trimmedClassName, new ArrayList()); | |
- return parentPackageNames.stream().anyMatch(this.unsafePackageNames::contains); | |
+ Objects.requireNonNull(this.unsafePackageNames); return parentPackageNames.stream().anyMatch(this.unsafePackageNames::contains); | |
} | |
return false; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--- TextParseUtil.old.java 2022-06-03 18:05:35.574585953 -0400 | |
+++ TextParseUtil.new.java 2022-06-03 18:05:50.630586001 -0400 | |
@@ -1,5 +1,8 @@ | |
package com.opensymphony.xwork.util; | |
+import java.util.regex.Matcher; | |
+import java.util.regex.Pattern; | |
+ | |
@@ -24,26 +27,26 @@ | |
public class TextParseUtil | |
{ | |
public static String translateVariables(String expression, OgnlValueStack stack) { | |
- while (true) { | |
- int x = expression.indexOf("${"); | |
- int y = expression.indexOf("}", x); | |
+ StringBuilder sb = new StringBuilder(); | |
+ Pattern p = Pattern.compile("\\$\\{([^}]*)\\}"); | |
+ Matcher m = p.matcher(expression); | |
+ int previous = 0; | |
+ while (m.find()) { | |
+ String value, g = m.group(1); | |
+ int start = m.start(); | |
- if (x != -1 && y != -1) { | |
- String var = expression.substring(x + 2, y); | |
- | |
- Object o = stack.findValue(var); | |
- | |
- if (o != null) { | |
- expression = expression.substring(0, x) + o + expression.substring(y + 1); | |
- continue; | |
- } | |
- expression = expression.substring(0, x) + expression.substring(y + 1); | |
- | |
- continue; | |
+ try { | |
+ Object o = stack.findValue(g); | |
+ value = (o == null) ? "" : o.toString(); | |
+ } catch (Exception ignored) { | |
+ value = ""; | |
} | |
- | |
- break; | |
+ sb.append(expression.substring(previous, start)).append(value); | |
+ previous = m.end(); | |
} | |
- return expression; | |
+ if (previous < expression.length()) { | |
+ sb.append(expression.substring(previous)); | |
+ } | |
+ return sb.toString(); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--- XmlConfigurationProvider-old.java 2022-06-03 17:52:30.786583432 -0400 | |
+++ XmlConfigurationProvider-new.java 2022-06-03 17:53:10.814583561 -0400 | |
@@ -97,17 +97,19 @@ | |
dbf.setNamespaceAware(true); | |
DocumentBuilder db = dbf.newDocumentBuilder(); | |
- db.setEntityResolver(new EntityResolver(this) { private final XmlConfigurationProvider this$0; | |
+ db.setEntityResolver(new EntityResolver() { | |
public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException { | |
+ if ("-//OpenSymphony Group//XWork 1.0.1//EN".equals(publicId)) | |
+ return new InputSource(ClassLoaderUtil.getResourceAsStream("xwork-1.0.1.dtd", XmlConfigurationProvider.class)); | |
if ("-//OpenSymphony Group//XWork 1.0//EN".equals(publicId)) { | |
- return new InputSource(ClassLoaderUtil.getResourceAsStream("xwork-1.0.dtd", (XmlConfigurationProvider.class$com$opensymphony$xwork$config$providers$XmlConfigurationProvider == null) ? (XmlConfigurationProvider.class$com$opensymphony$xwork$config$providers$XmlConfigurationProvider = XmlConfigurationProvider.class$("com.opensymphony.xwork.config.providers.XmlConfigurationProvider")) : XmlConfigurationProvider.class$com$opensymphony$xwork$config$providers$XmlConfigurationProvider)); | |
+ return new InputSource(ClassLoaderUtil.getResourceAsStream("xwork-1.0.dtd", XmlConfigurationProvider.class)); | |
} | |
return null; | |
- } } | |
- ); | |
- db.setErrorHandler(new ErrorHandler(this) { private final XmlConfigurationProvider this$0; | |
- | |
+ } | |
+ }); | |
+ db.setErrorHandler(new ErrorHandler() | |
+ { | |
public void warning(SAXParseException exception) throws SAXException {} | |
public void error(SAXParseException exception) throws SAXException { | |
@@ -118,8 +120,8 @@ | |
public void fatalError(SAXParseException exception) throws SAXException { | |
LOG.fatal(exception.getMessage() + " at (" + exception.getLineNumber() + ":" + exception.getColumnNumber() + ")"); | |
throw exception; | |
- } } | |
- ); | |
+ } | |
+ }); | |
loadConfigurationFile(this.configFileName, db); | |
} catch (Exception e) { | |
LOG.fatal("Could not load XWork configuration file, failing", e); | |
@@ -393,7 +395,7 @@ | |
try { | |
String paramName = (String)resultClass.getField("DEFAULT_PARAM").get(null); | |
params.put(paramName, resultElement.getChildNodes().item(0).getNodeValue()); | |
- } catch (Throwable t) {} | |
+ } catch (Throwable throwable) {} | |
} | |
} | |
@@ -509,7 +511,7 @@ | |
this.includedFileNames.add(fileName); | |
Document doc = null; | |
- InputStream is = null; | |
+ is = null; | |
try { | |
is = getInputStream(fileName); | |
@@ -550,6 +552,8 @@ | |
} else if (nodeName.equals("include")) { | |
String includeFileName = child.getAttribute("file"); | |
loadConfigurationFile(includeFileName, db); | |
+ } else if (nodeName.equals("constant")) { | |
+ addConstant(child); | |
} | |
} | |
} | |
@@ -559,6 +563,21 @@ | |
} | |
} | |
} | |
+ | |
+ private void addConstant(Element packageElement) throws ConfigurationException { | |
+ String name = TextUtils.noNull(packageElement.getAttribute("name")); | |
+ String value = TextUtils.noNull(packageElement.getAttribute("value")); | |
+ if (LOG.isDebugEnabled()) { | |
+ LOG.debug(String.format("Adding %s: \"%s\"", new Object[] { name, value })); | |
+ } | |
+ if ("xwork.excludedClasses".equals(name)) { | |
+ this.configuration.addExcludedClasses(commaDelimitedStringToSet(value)); | |
+ } else if ("xwork.excludedPackageNames".equals(name)) { | |
+ this.configuration.addExcludedPackageNames(commaDelimitedStringToSet(value)); | |
+ } else if ("xwork.allowedClasses".equals(name)) { | |
+ this.configuration.addAllowedClasses(commaDelimitedStringToSet(value)); | |
+ } | |
+ } | |
@@ -573,6 +592,22 @@ | |
return InterceptorBuilder.constructInterceptorReference(context, refName, refParams); | |
} | |
+ | |
+ | |
+ | |
+ | |
+ | |
+ | |
+ private Set<String> commaDelimitedStringToSet(String s) { | |
+ Set<String> set = new HashSet<String>(); | |
+ String[] split = s.split(",", -1); | |
+ for (String aSplit : split) { | |
+ String trimmed = aSplit.trim(); | |
+ if (trimmed.length() > 0) | |
+ set.add(trimmed); | |
+ } | |
+ return set; | |
+ } | |
public XmlConfigurationProvider() {} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--- ActionChainResult.java.old 2022-06-03 16:58:32.690573031 -0400 | |
+++ ActionChainResult.java.new 2022-06-03 16:59:14.582573166 -0400 | |
@@ -1,5 +1,3 @@ | |
-import com.opensymphony.xwork.util.OgnlValueStack; | |
-import com.opensymphony.xwork.util.TextParseUtil; | |
import java.util.HashMap; | |
import java.util.HashSet; | |
import java.util.Set; | |
@@ -24,6 +22,8 @@ | |
public class ActionChainResult | |
implements Result | |
{ | |
@@ -88,10 +88,11 @@ | |
if (this.namespace == null) { | |
this.namespace = invocation.getProxy().getNamespace(); | |
} | |
- OgnlValueStack stack = ActionContext.getContext().getValueStack(); | |
- String finalNamespace = TextParseUtil.translateVariables(this.namespace, stack); | |
- String finalActionName = TextParseUtil.translateVariables(this.actionName, stack); | |
+ String finalNamespace = this.namespace; | |
+ String finalActionName = this.actionName; | |
if (isInChainHistory(finalNamespace, finalActionName)) { | |
throw new XworkException("infinite recursion detected"); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--- OLD-xwork-1.0.3-atlassian-10.jar/xwork-default.xml 2022-06-03 17:44:39.000000000 -0400 | |
+++ NEW-xwork-1.0.3-atlassian-10.jar/xwork-default.xml 2022-06-03 17:44:23.000000000 -0400 | |
@@ -1,4 +1,4 @@ | |
-<!DOCTYPE xwork PUBLIC "-//OpenSymphony Group//XWork 1.0//EN" "http://www.opensymphony.com/xwork/xwork-1.0.dtd"> | |
+<!DOCTYPE xwork PUBLIC "-//OpenSymphony Group//XWork 1.0.1//EN" "http://www.opensymphony.com/xwork/xwork-1.0.1.dtd"> | |
<xwork> | |
<package name="xwork-default"> | |
@@ -31,4 +31,39 @@ | |
</interceptor-stack> | |
</interceptors> | |
</package> | |
-</xwork> | |
\ No newline at end of file | |
+ | |
+ <constant name="xwork.excludedClasses" | |
+ value=" | |
+ java.lang.Object, | |
+ java.lang.Runtime, | |
+ java.lang.System, | |
+ java.lang.Class, | |
+ java.lang.ClassLoader, | |
+ java.lang.Shutdown, | |
+ java.lang.ProcessBuilder, | |
+ java.lang.Thread, | |
+ sun.misc.Unsafe, | |
+ com.opensymphony.xwork2.ActionContext" /> | |
+ | |
+ <constant name="xwork.excludedPackageNames" | |
+ value=" | |
+ ognl, | |
+ java.io, | |
+ java.net, | |
+ java.nio, | |
+ javax, | |
+ freemarker.core, | |
+ freemarker.template, | |
+ freemarker.ext.jsp, | |
+ freemarker.ext.rhino, | |
+ sun.misc, | |
+ sun.reflect, | |
+ javassist, | |
+ org.apache.velocity, | |
+ org.objectweb.asm, | |
+ org.springframework.context, | |
+ com.opensymphony.xwork.util, | |
+ org.apache.tomcat, | |
+ org.apache.catalina.core, | |
+ org.wildfly.extension.undertow.deployment" /> | |
+</xwork> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment