Skip to content
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

C#: update MaD for HttpRequestMessage and UriBuilder #18694

Open
wants to merge 6 commits into
base: main
Choose a base branch
from

Conversation

LWSimpkins
Copy link
Contributor

Update MaD for C# related to SSRF and URL path traversal scenarios. Some of these are regressions from 2.19.4 to 2.20.0 upgrade, some were missing models.

HttpRequestMessage

  • Change model so constructor for Uri parameter matches string parameter, where the taint is to the object instead of an internal synthetic field (regression)

  • Example:

    HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, new Uri(untrustedUrl)); // Fixed regression. Now the `request` variable is considered tainted again
    (new HttpClient()).SendAsync(request); // SSRF can be flagged
    

UriBuilder

  • Change model for constructor so it flows to synthetic _uri field, which is used in the get_Uri model, so there is taint flow from the constructor to get_Uri

  • Add missing variants of the constructor

  • Add get/set property methods to support dataflow for MemberInitializer in ObjectInitializer

  • Examples:

    (new HttpClient()).GetAsync(new UriBuilder("https", untrustedHost).Uri); // Fixed regression. `untrustedHost` flows to `.Uri`, SSRF can be flagged
    
    (new HttpClient()).GetAsync(new UriBuilder("https", untrustedHost, 443).Uri); // Added constructor variant that was previously missing. `untrustedHost` flows to `.Uri`, SSRF can be flagged
    
    // Added get/set property methods to support MemberInitializer in ObjectInitializer
      var uriBuilder = new UriBuilder
      {
          Scheme = "https",
          Host = untrustedHost,
          Port = 443,
      };
      (new HttpClient()).GetAsync(uriBuilder.Uri); // `untrustedHost` flows to `.Uri`, SSRF can be flagged
    

@Copilot Copilot bot review requested due to automatic review settings February 5, 2025 21:50
@LWSimpkins LWSimpkins requested a review from a team as a code owner February 5, 2025 21:50

Choose a reason for hiding this comment

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

PR Overview

This pull request updates taint flow modeling for HttpRequestMessage and UriBuilder in C#, ensuring that constructed or updated URIs are appropriately recognized as tainted for SSRF and path traversal checks.

  • Fixes regression for HttpRequestMessage constructors taking a Uri parameter, aligning them with taint flow from string-based constructors
  • Adds taint flow to newly modeled UriBuilder constructors and properties

Changes

File Description
csharp/ql/lib/ext/System.model.yml Adds UriBuilder taint flow for multiple constructors, properties, & ToString
csharp/ql/lib/change-notes/2025-02-05-update-system.net.http.httprequestmessage-and-system.uribuilder-models.md Documents the minor analysis changes for HttpRequestMessage & UriBuilder
csharp/ql/lib/ext/System.Net.Http.model.yml Fixes taint flow regression for HttpRequestMessage constructor with Uri

Copilot reviewed 5 out of 5 changed files in this pull request and generated no comments.

Tip: Copilot code review supports C#, Go, Java, JavaScript, Markdown, Python, Ruby and TypeScript, with more languages coming soon. Learn more

Copy link
Contributor

github-actions bot commented Feb 5, 2025

⚠️ The head of this PR and the base branch were compared for differences in the framework coverage reports. The generated reports are available in the artifacts of this workflow run. The differences will be picked up by the nightly job after the PR gets merged.

Click to show differences in coverage

csharp

Generated file changes for csharp

  • Changes to framework-coverage-csharp.rst:
-    System,"``System.*``, ``System``",47,10864,54,5
+    System,"``System.*``, ``System``",47,10896,54,5
-    Totals,,108,12946,400,9
+    Totals,,108,12978,400,9
  • Changes to framework-coverage-csharp.csv:
- System,54,47,10864,,6,5,5,,,4,1,,33,2,,6,15,17,4,3,,5547,5317
+ System,54,47,10896,,6,5,5,,,4,1,,33,2,,6,15,17,4,3,,5579,5317

@LWSimpkins
Copy link
Contributor Author

DCA has been run for this PR (if we got the settings correct; not sure what you typically use). There were no changes to results.

@michaelnebel michaelnebel self-requested a review February 6, 2025 08:39
@michaelnebel
Copy link
Contributor

It looks like the regression happened after re-generating the models: #17666
The model generator mixes the heuristic and content based models for the URI builder in an unfortunate way for this particular usecase.

@michaelnebel
Copy link
Contributor

michaelnebel commented Feb 6, 2025

If we want to model the URIBuilder class manually, I think we should go for a solution that uses the (1) properties of the builder or (2) a less precise solution which taints the entire object (as it was the case before with the generated models). Personally, I would favor (1) as we might risk false positives due to field conflation.
A couple of examples (based on the implementation found here - these are the models for one of the constructors - as this one only taints two specific properties).

- ["System", "UriBuilder", False, "UriBuilder", "(System.String,System.String)", "", "Argument[0]", "Argument[this].Property[System.UriBuilder.Scheme]", "taint", "manual"]
- ["System", "UriBuilder", False, "UriBuilder", "(System.String,System.String)", "", "Argument[1]", "Argument[this].Property[System.UriBuilder.Host]", "taint", "manual"]

@LWSimpkins
Copy link
Contributor Author

LWSimpkins commented Feb 7, 2025

@michaelnebel UriBuilder is complicated in terms of modeling, since multiple properties are tainted by the constructor parameters.

Taking the constructor with the most parameters as an example: public UriBuilder(string? scheme, string? host, int port, string? path, string? extraValue)

Parameter Property
scheme Scheme, also taints Uri
host Host, also taints Uri
port Port, also taints Uri
path Path, also taints Uri
extraValue Query and/or Fragment, also taints Uri

When I tried the suggested change to have the sink use the appropriate parameters and also tried both of the following for .Uri, there was no flow because the Uri property and UriBuilder object were not tainted from the constructor parameters.

- ["System", "UriBuilder", False, "get_Uri", "()", "", "Argument[this]", "ReturnValue", "taint", "manual"]
- ["System", "UriBuilder", False, "get_Uri", "()", "", "Argument[this].Property[System.UriBuilder.Uri", "ReturnValue", "taint", "manual"]

Does MaD support modeling taint to multiple properties/sinks? If not, should I use Argument[this] for everything instead?

@michaelnebel
Copy link
Contributor

michaelnebel commented Feb 7, 2025

@michaelnebel UriBuilder is complicated in terms of modeling, since multiple properties are tainted by the constructor parameters.

Taking the constructor with the most parameters as an example: public UriBuilder(string? scheme, string? host, int port, string? path, string? extraValue)

Parameter Property
scheme Scheme, also taints Uri
host Host, also taints Uri
port Port, also taints Uri
path Path, also taints Uri
extraValue Query and/or Fragment, also taints Uri
When I tried the suggested change to have the sink use the appropriate parameters and also tried both of the following for .Uri, there was no flow because the Uri property and UriBuilder object were not tainted from the constructor parameters.

- ["System", "UriBuilder", False, "get_Uri", "()", "", "Argument[this]", "ReturnValue", "taint", "manual"] - ["System", "UriBuilder", False, "get_Uri", "()", "", "Argument[this].Property[System.UriBuilder.Uri", "ReturnValue", "taint", "manual"]

Does MaD support modeling taint to multiple properties/sinks? If not, should I use Argument[this] for everything instead?

Yes, it does get a bit involved to do the modelling - and you are right the models referring to the Uri property will get a bit more complicated. The Uri property is basically a taint propagator for all the other properties.
That is, (some of) the models for Uri should look something like (one will be required for each other property).

- ["System", "UriBuilder", False, "get_Uri", "()", "", "Argument[this].Property[System.UriBuilder.Scheme]", "ReturnValue", "taint", "manual"]
- ["System", "UriBuilder", False, "get_Uri", "()", "", "Argument[this].Property[System.UriBuilder.Host]", "ReturnValue", "taint", "manual"]

The same pattern is then needed for the constructors that "taints" all properties, eg

- ["System", "UriBuilder", False, "UriBuilder", "(System.Uri)", "", "Argument[0]", "Argument[this].Property[System.UriBuilder.Scheme]", "taint", "manual"]
- ["System", "UriBuilder", False, "UriBuilder", "(System.Uri)", "", "Argument[0]", "Argument[this].Property[System.UriBuilder.Host]", "taint", "manual"]
- ... <For all other properties>

The (other) simpler approach is simply to say that the entire object is tainted if any property is set. However, this carries the risk of false positives. Since, we didn't have a single example in our DCA codebase (and actually in our entire 5k repo suite, where we noticed this regressions), it is probably acceptable to go with the simpler solution (which will require less modelling).

@hvitved
Copy link
Contributor

hvitved commented Feb 7, 2025

I think shorthand notation like this will work as well

- ["System", "UriBuilder", False, "get_Uri", "()", "", "Argument[this].Property[System.UriBuilder.Scheme,System.UriBuilder.Host]", "ReturnValue", "taint", "manual"]
- ["System", "UriBuilder", False, "UriBuilder", "(System.Uri)", "", "Argument[0]", "Argument[this].Property[System.UriBuilder.Scheme,System.UriBuilder.Host]", "taint", "manual"]

@LWSimpkins
Copy link
Contributor Author

LWSimpkins commented Feb 8, 2025

@michaelnebel Excellent, did not realize multiple properties could be modeled like that! Updated PR using the shorthand notation (it worked for me @hvitved). Thank you!

We may open-source parts of the Microsoft SSRF queries in the future, so I went ahead with the more detailed modeling.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants