Skip to content

Commit 0fec095

Browse files
committed
Fix for CVE-2017-9805.
1 parent 573fbaf commit 0fec095

File tree

3 files changed

+99
-29
lines changed

3 files changed

+99
-29
lines changed

Diff for: xstream-distribution/src/content/changes.html

+8
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,14 @@
2828
filter for the appropriate milestone.
2929
</p>
3030

31+
<h1 id="upcoming-1.4.x">Upcoming 1.4.x maintenance release</h1>
32+
33+
<p>Not yet released.</p>
34+
35+
<p class="highlight">This maintenance release addresses the security vulnerability CVE-2017-9805 reported
36+
originally for Struts' XStream Plugin, an arbitrary execution of commands when unmarshalling for XStream instances
37+
with uninitialized security framework.</p>
38+
3139
<h1 id="1.4.13">1.4.13</h1>
3240

3341
<p>Released September 6, 2020.</p>

Diff for: xstream/src/java/com/thoughtworks/xstream/XStream.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -642,7 +642,7 @@ protected void setupSecurity() {
642642
}
643643

644644
addPermission(AnyTypePermission.ANY);
645-
denyTypes(new String[]{"java.beans.EventHandler"});
645+
denyTypes(new String[]{"java.beans.EventHandler", "javax.imageio.ImageIO$ContainsFilter"});
646646
denyTypesByRegExp(new Pattern[]{LAZY_ITERATORS, JAVAX_CRYPTO});
647647
allowTypeHierarchy(Exception.class);
648648
securityInitialized = false;

Diff for: xstream/src/test/com/thoughtworks/acceptance/SecurityVulnerabilityTest.java

+90-28
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,14 @@
1111
package com.thoughtworks.acceptance;
1212

1313
import java.beans.EventHandler;
14+
import java.util.Iterator;
1415

1516
import com.thoughtworks.xstream.XStream;
1617
import com.thoughtworks.xstream.XStreamException;
1718
import com.thoughtworks.xstream.converters.ConversionException;
18-
import com.thoughtworks.xstream.converters.reflection.ReflectionConverter;
19+
import com.thoughtworks.xstream.core.JVM;
1920
import com.thoughtworks.xstream.security.AnyTypePermission;
2021
import com.thoughtworks.xstream.security.ForbiddenClassException;
21-
import com.thoughtworks.xstream.security.NoTypePermission;
2222

2323

2424
/**
@@ -39,15 +39,15 @@ protected void setupSecurity(XStream xstream) {
3939

4040
public void testCannotInjectEventHandler() {
4141
final String xml = ""
42-
+ "<string class='runnable-array'>\n"
43-
+ " <dynamic-proxy>\n"
44-
+ " <interface>java.lang.Runnable</interface>\n"
45-
+ " <handler class='java.beans.EventHandler'>\n"
46-
+ " <target class='com.thoughtworks.acceptance.SecurityVulnerabilityTest$Exec'/>\n"
47-
+ " <action>exec</action>\n"
48-
+ " </handler>\n"
49-
+ " </dynamic-proxy>\n"
50-
+ "</string>";
42+
+ "<string class='runnable-array'>\n"
43+
+ " <dynamic-proxy>\n"
44+
+ " <interface>java.lang.Runnable</interface>\n"
45+
+ " <handler class='java.beans.EventHandler'>\n"
46+
+ " <target class='com.thoughtworks.acceptance.SecurityVulnerabilityTest$Exec'/>\n"
47+
+ " <action>exec</action>\n"
48+
+ " </handler>\n"
49+
+ " </dynamic-proxy>\n"
50+
+ "</string>";
5151

5252
try {
5353
xstream.fromXML(xml);
@@ -75,33 +75,96 @@ public void testCannotInjectEventHandlerWithUnconfiguredSecurityFramework() {
7575
xstream.fromXML(xml);
7676
fail("Thrown " + XStreamException.class.getName() + " expected");
7777
} catch (final XStreamException e) {
78-
assertTrue(e.getMessage().indexOf(EventHandler.class.getName())>=0);
78+
assertTrue(e.getMessage().indexOf(EventHandler.class.getName()) >= 0);
7979
}
8080
assertEquals(0, BUFFER.length());
8181
}
8282

8383
public void testExplicitlyConvertEventHandler() {
8484
final String xml = ""
85-
+ "<string class='runnable-array'>\n"
86-
+ " <dynamic-proxy>\n"
87-
+ " <interface>java.lang.Runnable</interface>\n"
88-
+ " <handler class='java.beans.EventHandler'>\n"
89-
+ " <target class='com.thoughtworks.acceptance.SecurityVulnerabilityTest$Exec'/>\n"
90-
+ " <action>exec</action>\n"
91-
+ " </handler>\n"
92-
+ " </dynamic-proxy>\n"
93-
+ "</string>";
85+
+ "<string class='runnable-array'>\n"
86+
+ " <dynamic-proxy>\n"
87+
+ " <interface>java.lang.Runnable</interface>\n"
88+
+ " <handler class='java.beans.EventHandler'>\n"
89+
+ " <target class='com.thoughtworks.acceptance.SecurityVulnerabilityTest$Exec'/>\n"
90+
+ " <action>exec</action>\n"
91+
+ " </handler>\n"
92+
+ " </dynamic-proxy>\n"
93+
+ "</string>";
9494

9595
xstream.allowTypes(new Class[]{EventHandler.class});
96-
xstream.registerConverter(new ReflectionConverter(xstream.getMapper(), xstream
97-
.getReflectionProvider(), EventHandler.class));
9896

9997
final Runnable[] array = (Runnable[])xstream.fromXML(xml);
10098
assertEquals(0, BUFFER.length());
10199
array[0].run();
102100
assertEquals("Executed!", BUFFER.toString());
103101
}
104102

103+
public void testCannotInjectConvertImageIOContainsFilterWithUnconfiguredSecurityFramework() {
104+
if (JVM.isVersion(7)) {
105+
final String xml = ""
106+
+ "<string class='javax.imageio.spi.FilterIterator'>\n"
107+
+ " <iter class='java.util.ArrayList$Itr'>\n"
108+
+ " <cursor>0</cursor>\n"
109+
+ " <lastRet>1</lastRet>\n"
110+
+ " <expectedModCount>1</expectedModCount>\n"
111+
+ " <outer-class>\n"
112+
+ " <com.thoughtworks.acceptance.SecurityVulnerabilityTest_-Exec/>\n"
113+
+ " </outer-class>\n"
114+
+ " </iter>\n"
115+
+ " <filter class='javax.imageio.ImageIO$ContainsFilter'>\n"
116+
+ " <method>\n"
117+
+ " <class>com.thoughtworks.acceptance.SecurityVulnerabilityTest$Exec</class>\n"
118+
+ " <name>exec</name>\n"
119+
+ " <parameter-types/>\n"
120+
+ " </method>\n"
121+
+ " <name>exec</name>\n"
122+
+ " </filter>\n"
123+
+ " <next/>\n"
124+
+ "</string>";
125+
126+
try {
127+
xstream.fromXML(xml);
128+
fail("Thrown " + XStreamException.class.getName() + " expected");
129+
} catch (final XStreamException e) {
130+
assertTrue(e.getMessage().indexOf("javax.imageio.ImageIO$ContainsFilter") >= 0);
131+
}
132+
assertEquals(0, BUFFER.length());
133+
}
134+
}
135+
136+
public void testExplicitlyConvertImageIOContainsFilter() {
137+
if (JVM.isVersion(7)) {
138+
final String xml = ""
139+
+ "<string class='javax.imageio.spi.FilterIterator'>\n"
140+
+ " <iter class='java.util.ArrayList$Itr'>\n"
141+
+ " <cursor>0</cursor>\n"
142+
+ " <lastRet>1</lastRet>\n"
143+
+ " <expectedModCount>1</expectedModCount>\n"
144+
+ " <outer-class>\n"
145+
+ " <com.thoughtworks.acceptance.SecurityVulnerabilityTest_-Exec/>\n"
146+
+ " </outer-class>\n"
147+
+ " </iter>\n"
148+
+ " <filter class='javax.imageio.ImageIO$ContainsFilter'>\n"
149+
+ " <method>\n"
150+
+ " <class>com.thoughtworks.acceptance.SecurityVulnerabilityTest$Exec</class>\n"
151+
+ " <name>exec</name>\n"
152+
+ " <parameter-types/>\n"
153+
+ " </method>\n"
154+
+ " <name>exec</name>\n"
155+
+ " </filter>\n"
156+
+ " <next/>\n"
157+
+ "</string>";
158+
159+
xstream.allowTypes(new String[]{"javax.imageio.ImageIO$ContainsFilter"});
160+
161+
final Iterator iterator = (Iterator)xstream.fromXML(xml);
162+
assertEquals(0, BUFFER.length());
163+
iterator.next();
164+
assertEquals("Executed!", BUFFER.toString());
165+
}
166+
}
167+
105168
public static class Exec {
106169

107170
public void exec() {
@@ -120,7 +183,7 @@ public void testInstanceOfVoid() {
120183

121184
public void testDeniedInstanceOfVoid() {
122185
xstream.addPermission(AnyTypePermission.ANY); // clear out defaults
123-
xstream.denyTypes(new Class[] { void.class, Void.class });
186+
xstream.denyTypes(new Class[]{void.class, Void.class});
124187
try {
125188
xstream.fromXML("<void/>");
126189
fail("Thrown " + ForbiddenClassException.class.getName() + " expected");
@@ -130,17 +193,16 @@ public void testDeniedInstanceOfVoid() {
130193
}
131194

132195
public void testAllowedInstanceOfVoid() {
133-
xstream.allowTypes(new Class[] { void.class, Void.class });
196+
xstream.allowTypes(new Class[]{void.class, Void.class});
134197
try {
135198
xstream.fromXML("<void/>");
136199
fail("Thrown " + ConversionException.class.getName() + " expected");
137200
} catch (final ConversionException e) {
138201
assertEquals("void", e.get("construction-type"));
139202
}
140203
}
141-
142-
public static class LazyIterator {
143-
}
204+
205+
public static class LazyIterator {}
144206

145207
public void testInstanceOfLazyIterator() {
146208
xstream.alias("lazy-iterator", LazyIterator.class);

0 commit comments

Comments
 (0)