Skip to content

Conversation

@maikypedia
Copy link
Contributor

This pull request adds a query for Server Side Template Injection covering ERB and Slim.

Looking forward to your suggestions.

@maikypedia maikypedia requested a review from a team as a code owner February 25, 2023 14:46
@maikypedia maikypedia requested review from asgerf and removed request for a team February 25, 2023 14:46
@calumgrant calumgrant requested review from alexrford and removed request for asgerf February 27, 2023 09:26
Copy link
Contributor

@alexrford alexrford left a comment

Choose a reason for hiding this comment

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

Thanks for your contribution! I'm generally happy with the structure of this and appreciate the use of Concepts.qll.

We generally request that new queries are added to the experimental directory first (https://github.com/github/codeql/blob/main/CONTRIBUTING.md#submitting-a-new-experimental-query) - would you mind moving this query to ruby/ql/src/experimental/?

Otherwise I just have some stylistic and naming suggestions.


/** A call to `ERB.new`, considered as a template construction. */
private class ERBTemplateNewCall extends TemplateConstruction::Range, DataFlow::CallNode {
ERBTemplateNewCall() { this = API::getTopLevelMember("ERB").getAMethodCall("new") }
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
ERBTemplateNewCall() { this = API::getTopLevelMember("ERB").getAMethodCall("new") }
ErbTemplateNewCall() { this = API::getTopLevelMember("ERB").getAMethodCall("new") }

Copy link
Contributor

@alexrford alexrford left a comment

Choose a reason for hiding this comment

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

I'm happy with this, just needs a couple of minor changes before we can merge it:

  • There's a merge conflict in Frameworks.qll - probably because another import was added to the file
  • We need a change note to document the new query. This can be added by creating a new file ruby/ql/src/change-notes/2023-03-15-ssti-query, e.g:
---
category: newQuery
---
* Added a new experimental query, `rb/server-side-template-injection`, to detect cases where user input may be embedded into a template's code in an unsafe manner.

(feel free to change the wording in this note if you'd like).

@maikypedia maikypedia requested a review from a team as a code owner March 16, 2023 15:15
@alexrford alexrford self-requested a review March 17, 2023 11:32
alexrford
alexrford previously approved these changes Mar 17, 2023
@github-actions
Copy link
Contributor

github-actions bot commented Mar 17, 2023

QHelp previews:

ruby/ql/src/experimental/template-injection/TemplateInjection.qhelp

Server-side template injection

Template Injection occurs when user input is embedded in a template's code in an unsafe manner. An attacker can use native template syntax to inject a malicious payload into a template, which is then executed server-side. This permits the attacker to run arbitrary code in the server's context.

Recommendation

To fix this, ensure that untrusted input is not used as part of a template's code. If the application requirements do not allow this, use a sandboxed environment where access to unsafe attributes and methods is prohibited.

Example

Consider the example given below, an untrusted HTTP parameter name is used to generate a template string. This can lead to remote code execution.

require 'erb'
require 'slim'

class BadERBController < ActionController::Base
  def some_request_handler
    name = params["name"]
    html_text = "
      <!DOCTYPE html><html><body>
      <h2>Hello %s </h2></body></html>
      " % name
    template = ERB.new(html_text).result(binding) 
  end
end

class BadSlimController < ActionController::Base
  def some_request_handler
    name = params["name"]
    html_text = "
      <!DOCTYPE html><html><body>
      <h2>Hello %s </h2></body></html>
      " % name
    Slim::Template.new{ html_text }.render 
  end
end

Here we have fixed the problem by including ERB/Slim syntax in the string, then the user input will be rendered but not evaluated.

require 'erb'
require 'slim'

class GoodController < ActionController::Base
  def some_request_handler
    name = params["name"]
    html_text = "
      <!DOCTYPE html><html><body>
      <h2>Hello <%= name %> </h2></body></html>
      "
    template = ERB.new(html_text).result(binding) 
  end
end

class GoodController < ActionController::Base
  def some_request_handler
    name = params["name"]
    html_text = "
    <!DOCTYPE html>
      html
        body
          h2  == name;
    "
    Slim::Template.new{ html_text }.render(Object.new, name: name)
  end
end

References

Copy link
Contributor

@alexrford alexrford left a comment

Choose a reason for hiding this comment

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

Thanks for your contribution! Merging after CI.

@alexrford alexrford merged commit be163cf into github:main Mar 20, 2023
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