Skip to content

Java: Promote Server-side template injection from experimental #10352

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

Merged
merged 14 commits into from
Sep 20, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
4 changes: 3 additions & 1 deletion java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ private module Frameworks {
private import semmle.code.java.frameworks.Retrofit
private import semmle.code.java.frameworks.Stream
private import semmle.code.java.frameworks.Strings
private import semmle.code.java.frameworks.Thymeleaf
private import semmle.code.java.frameworks.ratpack.Ratpack
private import semmle.code.java.frameworks.ratpack.RatpackExec
private import semmle.code.java.frameworks.spring.SpringCache
Expand All @@ -141,6 +142,7 @@ private module Frameworks {
private import semmle.code.java.security.LdapInjection
private import semmle.code.java.security.MvelInjection
private import semmle.code.java.security.OgnlInjection
private import semmle.code.java.security.TemplateInjection
private import semmle.code.java.security.XPath
private import semmle.code.java.security.XsltInjection
private import semmle.code.java.frameworks.Jdbc
Expand Down Expand Up @@ -625,7 +627,7 @@ module CsvValidation {
"open-url", "jndi-injection", "ldap", "sql", "jdbc-url", "logging", "mvel", "xpath",
"groovy", "xss", "ognl-injection", "intent-start", "pending-intent-sent",
"url-open-stream", "url-redirect", "create-file", "write-file", "set-hostname-verifier",
"header-splitting", "information-leak", "xslt", "jexl", "bean-validation"
"header-splitting", "information-leak", "xslt", "jexl", "bean-validation", "ssti"
] and
not kind.matches("regex-use%") and
not kind.matches("qltest%") and
Expand Down
16 changes: 16 additions & 0 deletions java/ql/lib/semmle/code/java/frameworks/Thymeleaf.qll
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/**
* Provides classes and predicates for working with the Thymeleaf template engine.
*/

import java
private import semmle.code.java.dataflow.ExternalFlow

private class ThymeleafSummaryModels extends SummaryModelCsv {
override predicate row(string row) {
row =
[
"org.thymeleaf;TemplateSpec;false;TemplateSpec;;;Argument[0];Argument[-1];taint;manual",
"org.thymeleaf;TemplateSpec;false;getTemplate;;;Argument[-1];ReturnValue;taint;manual",
]
}
}
108 changes: 108 additions & 0 deletions java/ql/lib/semmle/code/java/security/TemplateInjection.qll
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/** Definitions related to the server-side template injection (SST) query. */

import java
private import semmle.code.java.dataflow.FlowSources
private import semmle.code.java.dataflow.ExternalFlow
private import semmle.code.java.dataflow.TaintTracking

/**
* A source for server-side template injection (SST) vulnerabilities.
*/
abstract class TemplateInjectionSource extends DataFlow::Node {
/** Holds if this source has the specified `state`. */
predicate hasState(DataFlow::FlowState state) { state instanceof DataFlow::FlowStateEmpty }
}

/**
* A sink for server-side template injection (SST) vulnerabilities.
*/
abstract class TemplateInjectionSink extends DataFlow::Node {
/** Holds if this sink has the specified `state`. */
predicate hasState(DataFlow::FlowState state) { state instanceof DataFlow::FlowStateEmpty }
}

/**
* A unit class for adding additional taint steps.
*
* Extend this class to add additional taint steps that should apply to flows related to
* server-side template injection (SST) vulnerabilities.
*/
class TemplateInjectionAdditionalTaintStep extends Unit {
/**
* Holds if the step from `node1` to `node2` should be considered a taint
* step for flows related to server-side template injection (SST) vulnerabilities.
*/
predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { none() }

/**
* Holds if the step from `node1` to `node2` should be considered a taint
* step for flows related toserver-side template injection (SST) vulnerabilities.
* This step is only applicable in `state1` and updates the flow state to `state2`.
*/
predicate isAdditionalTaintStep(
DataFlow::Node node1, DataFlow::FlowState state1, DataFlow::Node node2,
DataFlow::FlowState state2
) {
none()
}
}

/**
* A sanitizer for server-side template injection (SST) vulnerabilities.
*/
abstract class TemplateInjectionSanitizer extends DataFlow::Node { }

/**
* A sanitizer for server-side template injection (SST) vulnerabilities.
* This sanitizer is only applicable when `TemplateInjectionSanitizerWithState::hasState`
* holds for the flow state.
*/
abstract class TemplateInjectionSanitizerWithState extends DataFlow::Node {
/** Holds if this sanitizer has the specified `state`. */
abstract predicate hasState(DataFlow::FlowState state);
}

private class DefaultTemplateInjectionSource extends TemplateInjectionSource instanceof RemoteFlowSource {
}

private class DefaultTemplateInjectionSink extends TemplateInjectionSink {
DefaultTemplateInjectionSink() { sinkNode(this, "ssti") }
}

private class DefaultTemplateInjectionSanitizer extends TemplateInjectionSanitizer {
DefaultTemplateInjectionSanitizer() {
this.getType() instanceof PrimitiveType or
this.getType() instanceof BoxedType or
this.getType() instanceof NumericType
}
}

private class TemplateInjectionSinkModels extends SinkModelCsv {
override predicate row(string row) {
row =
[
"freemarker.template;Template;true;Template;(String,Reader);;Argument[1];ssti;manual",
"freemarker.template;Template;true;Template;(String,Reader,Configuration);;Argument[1];ssti;manual",
"freemarker.template;Template;true;Template;(String,Reader,Configuration,String);;Argument[1];ssti;manual",
"freemarker.template;Template;true;Template;(String,String,Reader,Configuration);;Argument[2];ssti;manual",
"freemarker.template;Template;true;Template;(String,String,Reader,Configuration,String);;Argument[2];ssti;manual",
"freemarker.template;Template;true;Template;(String,String,Reader,Configuration,ParserConfiguration,String);;Argument[2];ssti;manual",
"freemarker.template;Template;true;Template;(String,String,Configuration);;Argument[1];ssti;manual",
"freemarker.cache;StringTemplateLoader;true;putTemplate;;;Argument[1];ssti;manual",
"com.mitchellbosecke.pebble;PebbleEngine;true;getTemplate;;;Argument[0];ssti;manual",
"com.mitchellbosecke.pebble;PebbleEngine;true;getLiteralTemplate;;;Argument[0];ssti;manual",
"com.hubspot.jinjava;Jinjava;true;renderForResult;;;Argument[0];ssti;manual",
"com.hubspot.jinjava;Jinjava;true;render;;;Argument[0];ssti;manual",
"org.thymeleaf;ITemplateEngine;true;process;;;Argument[0];ssti;manual",
"org.thymeleaf;ITemplateEngine;true;processThrottled;;;Argument[0];ssti;manual",
"org.apache.velocity.app;Velocity;true;evaluate;;;Argument[3];ssti;manual",
"org.apache.velocity.app;Velocity;true;mergeTemplate;;;Argument[2];ssti;manual",
"org.apache.velocity.app;VelocityEngine;true;evaluate;;;Argument[3];ssti;manual",
"org.apache.velocity.app;VelocityEngine;true;mergeTemplate;;;Argument[2];ssti;manual",
"org.apache.velocity.runtime.resource.util;StringResourceRepository;true;putStringResource;;;Argument[1];ssti;manual",
"org.apache.velocity.runtime;RuntimeServices;true;evaluate;;;Argument[3];ssti;manual",
"org.apache.velocity.runtime;RuntimeServices;true;parse;;;Argument[0];ssti;manual",
"org.apache.velocity.runtime;RuntimeSingleton;true;parse;;;Argument[0];ssti;manual"
]
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/** Provides a taint tracking configuration for server-side template injection (SST) vulnerabilities */

import java
import semmle.code.java.dataflow.TaintTracking
import semmle.code.java.dataflow.FlowSources
import semmle.code.java.security.TemplateInjection

/** A taint tracking configuration to reason about server-side template injection (SST) vulnerabilities */
class TemplateInjectionFlowConfig extends TaintTracking::Configuration {
TemplateInjectionFlowConfig() { this = "TemplateInjectionFlowConfig" }

override predicate isSource(DataFlow::Node source, DataFlow::FlowState state) {
source.(TemplateInjectionSource).hasState(state)
}

override predicate isSink(DataFlow::Node sink, DataFlow::FlowState state) {
sink.(TemplateInjectionSink).hasState(state)
}

override predicate isSanitizer(DataFlow::Node sanitizer) {
sanitizer instanceof TemplateInjectionSanitizer
}

override predicate isSanitizer(DataFlow::Node sanitizer, DataFlow::FlowState state) {
sanitizer.(TemplateInjectionSanitizerWithState).hasState(state)
}

override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
any(TemplateInjectionAdditionalTaintStep a).isAdditionalTaintStep(node1, node2)
}

override predicate isAdditionalTaintStep(
DataFlow::Node node1, DataFlow::FlowState state1, DataFlow::Node node2,
DataFlow::FlowState state2
) {
any(TemplateInjectionAdditionalTaintStep a).isAdditionalTaintStep(node1, state1, node2, state2)
}
}
32 changes: 32 additions & 0 deletions java/ql/src/Security/CWE/CWE-094/TemplateInjection.qhelp
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<!DOCTYPE qhelp PUBLIC "-//Semmle//qhelp//EN" "qhelp.dtd">
<qhelp>
<overview>
<p>
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.
</p>
</overview>
<recommendation>
<p>
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.
</p>
</recommendation>
<example>
<p>
In the example given below, an untrusted HTTP parameter <code>code</code> is used as a Velocity template string.
This can lead to remote code execution.
</p>
<sample src="SSTIBad.java" />

<p>
In the next example, the problem is avoided by using a fixed template string <code>s</code>.
Since the template's code is not attacker-controlled in this case, this solution prevents the execution of untrusted code.
</p>
<sample src="SSTIGood.java" />
</example>
<references>
<li>Portswigger: <a href="https://portswigger.net/web-security/server-side-template-injection">Server Side Template Injection</a>.</li>
</references>
</qhelp>
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
/**
* @name Server Side Template Injection
* @description Untrusted input used as a template parameter can lead to remote code execution.
* @name Server-side template injection
* @description Untrusted input interpreted as a template can lead to remote code execution.
* @kind path-problem
* @problem.severity error
* @security-severity 9.3
* @precision high
* @id java/server-side-template-injection
* @tags security
* external/cwe/cwe-1336
* external/cwe/cwe-094
*/

import java
import TemplateInjection
import semmle.code.java.security.TemplateInjectionQuery
import DataFlow::PathGraph

from TemplateInjectionFlowConfig config, DataFlow::PathNode source, DataFlow::PathNode sink
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
category: newQuery
---
* The query "Server-side template injection" (`java/server-side-template-injection`) has been promoted from experimental to the main query pack. This query was originally [submitted as an experimental query by @porcupineyhairs](https://github.com/github/codeql/pull/5935).

This file was deleted.

Loading