-
Notifications
You must be signed in to change notification settings - Fork 1.8k
[Java] Add QL for detecting Spring View Manipulation Vulnerabilities. #4214
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
Conversation
Would it also make sense to cover the implicit view name translation? Though that is not really a dataflow / taint issue so would probably need a separate query. |
Yup, I was just adding support for that.
I will add the tests later today too.
|
java/ql/src/experimental/Security/CWE/CWE-094/SpringViewManipulationLib.qll
Outdated
Show resolved
Hide resolved
java/ql/src/experimental/Security/CWE/CWE-094/SpringViewManipulationLib.qll
Outdated
Show resolved
Hide resolved
@smowton I made the changes you requested. I have decided to not include the tests with this one as this is experimental. I hope that's not an issue. I am creating a corresponding GHSL issue for bounty tracking now. |
java/ql/src/experimental/Security/CWE/CWE-094/SpringViewManipulationLib.qll
Outdated
Show resolved
Hide resolved
java/ql/src/experimental/Security/CWE/CWE-094/SpringViewManipulationLib.qll
Show resolved
Hide resolved
java/ql/src/experimental/Security/CWE/CWE-094/SpringViewManipulationLib.qll
Outdated
Show resolved
Hide resolved
class SpringViewManipulationSink extends DataFlow::ExprNode { | ||
SpringViewManipulationSink() { | ||
exists(ReturnStmt r, SpringRequestMappingMethod m | | ||
r.getResult() = this.asExpr() and m.getBody().getAStmt() = r and not m.isResponseBody() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please include Portlet and Servlet versions of ModelAndView
which contains multiple sinks in its constructor and setViewName
.
Also, include the implicit view resolution cases
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@pwntester I can't include the implicit view resolution classes here as they don't actually require flow analysis. they would form part of a separate query.
I have included the sinks for ModelAndView
though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point, but I think it makes sense to add that separate query part of this PR though
java/ql/src/experimental/Security/CWE/CWE-094/SpringViewManipulationLib.qll
Outdated
Show resolved
Hide resolved
r.getResult() = this.asExpr() and | ||
m.getBody().getAStmt() = r and | ||
not m.isResponseBody() and | ||
r.getResult().getType() instanceof TypeString |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We observed many False Positives caused by controller methods performing redirects such as:
return "redirect:" + request.getHeader("Referer");
or
return redirectToRenderSession(bar, foo);
These are open for a new separate bounty submission for Open Redirect, but not relevant in this context. Note that redirect:
prefix can be added directly in the return statement or in a method such as the second example, so the sanitizer should look for string concatenations/interpolations in any node of the dataflow
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
okay I will extract this and create a new PR for the Open Redirect issue.
java/ql/src/experimental/Security/CWE/CWE-094/SpringViewManipulation.ql
Outdated
Show resolved
Hide resolved
conf.hasFlowPath(source, sink) and | ||
not s.hasFlow(_, sink.getNode()) and | ||
not exists(AddExpr e | e.getLeftOperand().(StringLiteral).getRepresentedString() = "redirect:" | | ||
e = sink.getNode().asExpr() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is this necessary given the StringFlowConfig?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, codeql won't recognize the local flow otherwise. I tried testing with the dataflow config alone. QL won't recognise the flow from in the case of statements like
return "redirect:" + request.getHeader("Referer");
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks for the info, it make sense since both the source and the sink belong to the same expression. ccing @aschackmull to double check
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, the StringFlowConfig
likely won't do much, as it only does pure data flow without any taint steps, so it'll only report flow if the literal string "redirect:" (or "ajaxredirect:") reaches the sink unmodified. Adding a suitable string concatenation step as an additional step would address this.
However, checking sanitization through a separate flow config like this likely isn't what you want, since if a sink is reached both by "redirect:" + something
and remotely controlled data, without the former path being a subpath of the latter, then you still have a vulnerability, but this sort of pattern will remove those results leading to false negatives.
Instead there's a much simpler and better solution: Get rid of StringFlowConfig
and insert the concatenated-with-"redirect:" check as a sanitizer directly in the SpringViewManipulationConfig
, i.e. add something like
override predicate isSanitizer(DataFlow::Node node) {
exists(AddExpr e |
node.asExpr() = e and
e.getLeftOperand().(StringLiteral).getRepresentedString() = ["redirect:", "ajaxredirect:"]
)
}
This will remove all such AddExpr
nodes from the data-flow graph in the calculation of flow in SpringViewManipulationConfig
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @aschackmull! @porcupineyhairs do not forget to account for Appendable.append
(StringBuffer, StringBuilder, StringWriter, ...) and String.format
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@aschackmull I considered adding the isSanitizer
check earlier. String concatenation steps such as those suggested by @pwntester would make writing a good sanitizer almost impossible. Hence I chose the flow config. I could change it to a taintflow to fix the issue you pointed earlier. WDYT?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think it sounds that difficult to incorporate those additional kinds of string concatenation in a sanitizer. String.format
should be very similar to an AddExpr
, and for things like StringBuilder
you can likely just mark any local flow from a StringBuilder
that gets a string like "redirect"
as being sanitizers, i.e. all occurrences of x
in the following:
x.append("redirect:");
x.append(tainted());
return x.toString();
As you can see, this will block flow from tainted()
reaching the return
when the final x
is marked as a sanitizer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@aschackmull @pwntester I have added the sanitisers and rebased to the latest main.
java/ql/src/experimental/Security/CWE/CWE-094/SpringViewManipulation.ql
Outdated
Show resolved
Hide resolved
java/ql/src/experimental/Security/CWE/CWE-094/SpringViewManipulationLib.qll
Outdated
Show resolved
Hide resolved
java/ql/src/experimental/Security/CWE/CWE-094/SpringViewManipulationLib.qll
Outdated
Show resolved
Hide resolved
@pwntester @aschackmull I have also included a second query now to keep track of the implicit view resolution classes. |
java/ql/src/experimental/Security/CWE/CWE-094/SpringImplicitViewManipulation.ql
Outdated
Show resolved
Hide resolved
java/ql/src/experimental/Security/CWE/CWE-094/SpringViewManipulationLib.qll
Outdated
Show resolved
Hide resolved
java/ql/src/experimental/Security/CWE/CWE-094/SpringViewManipulationLib.qll
Outdated
Show resolved
Hide resolved
java/ql/src/experimental/Security/CWE/CWE-094/SpringViewManipulationLib.qll
Outdated
Show resolved
Hide resolved
java/ql/src/experimental/Security/CWE/CWE-094/SpringViewManipulationLib.qll
Outdated
Show resolved
Hide resolved
Hi @porcupineyhairs, after reviewing some initial results we still get low SNR. Most FPs are caused for the following reasons (you can use the projects to verify future enhacements): Projects returning a
|
I have made a few changes to my code. it can now avoid some of the FP's you mention above.
As for @aschackmull can you please help me here? |
@aschackmull Thanks to @smowton I have fixed the sanitizers now. With this all of the FP's from the path query, the ones which @pwntester lists above would now be fixed. |
java/ql/src/experimental/Security/CWE/CWE-094/SpringImplicitViewManipulation.ql
Outdated
Show resolved
Hide resolved
java/ql/src/experimental/Security/CWE/CWE-094/SpringImplicitViewManipulation.ql
Outdated
Show resolved
Hide resolved
@pwntester @smowton I went through the results of the implicit view query. There was a FP in anavenianil/watererp_latest. I have added a small sanitizer for that now. With the last FP gone, all of the results detected with the implicit view manipulation query are true positive results. |
@porcupineyhairs it would be great if you could unresolve this conversation: |
@pwntester @smowton I have made further changes to my PR. All of the FP's across both the PR's are now fixed. PTAL. |
java/ql/src/experimental/Security/CWE/CWE-094/SpringImplicitViewManipulation.ql
Outdated
Show resolved
Hide resolved
java/ql/src/experimental/Security/CWE/CWE-094/SpringImplicitViewManipulation.ql
Outdated
Show resolved
Hide resolved
java/ql/src/experimental/Security/CWE/CWE-094/SpringViewManipulationLib.qll
Show resolved
Hide resolved
java/ql/src/experimental/Security/CWE/CWE-094/SpringViewManipulationLib.qll
Outdated
Show resolved
Hide resolved
java/ql/src/experimental/Security/CWE/CWE-094/SpringViewManipulationLib.qll
Outdated
Show resolved
Hide resolved
@smowton Changes done! |
java/ql/src/experimental/Security/CWE/CWE-094/SpringViewManipulationLib.qll
Outdated
Show resolved
Hide resolved
java/ql/src/experimental/Security/CWE/CWE-094/SpringViewManipulationLib.qll
Outdated
Show resolved
Hide resolved
java/ql/src/experimental/Security/CWE/CWE-094/SpringImplicitViewManipulation.ql
Outdated
Show resolved
Hide resolved
java/ql/src/experimental/Security/CWE/CWE-094/SpringImplicitViewManipulation.ql
Outdated
Show resolved
Hide resolved
java/ql/src/experimental/Security/CWE/CWE-094/SpringImplicitViewManipulation.ql
Show resolved
Hide resolved
java/ql/src/experimental/Security/CWE/CWE-094/SpringImplicitViewManipulation.ql
Outdated
Show resolved
Hide resolved
java/ql/src/experimental/Security/CWE/CWE-094/SpringViewManipulationLib.qll
Outdated
Show resolved
Hide resolved
@smowton Changes done |
"getCookies", "getParameter", "getRenderParameters", "getParameterNames", | ||
"getParameterValues", "getParameterMap" | ||
]) | ||
// TODO consider getRemoteUser |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Address comment or expand to note why it is not done yet
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
removed it now. A taint obtained from that field is unlikely to be actually exploitable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Change does not appear to be pushed?
java/ql/src/experimental/Security/CWE/CWE-094/SpringViewManipulationLib.qll
Show resolved
Hide resolved
It would be nice to have some |
|
@aschackmull @smowton I have added the qhelp now and rebased the code to the current main. |
java/ql/src/experimental/Security/CWE/CWE-094/SpringImplicitViewManipulation.ql
Show resolved
Hide resolved
java/ql/src/experimental/Security/CWE/CWE-094/SpringViewManipulation.qhelp
Show resolved
Hide resolved
java/ql/src/experimental/Security/CWE/CWE-094/SpringViewManipulation.qhelp
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to me; over to @aschackmull
java/ql/src/experimental/Security/CWE/CWE-094/SpringViewManipulation.qhelp
Show resolved
Hide resolved
I have rebased it to the latest main while this is pending merge. |
@porcupineyhairs this is a little tricky because of the experimental query + adding the new Portlet source in the same PR. Is the Portlet source (a) important to this query, (b) generally interesting, or (c) both? If (a), then you should make it a query-specific source and not add it to the general RemoteFlowSource |
@smowton Taking option (c). |
Adjust qldoc.
An attacker contolling the name of the Spring View, can exploit Expression Language Injection vulnerabilities as described in this blog.
This PR adds a QL query for the same.