Skip to content
This repository was archived by the owner on Jan 5, 2023. It is now read-only.

Add CWE-79: HTML template escaping passthrough #493

Merged
merged 20 commits into from
Apr 8, 2021

Conversation

gagliardetto
Copy link
Contributor

This PR adds a new experimental query regarding CWE-79: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting'), and more specifically the cases when user-values are converted to types that allow completely avoiding the escaping that normally values would undergo in the HTML template execution.

Usage of such types on user-provided values leads to XSS.

@gagliardetto gagliardetto requested a review from a team as a code owner March 3, 2021 21:41
@smowton
Copy link
Contributor

smowton commented Mar 4, 2021

Have started an all-projects evaluation

* this allows the injection of arbitrary content (html, css, js) into the generated
* output of the templates.
*/
class ConversionFlowToPassthroughTypeConf extends TaintTracking::Configuration {
Copy link
Contributor

Choose a reason for hiding this comment

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

Hm. I'd be curious whether something like

exists(PassthroughTypeNode node | edges*(source, node) and edges*(node, sink) | node.getNode().getType() <is passthrough type>

would work here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

How would that ever be done?

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 mean, how does that work? I haven't seen anything similar before.

Copy link
Contributor

Choose a reason for hiding this comment

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

Sorry, I should have asked what you meant. edges is a predicate that's automatically generated when you import PathGraph and contains the final data-flow graph that's used for displaying results. It's therefore not actually complete, and does the flow-summary that you'll see.

@gagliardetto
Copy link
Contributor Author

Have started an all-projects evaluation

Any news on that front?

@smowton
Copy link
Contributor

smowton commented Mar 8, 2021

Have started an all-projects evaluation

Any news on that front?

47 hits. Observations from triaging the first 20:

  • Some FPs due to not checking the templated element flows to the sink (i.e., you currently check we have input -> conversion and input -> execution, but not conversion -> execution. This sort of "waypoint" query, where we want A -> B -> C (here A = remoteflowsource, B = template.JS, C = template.Execute) we need to use three configs: one checking A -> B, one B -> C (either of these can be optimised by restricting to those Bs that match the other; note in this case the configs are dependent and TaintTracking2 must be used), and a third checking A -> C where the B steps are marked as additional flow steps. The long-term fix here is flow tags, which will allow us to solve this in one config.
  • Some FPs because the escaped value has been converted to an integer (e.g. template.HTML("<b>" + userControlledInt + "</b>"))

@gagliardetto
Copy link
Contributor Author

@gagliardetto
Copy link
Contributor Author

Is there an example of flow tags in codeql-go?

@gagliardetto
Copy link
Contributor Author

I added an extra flow condition, and it seems to work (added a test). Not sure about where and how/why use TaintTracking2.

@gagliardetto
Copy link
Contributor Author

It's probably very inefficient 😬

@smowton
Copy link
Contributor

smowton commented Mar 8, 2021

Flow tags are a feature available in the Javascript dataflow lib but regrettably not in Go yet.

@gagliardetto gagliardetto force-pushed the html-template-escaping-passthrough branch from ef5cbe8 to 3e1916e Compare March 8, 2021 22:40
@gagliardetto
Copy link
Contributor Author

Is my code correct? AFAICT it works.

@smowton
Copy link
Contributor

smowton commented Mar 9, 2021

Looks good -- it's possible to make a bit more efficient by restricting some of the flows to only investigate sources/sinks that are involved in a flow according to one of the others (starting with whichever flow we expect to be most rare, then working out from there). That would require using TaintTracking2 etc so dependent flows don't use the same instance of TaintTracking; however it's not necessary particularly when the waypoint (B, type conversion to html/template.JS or similar) is quite unusual. I'll try this out on LGTM now.

@gagliardetto gagliardetto force-pushed the html-template-escaping-passthrough branch from 5bfdc0d to a3ad589 Compare March 9, 2021 14:38
@gagliardetto
Copy link
Contributor Author

Using the same instance of TaintTracking for all the flows is kinda messing up the steps that are displayed in the results (I think that's the cause).

How can that be avoided? I'm trying to use TaintTracking2 but don't know how to combine them together.

@smowton
Copy link
Contributor

smowton commented Mar 9, 2021

Use a different version of TaintTracking for each Configuration. The DataFlow::Node types they use can be directly compared.

@gagliardetto
Copy link
Contributor Author

There's only TaintTracking and TaintTracking2.

@gagliardetto
Copy link
Contributor Author

Nope, still same.

@gagliardetto
Copy link
Contributor Author

Using two ~instances, TaintTracking and TaintTracking2, removes things from the .expected file (probably registering only steps from the TaintTracking).

Not sure whether that's good.

@smowton
Copy link
Contributor

smowton commented Mar 9, 2021

The expected file has three subsections: nodes, edges, and results (the top-level #select). If it removes only nodes and edges that's at least harmless; if it removes results too that's more of a problem. Can you post a diff between two different revisions, with the code changes and corresponding expected file changes?

@gagliardetto

This comment has been minimized.

@gagliardetto
Copy link
Contributor Author

I get the same results with both versions of the query (the first version that uses only one TaintTracking, and the second version using the two different TaintTracking).

@gagliardetto

This comment has been minimized.

@gagliardetto
Copy link
Contributor Author

Never mind. That seems to be the flow steps in a regular flow, too.

@gagliardetto
Copy link
Contributor Author

Going back to using one instance of TaintTracking because it saves all nodes and edges (the results don't change).

@gagliardetto gagliardetto requested review from smowton and sauyon March 11, 2021 14:23
Copy link
Contributor

@smowton smowton left a comment

Choose a reason for hiding this comment

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

Mostly looking good -- I see a few results we could easily exclude though; since this is quite closely related to an XSS query, we should probably include the same sanitizers. They are defined here:

abstract class Sanitizer extends DataFlow::Node { }

And here's an example of a user (https://github.com/github/codeql-go/blob/main/ql/src/semmle/go/security/ReflectedXssCustomizations.qll#L28), though we can just directly include these using the isSanitizer method of any appropriate configuration.

Copy link
Contributor

@smowton smowton left a comment

Choose a reason for hiding this comment

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

A few minor cleanups, then lgtm

@smowton
Copy link
Contributor

smowton commented Apr 8, 2021

Just noticed these had been done -- please do ping when you've applied review suggestions, or it's quite easy not to notice

@smowton smowton force-pushed the html-template-escaping-passthrough branch from ecb7ac9 to 68c0073 Compare April 8, 2021 13:25
@smowton smowton enabled auto-merge April 8, 2021 13:25
@smowton
Copy link
Contributor

smowton commented Apr 8, 2021

Rebased and set to automerge if CI passes.

@gagliardetto
Copy link
Contributor Author

CI passed, but the automerge didn't happen.

@gagliardetto
Copy link
Contributor Author

please do ping when you've applied review suggestions, or it's quite easy not to notice

I will 🤗

@gagliardetto
Copy link
Contributor Author

CI passed, but the automerge didn't happen.

Maybe it's missing the approval.

@smowton smowton merged commit 7bf5abf into github:main Apr 8, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants