Skip to content

Commit a1ccbcd

Browse files
authored
Merge pull request github#5260 from artem-smotrakov/spring-http-invoker
Java: Query for detecting unsafe deserialization with Spring exporters
2 parents e3ab94f + 4b7c57c commit a1ccbcd

24 files changed

+393
-0
lines changed
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
@Configuration
2+
public class Server {
3+
4+
@Bean(name = "/account")
5+
HttpInvokerServiceExporter accountService() {
6+
HttpInvokerServiceExporter exporter = new HttpInvokerServiceExporter();
7+
exporter.setService(new AccountServiceImpl());
8+
exporter.setServiceInterface(AccountService.class);
9+
return exporter;
10+
}
11+
12+
}
13+
14+
class AccountServiceImpl implements AccountService {
15+
16+
@Override
17+
public String echo(String data) {
18+
return data;
19+
}
20+
}
21+
22+
interface AccountService {
23+
String echo(String data);
24+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<bean name="/account" class="org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter">
2+
<property name="service" ref="accountService"/>
3+
<property name="serviceInterface" value="AccountService"/>
4+
</bean>
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<!DOCTYPE qhelp PUBLIC
2+
"-//Semmle//qhelp//EN"
3+
"qhelp.dtd">
4+
<qhelp>
5+
<include src="UnsafeSpringExporterQuery.inc.qhelp" />
6+
<include src="UnsafeSpringExporterInConfigurationClassExample.inc.qhelp" />
7+
<include src="UnsafeSpringExporterReferences.inc.qhelp" />
8+
</qhelp>
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/**
2+
* @name Unsafe deserialization with Spring's remote service exporters.
3+
* @description A Spring bean, which is based on RemoteInvocationSerializingExporter,
4+
* initializes an endpoint that uses ObjectInputStream to deserialize
5+
* incoming data. In the worst case, that may lead to remote code execution.
6+
* @kind problem
7+
* @problem.severity error
8+
* @precision high
9+
* @id java/unsafe-deserialization-spring-exporter-in-configuration-class
10+
* @tags security
11+
* external/cwe/cwe-502
12+
*/
13+
14+
import java
15+
import UnsafeSpringExporterLib
16+
17+
/**
18+
* Holds if `type` is a Spring configuration that declares beans.
19+
*/
20+
private predicate isConfiguration(RefType type) {
21+
type.hasAnnotation("org.springframework.context.annotation", "Configuration") or
22+
isConfigurationAnnotation(type.getAnAnnotation())
23+
}
24+
25+
/**
26+
* Holds if `annotation` is a Java annotations that declares a Spring configuration.
27+
*/
28+
private predicate isConfigurationAnnotation(Annotation annotation) {
29+
isConfiguration(annotation.getType()) or
30+
isConfigurationAnnotation(annotation.getType().getAnAnnotation())
31+
}
32+
33+
/**
34+
* A method that initializes a unsafe bean based on `RemoteInvocationSerializingExporter`.
35+
*/
36+
private class UnsafeBeanInitMethod extends Method {
37+
string identifier;
38+
39+
UnsafeBeanInitMethod() {
40+
isRemoteInvocationSerializingExporter(this.getReturnType()) and
41+
isConfiguration(this.getDeclaringType()) and
42+
exists(Annotation a | this.getAnAnnotation() = a |
43+
a.getType().hasQualifiedName("org.springframework.context.annotation", "Bean") and
44+
if a.getValue("name") instanceof StringLiteral
45+
then identifier = a.getValue("name").(StringLiteral).getRepresentedString()
46+
else identifier = this.getName()
47+
)
48+
}
49+
50+
/**
51+
* Gets this bean's name if given by the `Bean` annotation, or this method's identifier otherwise.
52+
*/
53+
string getBeanIdentifier() { result = identifier }
54+
}
55+
56+
from UnsafeBeanInitMethod method
57+
select method,
58+
"Unsafe deserialization in a Spring exporter bean '" + method.getBeanIdentifier() + "'"
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<!DOCTYPE qhelp PUBLIC
2+
"-//Semmle//qhelp//EN"
3+
"qhelp.dtd">
4+
<qhelp>
5+
6+
<example>
7+
<p>
8+
The following example shows how a vulnerable HTTP endpoint can be defined
9+
using <code>HttpInvokerServiceExporter</code> and Spring annotations:
10+
</p>
11+
<sample src="SpringExporterUnsafeDeserialization.java" />
12+
</example>
13+
14+
</qhelp>
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<!DOCTYPE qhelp PUBLIC
2+
"-//Semmle//qhelp//EN"
3+
"qhelp.dtd">
4+
<qhelp>
5+
<include src="UnsafeSpringExporterQuery.inc.qhelp" />
6+
<include src="UnsafeSpringExporterInXMLConfigurationExample.inc.qhelp" />
7+
<include src="UnsafeSpringExporterReferences.inc.qhelp" />
8+
</qhelp>
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/**
2+
* @name Unsafe deserialization with Spring's remote service exporters.
3+
* @description A Spring bean, which is based on RemoteInvocationSerializingExporter,
4+
* initializes an endpoint that uses ObjectInputStream to deserialize
5+
* incoming data. In the worst case, that may lead to remote code execution.
6+
* @kind problem
7+
* @problem.severity error
8+
* @precision high
9+
* @id java/unsafe-deserialization-spring-exporter-in-xml-configuration
10+
* @tags security
11+
* external/cwe/cwe-502
12+
*/
13+
14+
import java
15+
import semmle.code.java.frameworks.spring.SpringBean
16+
import UnsafeSpringExporterLib
17+
18+
from SpringBean bean
19+
where isRemoteInvocationSerializingExporter(bean.getClass())
20+
select bean, "Unsafe deserialization in a Spring exporter bean '" + bean.getBeanIdentifier() + "'"
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<!DOCTYPE qhelp PUBLIC
2+
"-//Semmle//qhelp//EN"
3+
"qhelp.dtd">
4+
<qhelp>
5+
6+
<example>
7+
<p>
8+
The following examples shows how a vulnerable HTTP endpoint can be defined in a Spring XML config:
9+
</p>
10+
<sample src="SpringExporterUnsafeDeserialization.xml" />
11+
</example>
12+
13+
</qhelp>
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import java
2+
3+
/**
4+
* Holds if `type` is `RemoteInvocationSerializingExporter`.
5+
*/
6+
predicate isRemoteInvocationSerializingExporter(RefType type) {
7+
type.getASupertype*()
8+
.hasQualifiedName("org.springframework.remoting.rmi", "RemoteInvocationSerializingExporter")
9+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<!DOCTYPE qhelp PUBLIC
2+
"-//Semmle//qhelp//EN"
3+
"qhelp.dtd">
4+
<qhelp>
5+
6+
<overview>
7+
<p>
8+
The Spring Framework provides an abstract base class <code>RemoteInvocationSerializingExporter</code>
9+
for creating remote service exporters.
10+
A Spring exporter, which is based on this class, deserializes incoming data using <code>ObjectInputStream</code>.
11+
Deserializing untrusted data is easily exploitable and in many cases allows an attacker
12+
to execute arbitrary code.
13+
</p>
14+
<p>
15+
The Spring Framework also provides <code>HttpInvokerServiceExporter</code>
16+
and <code>SimpleHttpInvokerServiceExporter</code> classes
17+
that extend <code>RemoteInvocationSerializingExporter</code>.
18+
</p>
19+
<p>
20+
These classes export specified beans as HTTP endpoints that deserialize data from an HTTP request
21+
using unsafe <code>ObjectInputStream</code>. If a remote attacker can reach such endpoints,
22+
it results in remote code execution in the worst case.
23+
</p>
24+
<p>
25+
CVE-2016-1000027 has been assigned to this issue in the Spring Framework.
26+
It is regarded as a design limitation, and can be mitigated but not fixed outright.
27+
</p>
28+
</overview>
29+
30+
<recommendation>
31+
<p>
32+
Avoid using <code>HttpInvokerServiceExporter</code>, <code>SimpleHttpInvokerServiceExporter</code>
33+
and any other exporter that is based on <code>RemoteInvocationSerializingExporter</code>.
34+
Instead, use other message formats for API endpoints (for example, JSON),
35+
but make sure that the underlying deserialization mechanism is properly configured
36+
so that deserialization attacks are not possible. If the vulnerable exporters can not be replaced,
37+
consider using global deserialization filters introduced in JEP 290.
38+
</p>
39+
</recommendation>
40+
41+
</qhelp>

0 commit comments

Comments
 (0)