Skip to content

Commit dcabce6

Browse files
Cover beans from XML configs in SpringHttpInvokerUnsafeDeserialization.ql
1 parent 617ba65 commit dcabce6

File tree

4 files changed

+62
-30
lines changed

4 files changed

+62
-30
lines changed
Lines changed: 38 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
/**
2-
* @name Unsafe deserialization with spring's remote service exporters.
3-
* @description Creating a bean based on RemoteInvocationSerializingExporter
4-
* may lead to arbitrary code execution.
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.
56
* @kind problem
67
* @problem.severity error
78
* @precision high
@@ -11,26 +12,14 @@
1112
*/
1213

1314
import java
14-
15-
/**
16-
* Holds if `method` initializes a bean.
17-
*/
18-
private predicate createsBean(Method method) {
19-
method.hasAnnotation("org.springframework.context.annotation", "Bean")
20-
}
15+
import semmle.code.java.frameworks.spring.SpringBean
2116

2217
/**
2318
* Holds if `type` is `RemoteInvocationSerializingExporter`.
2419
*/
2520
private predicate isRemoteInvocationSerializingExporter(RefType type) {
26-
type.hasQualifiedName("org.springframework.remoting.rmi", "RemoteInvocationSerializingExporter")
27-
}
28-
29-
/**
30-
* Holds if `method` returns an object that extends `RemoteInvocationSerializingExporter`.
31-
*/
32-
private predicate returnsRemoteInvocationSerializingExporter(Method method) {
33-
isRemoteInvocationSerializingExporter(method.getReturnType().(RefType).getASupertype*())
21+
type.getASupertype*()
22+
.hasQualifiedName("org.springframework.remoting.rmi", "RemoteInvocationSerializingExporter")
3423
}
3524

3625
/**
@@ -41,15 +30,37 @@ private predicate isInConfiguration(Method method) {
4130
}
4231

4332
/**
44-
* Holds if `method` initializes a bean that is based on `RemoteInvocationSerializingExporter`.
33+
* A method that initializes a unsafe bean based on `RemoteInvocationSerializingExporter`.
4534
*/
46-
private predicate createsRemoteInvocationSerializingExporterBean(Method method) {
47-
isInConfiguration(method) and
48-
createsBean(method) and
49-
returnsRemoteInvocationSerializingExporter(method)
35+
private class UnsafeBeanInitMethod extends Method {
36+
string identifier;
37+
38+
UnsafeBeanInitMethod() {
39+
isInConfiguration(this) and
40+
isRemoteInvocationSerializingExporter(this.getReturnType()) and
41+
exists(Annotation a |
42+
a.getType().hasQualifiedName("org.springframework.context.annotation", "Bean")
43+
|
44+
this.getAnAnnotation() = a and
45+
if a.getValue("name") instanceof StringLiteral
46+
then identifier = a.getValue("name").(StringLiteral).getRepresentedString()
47+
else identifier = this.getName()
48+
)
49+
}
50+
51+
string getBeanIdentifier() { result = identifier }
5052
}
5153

52-
from Method method
53-
where createsRemoteInvocationSerializingExporterBean(method)
54-
select method,
55-
"Unsafe deserialization in a remote service exporter in '" + method.getName() + "' method"
54+
from File file, string identifier
55+
where
56+
exists(UnsafeBeanInitMethod method |
57+
file = method.getFile() and
58+
identifier = method.getBeanIdentifier()
59+
)
60+
or
61+
exists(SpringBean bean |
62+
isRemoteInvocationSerializingExporter(bean.getClass()) and
63+
file = bean.getFile() and
64+
identifier = bean.getBeanIdentifier()
65+
)
66+
select file, "Unsafe deserialization in Spring exporter bean '" + identifier + "'"
Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
1-
| SpringHttpInvokerUnsafeDeserialization.java:10:32:10:63 | unsafeHttpInvokerServiceExporter | Unasafe deserialization in a remote service exporter in 'unsafeHttpInvokerServiceExporter' method |
2-
| SpringHttpInvokerUnsafeDeserialization.java:18:41:18:88 | unsafeCustomeRemoteInvocationSerializingExporter | Unasafe deserialization in a remote service exporter in 'unsafeCustomeRemoteInvocationSerializingExporter' method |
1+
| SpringHttpInvokerUnsafeDeserialization.java:0:0:0:0 | SpringHttpInvokerUnsafeDeserialization | Unsafe deserialization in a remote service exporter bean '/unsafeCustomeRemoteInvocationSerializingExporter' |
2+
| SpringHttpInvokerUnsafeDeserialization.java:0:0:0:0 | SpringHttpInvokerUnsafeDeserialization | Unsafe deserialization in a remote service exporter bean '/unsafeHttpInvokerServiceExporter' |
3+
| beans.xml:0:0:0:0 | beans.xml | Unsafe deserialization in a remote service exporter bean '/unsafeBooking' |
4+
| beans.xml:0:0:0:0 | beans.xml | Unsafe deserialization in a remote service exporter bean 'org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter' |

java/ql/test/experimental/query-tests/security/CWE-502/SpringHttpInvokerUnsafeDeserialization.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
@Configuration
77
public class SpringHttpInvokerUnsafeDeserialization {
8-
8+
99
@Bean(name = "/unsafeHttpInvokerServiceExporter")
1010
HttpInvokerServiceExporter unsafeHttpInvokerServiceExporter() {
1111
HttpInvokerServiceExporter exporter = new HttpInvokerServiceExporter();
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<beans xmlns="http://www.springframework.org/schema/beans"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xmlns:context="http://www.springframework.org/schema/context"
5+
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
6+
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
7+
8+
<bean id="anotherBookingService" class="com.gypsyengineer.server.CabBookingServiceImpl"/>
9+
10+
<bean name="/unsafeBooking" class="org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter">
11+
<property name="service" ref="anotherBookingService"/>
12+
<property name="serviceInterface" value="com.gypsyengineer.api.CabBookingService"/>
13+
</bean>
14+
15+
<bean class="org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter">
16+
<property name="service" ref="anotherBookingService"/>
17+
<property name="serviceInterface" value="com.gypsyengineer.api.CabBookingService"/>
18+
</bean>
19+
</beans>

0 commit comments

Comments
 (0)