Skip to content

[java] CodeQL query, Increase fastjson detection. Improve RemoteFlowSource class, support SpringMvc. #3665

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions java/ql/src/Security/CWE/CWE-502/UnsafeDeserSpringMvc.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import com.alibaba.fastjson.JSON;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import org.yaml.snakeyaml.Yaml;

import javax.servlet.http.HttpServletRequest;

@RestController
public class UnsafeDeserSpringMvc {

@GetMapping(value = "index")
public void index(HttpServletRequest request){
Yaml yaml = new Yaml();
yaml.load(request.getParameter("str"));
Runtime runtime = Runtime.getRuntime();
HttpServletRequest request1 = request;
}

@GetMapping(value = "fastjson")
public void fastjson(HttpServletRequest request){
JSON.parseObject(request.getParameter("str"));
}

@GetMapping(value = "test")
public void fastjson1(String request){
String temp = request;
JSON.parseObject(temp);
}

@GetMapping(value = "test1")
public void fastjson2(String request){
System.out.println(request);
JSON.parseObject(request);
}

@GetMapping(value = "test2")
public void fastjson3(@RequestBody String request){
JSON.parseObject(request);
}

public void fastjson4(@RequestBody String request){
JSON.parseObject(request);
}
}
2 changes: 1 addition & 1 deletion java/ql/src/Security/CWE/CWE-502/UnsafeDeserialization.ql
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,4 @@ class UnsafeDeserializationConfig extends TaintTracking::Configuration {
from DataFlow::PathNode source, DataFlow::PathNode sink, UnsafeDeserializationConfig conf
where conf.hasFlowPath(source, sink)
select sink.getNode().(UnsafeDeserializationSink).getMethodAccess(), source, sink,
"Unsafe deserialization of $@.", source.getNode(), "user input"
"Unsafe deserialization of $@.", source.getNode(), "user input"
281 changes: 281 additions & 0 deletions java/ql/src/semmle/code/java/FlowSources.qll
Original file line number Diff line number Diff line change
@@ -0,0 +1,281 @@
/**
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This feels seems to be a duplicate of the existing FlowSources.qll file.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don’t think so. The FlowSources.qll file is missing for SpringMVC for remote input. The SpringServletInputParameterSource class can only get the remote part of the user's input, for example the following code is in the source Won't get it.

@GetMapping(value = "index")
public void index(String request){
     System.out.println(request);
}

I added the SpringMVC class to the FlowSources.qll file, mainly to verify whether the class annotation where the method parameters are obtained uses the ``Mapping``` and sub-annotations.
You can try the above code with the following statement.

import java
import semmle.code.java.dataflow.FlowSources

from RemoteFlowSource rfs
select rfs

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You have two FlowSources.qll files in your commit.
java/ql/src/semmle/code/java/dataflow/FlowSources.qll
and
java/ql/src/semmle/code/java/FlowSources.qll

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I have a problem with the project here and need to resubmit it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello
Sorry, I added pr.
Link to the PR:[#3674]

About fastjson introduction:
https://github.com/alibaba/fastjson
Fastjson deserializes through the parse and parseObject methods, and there may be command execution vulnerabilities in deserialization.
Vulnerabilities and fixes:
https://m.sangfor.com/source/blog-network-security/1516.html
https://github.com/alibaba/fastjson/issues

* Provides classes representing various flow sources for taint tracking.
*/

import java
import semmle.code.java.dataflow.DataFlow
import semmle.code.java.dataflow.TaintTracking
import semmle.code.java.dataflow.DefUse
import semmle.code.java.frameworks.Jdbc
import semmle.code.java.frameworks.Networking
import semmle.code.java.frameworks.Properties
import semmle.code.java.frameworks.Rmi
import semmle.code.java.frameworks.Servlets
import semmle.code.java.frameworks.ApacheHttp
import semmle.code.java.frameworks.android.XmlParsing
import semmle.code.java.frameworks.android.WebView
import semmle.code.java.frameworks.JaxWS
import semmle.code.java.frameworks.android.Intent
import semmle.code.java.frameworks.SpringWeb
import semmle.code.java.frameworks.Guice
import semmle.code.java.frameworks.struts.StrutsActions
import semmle.code.java.frameworks.Thrift
import semmle.code.java.frameworks.SpringMVC

/** A data flow source of remote user input. */
abstract class RemoteFlowSource extends DataFlow::Node {
/** Gets a string that describes the type of this remote flow source. */
abstract string getSourceType();
}

private class RemoteTaintedMethodAccessSource extends RemoteFlowSource {
RemoteTaintedMethodAccessSource() {
this.asExpr().(MethodAccess).getMethod() instanceof RemoteTaintedMethod
}

override string getSourceType() { result = "network data source" }
}

private class RmiMethodParameterSource extends RemoteFlowSource {
RmiMethodParameterSource() {
exists(RemoteCallableMethod method |
method.getAParameter() = this.asParameter() and
(
getType() instanceof PrimitiveType or
getType() instanceof TypeString
)
)
}

override string getSourceType() { result = "RMI method parameter" }
}

private class JaxWsMethodParameterSource extends RemoteFlowSource {
JaxWsMethodParameterSource() {
exists(JaxWsEndpoint endpoint |
endpoint.getARemoteMethod().getAParameter() = this.asParameter()
)
}

override string getSourceType() { result = "Jax WS method parameter" }
}

private class JaxRsMethodParameterSource extends RemoteFlowSource {
JaxRsMethodParameterSource() {
exists(JaxRsResourceClass service |
service.getAnInjectableCallable().getAParameter() = this.asParameter() or
service.getAnInjectableField().getAnAccess() = this.asExpr()
)
}

override string getSourceType() { result = "Jax Rs method parameter" }
}

private predicate variableStep(Expr tracked, VarAccess sink) {
exists(VariableAssign def |
def.getSource() = tracked and
defUsePair(def, sink)
)
}

private class ReverseDnsSource extends RemoteFlowSource {
ReverseDnsSource() {
// Try not to trigger on `localhost`.
exists(MethodAccess m | m = this.asExpr() |
m.getMethod() instanceof ReverseDNSMethod and
not exists(MethodAccess l |
(variableStep(l, m.getQualifier()) or l = m.getQualifier()) and
l.getMethod().getName() = "getLocalHost"
)
)
}

override string getSourceType() { result = "reverse DNS lookup" }
}

private class MessageBodyReaderParameterSource extends RemoteFlowSource {
MessageBodyReaderParameterSource() {
exists(MessageBodyReaderRead m |
m.getParameter(4) = this.asParameter() or
m.getParameter(5) = this.asParameter()
)
}

override string getSourceType() { result = "MessageBodyReader parameter" }
}

private class SpringMultipartFileSource extends RemoteFlowSource {
SpringMultipartFileSource() {
exists(MethodAccess ma, Method m |
ma = this.asExpr() and
m = ma.getMethod() and
m.getDeclaringType().hasQualifiedName("org.springframework.web.multipart", "MultipartFile") and
m.getName().matches("get%")
)
}

override string getSourceType() { result = "Spring MultipartFile getter" }
}

private class SpringServletInputParameterSource extends RemoteFlowSource {
SpringServletInputParameterSource() {
this.asParameter().getAnAnnotation() instanceof SpringServletInputAnnotation
}

override string getSourceType() { result = "Spring servlet input parameter" }
}

private class SpringMVC extends RemoteFlowSource {
SpringMVC() {
this.asParameter().getCallable().getAnAnnotation() instanceof SpringMvcAnnotation
}

override string getSourceType() { result = "SpringMVC input parameter" }
}

private class GuiceRequestParameterSource extends RemoteFlowSource {
GuiceRequestParameterSource() {
exists(GuiceRequestParametersAnnotation a |
a = this.asParameter().getAnAnnotation() or
a = this.asExpr().(FieldRead).getField().getAnAnnotation()
)
}

override string getSourceType() { result = "Guice request parameter" }
}

private class Struts2ActionSupportClassFieldReadSource extends RemoteFlowSource {
Struts2ActionSupportClassFieldReadSource() {
exists(Struts2ActionSupportClass c |
c.getASetterMethod().getField() = this.asExpr().(FieldRead).getField()
)
}

override string getSourceType() { result = "Struts2 ActionSupport field" }
}

private class ThriftIfaceParameterSource extends RemoteFlowSource {
ThriftIfaceParameterSource() {
exists(ThriftIface i | i.getAnImplementingMethod().getAParameter() = this.asParameter())
}

override string getSourceType() { result = "Thrift Iface parameter" }
}

/** Class for `tainted` user input. */
abstract class UserInput extends DataFlow::Node { }

/**
* DEPRECATED: Use `RemoteFlowSource` instead.
*
* Input that may be controlled by a remote user.
*/
deprecated class RemoteUserInput extends UserInput {
RemoteUserInput() { this instanceof RemoteFlowSource }
}

/** A node with input that may be controlled by a local user. */
abstract class LocalUserInput extends UserInput { }

/**
* A node with input from the local environment, such as files, standard in,
* environment variables, and main method parameters.
*/
class EnvInput extends LocalUserInput {
EnvInput() {
// Parameters to a main method.
exists(MainMethod main | this.asParameter() = main.getParameter(0))
or
// Args4j arguments.
exists(Field f | this.asExpr() = f.getAnAccess() |
f.getAnAnnotation().getType().getQualifiedName() = "org.kohsuke.args4j.Argument"
)
or
// Results from various specific methods.
this.asExpr().(MethodAccess).getMethod() instanceof EnvTaintedMethod
or
// Access to `System.in`.
exists(Field f | this.asExpr() = f.getAnAccess() | f instanceof SystemIn)
or
// Access to files.
this
.asExpr()
.(ConstructorCall)
.getConstructedType()
.hasQualifiedName("java.io", "FileInputStream")
}
}

/** A node with input from a database. */
class DatabaseInput extends LocalUserInput {
DatabaseInput() { this.asExpr().(MethodAccess).getMethod() instanceof ResultSetGetStringMethod }
}

private class RemoteTaintedMethod extends Method {
RemoteTaintedMethod() {
this instanceof ServletRequestGetParameterMethod or
this instanceof ServletRequestGetParameterMapMethod or
this instanceof ServletRequestGetParameterNamesMethod or
this instanceof HttpServletRequestGetQueryStringMethod or
this instanceof HttpServletRequestGetHeaderMethod or
this instanceof HttpServletRequestGetPathMethod or
this instanceof HttpServletRequestGetHeadersMethod or
this instanceof HttpServletRequestGetHeaderNamesMethod or
this instanceof HttpServletRequestGetRequestURIMethod or
this instanceof HttpServletRequestGetRequestURLMethod or
this instanceof HttpServletRequestGetRemoteUserMethod or
this instanceof ServletRequestGetBodyMethod or
this instanceof CookieGetValueMethod or
this instanceof CookieGetNameMethod or
this instanceof CookieGetCommentMethod or
this instanceof URLConnectionGetInputStreamMethod or
this instanceof SocketGetInputStreamMethod or
this instanceof ApacheHttpGetParams or
this instanceof ApacheHttpEntityGetContent or
// In the setting of Android we assume that XML has been transmitted over
// the network, so may be tainted.
this instanceof XmlPullGetMethod or
this instanceof XmlAttrSetGetMethod or
// The current URL in a browser may be untrusted or uncontrolled.
this instanceof WebViewGetUrlMethod
}
}

private class EnvTaintedMethod extends Method {
EnvTaintedMethod() {
this instanceof MethodSystemGetenv or
this instanceof PropertiesGetPropertyMethod or
this instanceof MethodSystemGetProperty
}
}

/** The type `java.net.InetAddress`. */
class TypeInetAddr extends RefType {
TypeInetAddr() { this.getQualifiedName() = "java.net.InetAddress" }
}

/** A reverse DNS method. */
class ReverseDNSMethod extends Method {
ReverseDNSMethod() {
this.getDeclaringType() instanceof TypeInetAddr and
(
this.getName() = "getHostName" or
this.getName() = "getCanonicalHostName"
)
}
}

/** Android `Intent` that may have come from a hostile application. */
class AndroidIntentInput extends DataFlow::Node {
AndroidIntentInput() {
exists(MethodAccess ma, AndroidGetIntentMethod m |
ma.getMethod().overrides*(m) and
this.asExpr() = ma
)
or
exists(Method m, AndroidReceiveIntentMethod rI |
m.overrides*(rI) and
this.asParameter() = m.getParameter(1)
)
}
}
9 changes: 9 additions & 0 deletions java/ql/src/semmle/code/java/dataflow/FlowSources.qll
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import semmle.code.java.frameworks.SpringWeb
import semmle.code.java.frameworks.Guice
import semmle.code.java.frameworks.struts.StrutsActions
import semmle.code.java.frameworks.Thrift
import semmle.code.java.frameworks.SpringMVC

/** A data flow source of remote user input. */
abstract class RemoteFlowSource extends DataFlow::Node {
Expand Down Expand Up @@ -124,6 +125,14 @@ private class SpringServletInputParameterSource extends RemoteFlowSource {
override string getSourceType() { result = "Spring servlet input parameter" }
}

private class SpringMVC extends RemoteFlowSource {
SpringMVC() {
this.asParameter().getCallable().getAnAnnotation() instanceof SpringMvcAnnotation
}

override string getSourceType() { result = "SpringMVC input parameter" }
}

private class GuiceRequestParameterSource extends RemoteFlowSource {
GuiceRequestParameterSource() {
exists(GuiceRequestParametersAnnotation a |
Expand Down
26 changes: 26 additions & 0 deletions java/ql/src/semmle/code/java/frameworks/FastJson.qll
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import java


/**
* The class `com.alibaba.fastjson.JSON` or `com.alibaba.fastjson.JSONObject`.
*/
class FastJson extends RefType {
FastJson() {
this.hasQualifiedName("com.alibaba.fastjson", "JSON") or
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you should probably either change this predicate or adjust the comment to include JSONObject as well.

this.hasQualifiedName("com.alibaba.fastjson", "JSONObject")
}
}

/**
* Call the parsing method of `JSON` or `JSONObject`.
* sink
* */
class FastJsonParse extends MethodAccess {
FastJsonParse() {
exists(Method m |
m.getDeclaringType() instanceof FastJson and
(m.hasName("parse") or m.hasName("parseObject")) and
m = this.getMethod()
)
}
}
Loading