11/**
2- * @name Unsafe deserialization with RMI .
2+ * @name Unsafe remote object .
33 * @description If a registered remote object has a method that accepts a complex object,
44 * an attacker can take advantage of the unsafe deserialization mechanism
55 * which is used to pass parameters in RMI.
66 * In the worst case, it results in remote code execution.
7- * @kind problem
7+ * @kind path- problem
88 * @problem.severity error
99 * @precision high
1010 * @id java/unsafe-deserialization-rmi
1313 */
1414
1515import java
16+ import semmle.code.java.dataflow.TaintTracking
1617import semmle.code.java.frameworks.Rmi
17-
18- private class ObjectInputStream extends Class {
19- ObjectInputStream ( ) { hasQualifiedName ( "java.io" , "ObjectInputStream" ) }
20- }
18+ import DataFlow:: PathGraph
2119
2220/**
2321 * A method that binds a name to a remote object.
@@ -33,34 +31,52 @@ private class BindMethod extends Method {
3331}
3432
3533/**
36- * Looks for a vulnerable method in a `Remote` object .
34+ * Holds if `type` has an unsafe remote method .
3735 */
38- private Method getVulnerableMethod ( RefType type ) {
36+ private predicate hasVulnerableMethod ( RefType type ) {
3937 exists ( RemoteCallableMethod m , Type parameterType |
4038 m .getDeclaringType ( ) = type and parameterType = m .getAParamType ( )
4139 |
4240 not parameterType instanceof PrimitiveType and
4341 not parameterType instanceof TypeString and
44- not parameterType instanceof ObjectInputStream and
45- result = m
42+ not parameterType .( RefType ) .hasQualifiedName ( "java.io" , "ObjectInputStream" )
4643 )
4744}
4845
4946/**
50- * A method call that registers a remote object that has a vulnerable method.
47+ * A taint-tracking configuration for unsafe remote objects
48+ * that are vulnerable to deserialization attacks.
5149 */
52- private class UnsafeRmiBinding extends MethodAccess {
53- Method vulnerableMethod ;
50+ private class BindingUnsafeRemoteObjectConfig extends TaintTracking :: Configuration {
51+ BindingUnsafeRemoteObjectConfig ( ) { this = "BindingUnsafeRemoteObjectConfig" }
5452
55- UnsafeRmiBinding ( ) {
56- this .getMethod ( ) instanceof BindMethod and
57- vulnerableMethod = getVulnerableMethod ( this .getArgument ( 1 ) .getType ( ) )
53+ override predicate isSource ( DataFlow:: Node source ) {
54+ exists ( ConstructorCall cc | cc = source .asExpr ( ) |
55+ hasVulnerableMethod ( cc .getConstructedType ( ) .getASupertype * ( ) )
56+ )
5857 }
5958
60- Method getVulnerableMethod ( ) { result = vulnerableMethod }
59+ override predicate isSink ( DataFlow:: Node sink ) {
60+ exists ( StaticMethodAccess ma | ma .getArgument ( 1 ) = sink .asExpr ( ) |
61+ ma .getMethod ( ) instanceof BindMethod
62+ )
63+ }
64+
65+ override predicate isAdditionalTaintStep ( DataFlow:: Node fromNode , DataFlow:: Node toNode ) {
66+ exists ( StaticMethodAccess ma , Method m | m = ma .getMethod ( ) |
67+ m .getDeclaringType ( ) .hasQualifiedName ( "java.rmi.server" , "UnicastRemoteObject" ) and
68+ m .hasName ( "exportObject" ) and
69+ not ma .getArgument ( [ 2 , 4 ] )
70+ .getType ( )
71+ .( RefType )
72+ .getASupertype * ( )
73+ .hasQualifiedName ( "java.io" , "ObjectInputFilter" ) and
74+ ma .getArgument ( 0 ) = fromNode .asExpr ( ) and
75+ ma = toNode .asExpr ( )
76+ )
77+ }
6178}
6279
63- from UnsafeRmiBinding call , Method vulnerableMethod
64- where vulnerableMethod = call .getVulnerableMethod ( )
65- select call , "Unsafe deserialization with RMI in '$@' method" , vulnerableMethod ,
66- vulnerableMethod .getStringSignature ( )
80+ from DataFlow:: PathNode source , DataFlow:: PathNode sink , BindingUnsafeRemoteObjectConfig conf
81+ where conf .hasFlowPath ( source , sink )
82+ select sink .getNode ( ) , source , sink , "Binding an unsafe remote object."
0 commit comments