mirror of
https://github.com/github/codeql.git
synced 2025-12-21 03:06:31 +01:00
SSRF query: add sanitizer looking for a variety of ways of prepending a sanitizing prefix, such as one that restricts the hostname a URI will refer to.
This commit is contained in:
@@ -25,6 +25,8 @@ class RequestForgeryConfiguration extends TaintTracking::Configuration {
|
||||
override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
requestForgeryStep(pred, succ)
|
||||
}
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) { node instanceof RequestForgerySanitizer }
|
||||
}
|
||||
|
||||
from DataFlow::PathNode source, DataFlow::PathNode sink, RequestForgeryConfiguration conf
|
||||
|
||||
@@ -5,6 +5,8 @@ import semmle.code.java.frameworks.spring.Spring
|
||||
import semmle.code.java.frameworks.JaxWS
|
||||
import semmle.code.java.frameworks.javase.Http
|
||||
import semmle.code.java.dataflow.DataFlow
|
||||
import semmle.code.java.dataflow.TaintTracking
|
||||
private import semmle.code.java.StringFormat
|
||||
|
||||
predicate requestForgeryStep(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
// propagate to a URI when its host is assigned to
|
||||
@@ -190,3 +192,83 @@ private class SpringRestTemplateUrlMethods extends Method {
|
||||
result = ma.getArgument(0)
|
||||
}
|
||||
}
|
||||
|
||||
/** A sanitizer for request forgery vulnerabilities. */
|
||||
abstract class RequestForgerySanitizer extends DataFlow::Node { }
|
||||
|
||||
private class HostnameSanitzingPrefix extends CompileTimeConstantExpr {
|
||||
int offset;
|
||||
|
||||
HostnameSanitzingPrefix() {
|
||||
exists(
|
||||
this.getStringValue().regexpFind(".*([?#]|[^?#:/\\\\][/\\\\]).*|[/\\\\][^/\\\\].*", 0, offset)
|
||||
)
|
||||
}
|
||||
|
||||
int getOffset() { result = offset }
|
||||
}
|
||||
|
||||
private AddExpr getParentAdd(AddExpr e) { result = e.getParent() }
|
||||
|
||||
private AddExpr getAnAddContainingHostnameSanitizingPrefix() {
|
||||
result = getParentAdd*(any(HostnameSanitzingPrefix p).getParent())
|
||||
}
|
||||
|
||||
private Expr getASanitizedAddOperand() {
|
||||
exists(AddExpr e |
|
||||
e = getAnAddContainingHostnameSanitizingPrefix() and
|
||||
(
|
||||
e.getLeftOperand() = getAnAddContainingHostnameSanitizingPrefix() or
|
||||
e.getLeftOperand() instanceof HostnameSanitzingPrefix
|
||||
) and
|
||||
result = e.getRightOperand()
|
||||
)
|
||||
}
|
||||
|
||||
private MethodAccess getNextAppend(MethodAccess append) {
|
||||
result = any(StringBuilderVar sbv).getNextAppend(append)
|
||||
}
|
||||
|
||||
class HostnameSanitizedExpr extends Expr {
|
||||
HostnameSanitizedExpr() {
|
||||
// Sanitize expressions that come after a sanitizing prefix in a tree of string additions:
|
||||
this = getASanitizedAddOperand()
|
||||
or
|
||||
// Sanitize expressions that come after a sanitizing prefix in a sequence of StringBuilder operations:
|
||||
exists(MethodAccess appendSanitizingConstant, MethodAccess subsequentAppend |
|
||||
appendSanitizingConstant.getArgument(0) instanceof HostnameSanitzingPrefix and
|
||||
getNextAppend*(appendSanitizingConstant) = subsequentAppend and
|
||||
this = subsequentAppend.getArgument(0)
|
||||
)
|
||||
or
|
||||
// Sanitize expressions that come after a sanitizing prefix in the args to a format call:
|
||||
exists(
|
||||
FormattingCall formatCall, FormatString formatString, HostnameSanitzingPrefix prefix,
|
||||
int sanitizedFromOffset, int laterOffset, int sanitizedArg
|
||||
|
|
||||
formatString = unique(FormatString fs | fs = formatCall.getAFormatString()) and
|
||||
(
|
||||
// An argument that sanitizes will be come before this:
|
||||
exists(int argIdx |
|
||||
formatCall.getArgumentToBeFormatted(argIdx) = prefix and
|
||||
sanitizedFromOffset = formatString.getAnArgUsageOffset(argIdx)
|
||||
)
|
||||
or
|
||||
// The format string itself sanitizes subsequent arguments:
|
||||
formatString = prefix.getStringValue() and
|
||||
sanitizedFromOffset = prefix.getOffset()
|
||||
) and
|
||||
laterOffset > sanitizedFromOffset and
|
||||
laterOffset = formatString.getAnArgUsageOffset(sanitizedArg) and
|
||||
this = formatCall.getArgumentToBeFormatted(sanitizedArg)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A value that is the result of prepending a string that prevents any value from controlling the
|
||||
* host of a URL.
|
||||
*/
|
||||
class HostnameSantizer extends RequestForgerySanitizer {
|
||||
HostnameSantizer() { this.asExpr() instanceof HostnameSanitizedExpr }
|
||||
}
|
||||
|
||||
@@ -175,6 +175,16 @@ class FormattingCall extends Call {
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the `i`th argument to be formatted. */
|
||||
Expr getArgumentToBeFormatted(int i) {
|
||||
i >= 0 and
|
||||
if this.hasExplicitVarargsArray()
|
||||
then
|
||||
result =
|
||||
this.getArgument(1 + this.getFormatStringIndex()).(ArrayCreationExpr).getInit().getInit(i)
|
||||
else result = this.getArgument(this.getFormatStringIndex() + 1 + i)
|
||||
}
|
||||
|
||||
/** Holds if the varargs argument is given as an explicit array. */
|
||||
private predicate hasExplicitVarargsArray() {
|
||||
this.getNumArgument() = this.getFormatStringIndex() + 2 and
|
||||
@@ -353,6 +363,11 @@ class FormatString extends string {
|
||||
* is not referred by any format specifier.
|
||||
*/
|
||||
/*abstract*/ int getASkippedFmtSpecIndex() { none() }
|
||||
|
||||
/**
|
||||
* Gets an offset in this format string where argument `argNo` will be interpolated, if any.
|
||||
*/
|
||||
int getAnArgUsageOffset(int argNo) { none() }
|
||||
}
|
||||
|
||||
private class PrintfFormatString extends FormatString {
|
||||
@@ -425,6 +440,16 @@ private class PrintfFormatString extends FormatString {
|
||||
result > count(int i | fmtSpecRefersToSequentialIndex(i)) and
|
||||
not result = fmtSpecRefersToSpecificIndex(_)
|
||||
}
|
||||
|
||||
override int getAnArgUsageOffset(int argNo) {
|
||||
argNo = fmtSpecRefersToSpecificIndex(result)
|
||||
or
|
||||
fmtSpecRefersToSequentialIndex(result) and
|
||||
argNo = count(int i | i < result and fmtSpecRefersToSequentialIndex(i))
|
||||
or
|
||||
fmtSpecRefersToPrevious(result) and
|
||||
argNo = count(int i | i < result and fmtSpecRefersToSequentialIndex(i)) - 1
|
||||
}
|
||||
}
|
||||
|
||||
private class LoggerFormatString extends FormatString {
|
||||
@@ -449,4 +474,9 @@ private class LoggerFormatString extends FormatString {
|
||||
}
|
||||
|
||||
override int getMaxFmtSpecIndex() { result = count(int i | fmtPlaceholder(i)) }
|
||||
|
||||
override int getAnArgUsageOffset(int argNo) {
|
||||
fmtPlaceholder(result) and
|
||||
argNo = count(int i | fmtPlaceholder(i) and i < result)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user