Skip to content

Conversation

erik-krogh
Copy link
Contributor

@erik-krogh erik-krogh commented Apr 2, 2020

Adds a new query js/xss-through-dom.

Relevant CVEs:

CVE source sink
CVE-2019-14862 element.name IE7 document.createElement(..)
CVE-2016-10735 element.getAttribute("data-target") $(..)
CVE-2019-8331 element.getAttribute('data-original-title') $().html(..)
CVE-2019-15482 $().text() $(..)
CVE-2019-12313 element.innerText element.insertAdjacentHTML(..)
CVE-2018-19048 $().val() element.innerHTML
CVE-2018-8035 element.value $.jGrowl(..)

All of the above CVEs reads a string from a DOM node (attribute or text-node), where that string is controlled by someone else (e.g. a client, another library, rendered by a server...).
This string then ends up in an XSS sink, and a safe text thereby becomes unsafe HTML.

So the query does not flag vulnerabilities that are immediately exploitable. An attacker needs a write primitive to the relevant location.
But the point is that the write primitive is safe on its own, and an xss-through-dom vulnerability will escalate the attack by unescaping the text.

The query currently flags a lot of results, many of these are just bad style and not necessarily exploitable.

Here are some results: https://lgtm.com/query/2351488529036590711/
Many of the results are the old bootstrap CVE (CVE-2016-10735).
(That CVE existed across many versions of bootstrap. The query does not currently flag all versions).

With this PR we get 2 CVE TPs, and one step closer to 4 more CVE TPs.
(I don't know how many of the last 4 we can reasonably get. 3 of them miss have missing type/data-flow, and the last is a missing $.jGrowl sink)

An evaluation shows that the performance overhead is not that big.

TODO:

  • Generalize the new SanitizerGuardNodes to DomBasedXss?
  • change-note

@erik-krogh erik-krogh added JS Awaiting evaluation Do not merge yet, this PR is waiting for an evaluation to finish labels Apr 2, 2020
@erik-krogh erik-krogh removed the Awaiting evaluation Do not merge yet, this PR is waiting for an evaluation to finish label Apr 17, 2020
@erik-krogh erik-krogh marked this pull request as ready for review April 17, 2020 08:58
@erik-krogh erik-krogh requested review from mchammer01 and a team as code owners April 17, 2020 08:58
@erik-krogh erik-krogh removed the request for review from mchammer01 April 17, 2020 09:00
exists(DataFlow::PropRead read | read = this.getCalleeNode() |
read.getBase().getALocalSource() = [dollar(), objectRef()] and
read.getPropertyNameExpr().flow().mayHaveStringValue(name)
)
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we merge this with the above case? It seems to almost subsume it with the only caveat about this only recognizing method calls.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

With the current version we loose support for non-method calls, which I think is OK.
I can quickly add it back by adding a .getLocalSource().

Copy link
Contributor

Choose a reason for hiding this comment

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

Hm, we'd lose quite a few calls. They don't look very interesting I'll give you that, but I think we should keep them.

Copy link
Contributor Author

@erik-krogh erik-krogh Apr 21, 2020

Choose a reason for hiding this comment

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

Yeah, I'm ok with loosing non-method calls, but I'm not comfortable with loosing the reflective calls.

I'll revert it back to having 2 cases.

bindingset[result]
string unsafeAttributeName() {
result.regexpMatch("data-.*") or
result = ["name", "value"]
Copy link
Contributor

Choose a reason for hiding this comment

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

I'd suggest including aria-.* and maybe title and alt as well.

Comment on lines 8 to 9
Writing text from a webpage to the same webpage without properly sanitizing the
input first, might allow for a cross-site scripting vulnerability.
Copy link
Contributor

Choose a reason for hiding this comment

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

This opening paragraph feels a little too generic. I'd try to be a little more concrete, for example:

Suggested change
Writing text from a webpage to the same webpage without properly sanitizing the
input first, might allow for a cross-site scripting vulnerability.
Extracting text from a DOM node and interpreting it as HTML can lead to a cross-site scripting vulnerability.

and then in the next paragraph explain why that's the case, i.e. that it invalidates any escaping already performed on that data.

Copy link
Contributor

@asgerf asgerf left a comment

Choose a reason for hiding this comment

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

LGTM

@mchammer01 can we ask you for a doc review?

Copy link
Contributor

@mchammer01 mchammer01 left a comment

Choose a reason for hiding this comment

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

Yes of course @asgerf 😃. Here is my review:
@erik-krogh - this looks good 👍
There is a small number of minor comments for your consideration.
Also could you add an update to the change notes (the page with a table summarizing the new and updated queries, and that also reports whether query results are shown on LGTM by default or not)? Thanks.
Hope this helps.

@@ -0,0 +1,21 @@
/**
* @name Cross-site scripting through DOM
* @description Writing user controlled DOM to HTML can allow for
Copy link
Contributor

Choose a reason for hiding this comment

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

Shouldn't this be user-controlled (with an hyphen)?

</li>
<li>
OWASP
<a href="https://www.owasp.org/index.php/DOM_Based_XSS">DOM Based XSS</a>.
Copy link
Contributor

Choose a reason for hiding this comment

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

This reference and the one below have a note at the top of the page, saying that their content hasn't been moved to a new platform yet. Just wondering whether we're still happy to link to these URLs (I am not sure whether the URLs will change once the content is ported onto the new platform, so feel free to ignore if my comment is irrelevant).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The links already redirect to the new wiki.
(https://www.owasp.org/index.php/DOM_Based_XSS redirects to https://owasp.org/www-community/attacks/DOM_Based_XSS).

I'll update the links, and the warning will hopefully disappear from their site eventually.

@erik-krogh
Copy link
Contributor Author

@erik-krogh - this looks good
There is a small number of minor comments for your consideration.
Also could you add an update to the change notes (the page with a table summarizing the new and updated queries, and that also reports whether query results are shown on LGTM by default or not)? Thanks.

I've added the change note.

Hope this helps.

It did 👍

Copy link
Contributor

@esbena esbena left a comment

Choose a reason for hiding this comment

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

I see some potential for reuse. Please let me know if they are invalid.

.getAnArgument()
.(StringOps::ConcatenationRoot)
.getConstantStringParts()
.substring(0, 1) = "<"
Copy link
Contributor

Choose a reason for hiding this comment

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

Did you look into reusing some of this logic, or at least using a regexp instead of substring? https://github.com/Semmle/ql/blob/1b88c9768827c37355eea473cba4e24796be310a/javascript/ql/src/semmle/javascript/security/dataflow/Xss.qll#L80-L83

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 can't reuse all of it.
But I can use isPrefixOfJQueryHtmlString, and I'll copy-paste the regexp.

Comment on lines +83 to +84
read.getPropertyName() = propName or
read.getPropertyNameExpr().flow().mayHaveStringValue(propName)
Copy link
Contributor

Choose a reason for hiding this comment

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

I created https://github.com/github/codeql-javascript-team/issues/98 for this pattern just now.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Lets do that in a later PR.

Copy link
Contributor

Choose a reason for hiding this comment

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

Absolutely.

*
* This sanitizer helps prune infeasible paths in type-overloaded functions.
*/
class TypeTestGuard extends TaintTracking::SanitizerGuardNode, DataFlow::ValueNode {
Copy link
Contributor

Choose a reason for hiding this comment

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

Have you considered the sanitizers for js/unsafe-jquery-plugin?https://github.com/Semmle/ql/blob/1b88c9768827c37355eea473cba4e24796be310a/javascript/ql/src/semmle/javascript/security/dataflow/UnsafeJQueryPluginCustomizations.qll#L187-L190

They are also used to eliminate some non-string values.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That sanitizer is a generalization of one of my sanitizers, so that is perfect 👍

@erik-krogh
Copy link
Contributor Author

@mchammer01: Can you approve the changes?

Copy link
Contributor

@mchammer01 mchammer01 left a comment

Choose a reason for hiding this comment

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

Apologies @erik-krogh, I had the day off yesterday.
LGTM, thanks for the documentation updates ✨

@semmle-qlci semmle-qlci merged commit da32926 into github:master Apr 23, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants