mirror of
https://github.com/github/codeql.git
synced 2026-05-02 04:05:14 +02:00
Merge pull request #4676 from MathiasVP/untrusted-dataflow-to-external-api-query
C++: Untrusted data used in external APIs
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>Using unsanitized untrusted data in an external API can cause a variety of security issues. This query reports
|
||||
all external APIs that are used with untrusted data, along with how frequently the API is used, and how many
|
||||
unique sources of untrusted data flow to this API. This query is designed primarily to help identify which APIs
|
||||
may be relevant for security analysis of this application.</p>
|
||||
|
||||
<p>An external API is defined as a call to a function that is not defined in the source code, and is not
|
||||
modeled as a taint step in the default taint library. External APIs may be from the C++ standard library,
|
||||
third party dependencies or from internal dependencies. The query will report the function name, along with
|
||||
either <code>[param x]</code>, where <code>x</code> indicates the position of the parameter receiving the
|
||||
untrusted data or <code>[qualifier]</code> indicating the untrusted data is used as the qualifier to the
|
||||
function call.</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
|
||||
<p>For each result:</p>
|
||||
|
||||
<ul>
|
||||
<li>If the result highlights a known sink, no action is required.</li>
|
||||
<li>If the result highlights an unknown sink for a problem, then add modeling for the sink to the relevant query.</li>
|
||||
<li>If the result represents a call to an external API which transfers taint, add the appropriate modeling, and
|
||||
re-run the query to determine what new results have appeared due to this additional modeling.</li>
|
||||
</ul>
|
||||
|
||||
<p>Otherwise, the result is likely uninteresting. Custom versions of this query can extend the <code>SafeExternalAPIFunction</code>
|
||||
class to exclude known safe external APIs from future analysis.</p>
|
||||
|
||||
</recommendation>
|
||||
<example>
|
||||
|
||||
<p>If the query were to return the API <code>fputs [param 1]</code>
|
||||
then we should first consider whether this a security relevant sink. In this case, this is writing to a <code>FILE*</code>, so we should
|
||||
consider whether this is an XSS sink. If it is, we should confirm that it is handled by the XSS query.</p>
|
||||
|
||||
<p>If the query were to return the API <code>strcat [param 1]</code>, then this should be
|
||||
reviewed as a possible taint step, because tainted data would flow from the 1st argument to the 0th argument of the call.</p>
|
||||
|
||||
<p>Note that both examples are correctly handled by the standard taint tracking library and XSS query.</p>
|
||||
</example>
|
||||
<references>
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* @name Frequency counts for external APIs that are used with untrusted data
|
||||
* @description This reports the external APIs that are used with untrusted data, along with how
|
||||
* frequently the API is called, and how many unique sources of untrusted data flow
|
||||
* to it.
|
||||
* @id cpp/count-untrusted-data-external-api
|
||||
* @kind table
|
||||
* @tags security external/cwe/cwe-20
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import ExternalAPIs
|
||||
|
||||
from ExternalAPIUsedWithUntrustedData externalAPI
|
||||
select externalAPI, count(externalAPI.getUntrustedDataNode()) as numberOfUses,
|
||||
externalAPI.getNumberOfUntrustedSources() as numberOfUntrustedSources order by
|
||||
numberOfUntrustedSources desc
|
||||
13
cpp/ql/src/Security/CWE/CWE-020/ExternalAPISinkExample.cpp
Normal file
13
cpp/ql/src/Security/CWE/CWE-020/ExternalAPISinkExample.cpp
Normal file
@@ -0,0 +1,13 @@
|
||||
#include <cstdio>
|
||||
|
||||
void do_get(FILE* request, FILE* response) {
|
||||
char page[1024];
|
||||
fgets(page, 1024, request);
|
||||
|
||||
char buffer[1024];
|
||||
strcat(buffer, "The page \"");
|
||||
strcat(buffer, page);
|
||||
strcat(buffer, "\" was not found.");
|
||||
|
||||
fputs(buffer, response);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
#include <cstdio>
|
||||
|
||||
void do_get(FILE* request, FILE* response) {
|
||||
char user_id[1024];
|
||||
fgets(user_id, 1024, request);
|
||||
|
||||
char buffer[1024];
|
||||
strcat(buffer, "SELECT * FROM user WHERE user_id='");
|
||||
strcat(buffer, user_id);
|
||||
strcat(buffer, "'");
|
||||
|
||||
// ...
|
||||
}
|
||||
50
cpp/ql/src/Security/CWE/CWE-020/ExternalAPIs.qll
Normal file
50
cpp/ql/src/Security/CWE/CWE-020/ExternalAPIs.qll
Normal file
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* Definitions for reasoning about untrusted data used in APIs defined outside the
|
||||
* database.
|
||||
*/
|
||||
|
||||
private import cpp
|
||||
private import semmle.code.cpp.models.interfaces.DataFlow
|
||||
private import semmle.code.cpp.models.interfaces.Taint
|
||||
import ExternalAPIsSpecific
|
||||
|
||||
/** A node representing untrusted data being passed to an external API. */
|
||||
class UntrustedExternalAPIDataNode extends ExternalAPIDataNode {
|
||||
UntrustedExternalAPIDataNode() { any(UntrustedDataToExternalAPIConfig c).hasFlow(_, this) }
|
||||
|
||||
/** Gets a source of untrusted data which is passed to this external API data node. */
|
||||
DataFlow::Node getAnUntrustedSource() {
|
||||
any(UntrustedDataToExternalAPIConfig c).hasFlow(result, this)
|
||||
}
|
||||
}
|
||||
|
||||
private newtype TExternalAPI =
|
||||
TExternalAPIParameter(Function f, int index) {
|
||||
exists(UntrustedExternalAPIDataNode n |
|
||||
f = n.getExternalFunction() and
|
||||
index = n.getIndex()
|
||||
)
|
||||
}
|
||||
|
||||
/** An external API which is used with untrusted data. */
|
||||
class ExternalAPIUsedWithUntrustedData extends TExternalAPI {
|
||||
/** Gets a possibly untrusted use of this external API. */
|
||||
UntrustedExternalAPIDataNode getUntrustedDataNode() {
|
||||
this = TExternalAPIParameter(result.getExternalFunction(), result.getIndex())
|
||||
}
|
||||
|
||||
/** Gets the number of untrusted sources used with this external API. */
|
||||
int getNumberOfUntrustedSources() {
|
||||
result = strictcount(getUntrustedDataNode().getAnUntrustedSource())
|
||||
}
|
||||
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() {
|
||||
exists(Function f, int index, string indexString |
|
||||
if index = -1 then indexString = "qualifier" else indexString = "param " + index
|
||||
|
|
||||
this = TExternalAPIParameter(f, index) and
|
||||
result = f.toString() + " [" + indexString + "]"
|
||||
)
|
||||
}
|
||||
}
|
||||
56
cpp/ql/src/Security/CWE/CWE-020/ExternalAPIsSpecific.qll
Normal file
56
cpp/ql/src/Security/CWE/CWE-020/ExternalAPIsSpecific.qll
Normal file
@@ -0,0 +1,56 @@
|
||||
/**
|
||||
* Provides AST-specific definitions for use in the `ExternalAPI` library.
|
||||
*/
|
||||
|
||||
import semmle.code.cpp.dataflow.TaintTracking
|
||||
import semmle.code.cpp.models.interfaces.FlowSource
|
||||
import semmle.code.cpp.models.interfaces.DataFlow
|
||||
import SafeExternalAPIFunction
|
||||
|
||||
/** A node representing untrusted data being passed to an external API. */
|
||||
class ExternalAPIDataNode extends DataFlow::Node {
|
||||
Call call;
|
||||
int i;
|
||||
|
||||
ExternalAPIDataNode() {
|
||||
// Argument to call to a function
|
||||
(
|
||||
this.asExpr() = call.getArgument(i)
|
||||
or
|
||||
i = -1 and this.asExpr() = call.getQualifier()
|
||||
) and
|
||||
exists(Function f |
|
||||
f = call.getTarget() and
|
||||
// Defined outside the source archive
|
||||
not f.hasDefinition() and
|
||||
// Not already modeled as a dataflow or taint step
|
||||
not f instanceof DataFlowFunction and
|
||||
not f instanceof TaintFunction and
|
||||
// Not a call to a known safe external API
|
||||
not f instanceof SafeExternalAPIFunction
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the called API `Function`. */
|
||||
Function getExternalFunction() { result = call.getTarget() }
|
||||
|
||||
/** Gets the index which is passed untrusted data (where -1 indicates the qualifier). */
|
||||
int getIndex() { result = i }
|
||||
|
||||
/** Gets the description of the function being called. */
|
||||
string getFunctionDescription() { result = getExternalFunction().toString() }
|
||||
}
|
||||
|
||||
/** A configuration for tracking flow from `RemoteFlowSource`s to `ExternalAPIDataNode`s. */
|
||||
class UntrustedDataToExternalAPIConfig extends TaintTracking::Configuration {
|
||||
UntrustedDataToExternalAPIConfig() { this = "UntrustedDataToExternalAPIConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
exists(RemoteFlowFunction remoteFlow |
|
||||
remoteFlow = source.asExpr().(Call).getTarget() and
|
||||
remoteFlow.hasRemoteFlowSource(_, _)
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof ExternalAPIDataNode }
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>Using unsanitized untrusted data in an external API can cause a variety of security issues. This query reports
|
||||
all external APIs that are used with untrusted data, along with how frequently the API is used, and how many
|
||||
unique sources of untrusted data flow to this API. This query is designed primarily to help identify which APIs
|
||||
may be relevant for security analysis of this application.</p>
|
||||
|
||||
<p>An external API is defined as a call to a function that is not defined in the source code, and is not
|
||||
modeled as a taint step in the default taint library. External APIs may be from the C++ standard library,
|
||||
third party dependencies or from internal dependencies. The query will report the function name, along with
|
||||
either <code>[param x]</code>, where <code>x</code> indicates the position of the parameter receiving the
|
||||
untrusted data or <code>[qualifier]</code> indicating the untrusted data is used as the qualifier to the
|
||||
function call.</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
|
||||
<p>For each result:</p>
|
||||
|
||||
<ul>
|
||||
<li>If the result highlights a known sink, no action is required.</li>
|
||||
<li>If the result highlights an unknown sink for a problem, then add modeling for the sink to the relevant query.</li>
|
||||
<li>If the result represents a call to an external API which transfers taint, add the appropriate modeling, and
|
||||
re-run the query to determine what new results have appeared due to this additional modeling.</li>
|
||||
</ul>
|
||||
|
||||
<p>Otherwise, the result is likely uninteresting. Custom versions of this query can extend the <code>SafeExternalAPIFunction</code>
|
||||
class to exclude known safe external APIs from future analysis.</p>
|
||||
|
||||
</recommendation>
|
||||
<example>
|
||||
|
||||
<p>If the query were to return the API <code>fputs [param 1]</code>
|
||||
then we should first consider whether this a security relevant sink. In this case, this is writing to a <code>FILE*</code>, so we should
|
||||
consider whether this is an XSS sink. If it is, we should confirm that it is handled by the XSS query.</p>
|
||||
|
||||
<p>If the query were to return the API <code>strcat [param 1]</code>, then this should be
|
||||
reviewed as a possible taint step, because tainted data would flow from the 1st argument to the 0th argument of the call.</p>
|
||||
|
||||
<p>Note that both examples are correctly handled by the standard taint tracking library and XSS query.</p>
|
||||
</example>
|
||||
<references>
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* @name Frequency counts for external APIs that are used with untrusted data
|
||||
* @description This reports the external APIs that are used with untrusted data, along with how
|
||||
* frequently the API is called, and how many unique sources of untrusted data flow
|
||||
* to it.
|
||||
* @id cpp/count-untrusted-data-external-api-ir
|
||||
* @kind table
|
||||
* @tags security external/cwe/cwe-20
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import ir.ExternalAPIs
|
||||
|
||||
from ExternalAPIUsedWithUntrustedData externalAPI
|
||||
select externalAPI, count(externalAPI.getUntrustedDataNode()) as numberOfUses,
|
||||
externalAPI.getNumberOfUntrustedSources() as numberOfUntrustedSources order by
|
||||
numberOfUntrustedSources desc
|
||||
@@ -0,0 +1,59 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>Using unsanitized untrusted data in an external API can cause a variety of security issues. This query reports
|
||||
external APIs that use untrusted data. The results are not filtered, so you can audit all examples.
|
||||
The query provides data for security reviews of the application and you can also use it to identify external APIs
|
||||
that should be modeled as either taint steps, or sinks for specific problems.</p>
|
||||
|
||||
<p>An external API is defined as a call to a function that is not defined in the source code, and is not modeled
|
||||
as a taint step in the default taint library. External APIs may be from the
|
||||
C++ standard library, third-party dependencies or from internal dependencies. The query reports uses of
|
||||
untrusted data in either the qualifier or as one of the arguments of external APIs.</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
|
||||
<p>For each result:</p>
|
||||
|
||||
<ul>
|
||||
<li>If the result highlights a known sink, confirm that the result is reported by the relevant query, or
|
||||
that the result is a false positive because this data is sanitized.</li>
|
||||
<li>If the result highlights an unknown sink for a problem, then add modeling for the sink to the relevant query,
|
||||
and confirm that the result is either found, or is safe due to appropriate sanitization.</li>
|
||||
<li>If the result represents a call to an external API that transfers taint, add the appropriate modeling, and
|
||||
re-run the query to determine what new results have appeared due to this additional modeling.</li>
|
||||
</ul>
|
||||
|
||||
<p>Otherwise, the result is likely uninteresting. Custom versions of this query can extend the <code>SafeExternalAPIFunction</code>
|
||||
class to exclude known safe external APIs from future analysis.</p>
|
||||
|
||||
</recommendation>
|
||||
<example>
|
||||
|
||||
<p>In this first example, input is read from <code>fgets</code> and then ultimately used in a call to the
|
||||
<code>fputs</code> external API:</p>
|
||||
|
||||
<sample src="ExternalAPISinkExample.cpp" />
|
||||
|
||||
<p>This is an XSS sink. The XSS query should therefore be reviewed to confirm that this sink is appropriately modeled,
|
||||
and if it is, to confirm that the query reports this particular result, or that the result is a false positive due to
|
||||
some existing sanitization.</p>
|
||||
|
||||
<p>In this second example, again a request parameter is read from <code>fgets</code>.</p>
|
||||
|
||||
<sample src="ExternalAPITaintStepExample.cpp" />
|
||||
|
||||
<p>If the query reported the call to <code>strcat</code> on line 9, this would suggest that this external API is
|
||||
not currently modeled as a taint step in the taint tracking library. The next step would be to model this as a taint step, then
|
||||
re-run the query to determine what additional results might be found. In this example, it seems likely that <code>buffer</code>
|
||||
will be executed as an SQL query, potentially leading to an SQL injection vulnerability.</p>
|
||||
|
||||
<p>Note that both examples are correctly handled by the standard taint tracking library and XSS query.</p>
|
||||
</example>
|
||||
<references>
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* @name Untrusted data passed to external API
|
||||
* @description Data provided remotely is used in this external API without sanitization, which could be a security risk.
|
||||
* @id cpp/untrusted-data-to-external-api-ir
|
||||
* @kind path-problem
|
||||
* @precision low
|
||||
* @problem.severity error
|
||||
* @tags security external/cwe/cwe-20
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.ir.dataflow.TaintTracking
|
||||
import ir.ExternalAPIs
|
||||
import semmle.code.cpp.security.FlowSources
|
||||
import DataFlow::PathGraph
|
||||
|
||||
from UntrustedDataToExternalAPIConfig config, DataFlow::PathNode source, DataFlow::PathNode sink
|
||||
where config.hasFlowPath(source, sink)
|
||||
select sink, source, sink,
|
||||
"Call to " + sink.getNode().(ExternalAPIDataNode).getExternalFunction().toString() +
|
||||
" with untrusted data from $@.", source, source.getNode().(RemoteFlowSource).getSourceType()
|
||||
20
cpp/ql/src/Security/CWE/CWE-020/SafeExternalAPIFunction.qll
Normal file
20
cpp/ql/src/Security/CWE/CWE-020/SafeExternalAPIFunction.qll
Normal file
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* Provides a class for modeling external functions that are "safe" from a security perspective.
|
||||
*/
|
||||
|
||||
private import cpp
|
||||
private import semmle.code.cpp.models.implementations.Pure
|
||||
|
||||
/**
|
||||
* A `Function` that is considered a "safe" external API from a security perspective.
|
||||
*/
|
||||
abstract class SafeExternalAPIFunction extends Function { }
|
||||
|
||||
/** The default set of "safe" external APIs. */
|
||||
private class DefaultSafeExternalAPIFunction extends SafeExternalAPIFunction {
|
||||
DefaultSafeExternalAPIFunction() {
|
||||
this instanceof PureStrFunction or
|
||||
this instanceof StrLenFunction or
|
||||
this instanceof PureMemFunction
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>Using unsanitized untrusted data in an external API can cause a variety of security issues. This query reports
|
||||
external APIs that use untrusted data. The results are not filtered, so you can audit all examples.
|
||||
The query provides data for security reviews of the application and you can also use it to identify external APIs
|
||||
that should be modeled as either taint steps, or sinks for specific problems.</p>
|
||||
|
||||
<p>An external API is defined as a call to a function that is not defined in the source code, and is not modeled
|
||||
as a taint step in the default taint library. External APIs may be from the
|
||||
C++ standard library, third-party dependencies or from internal dependencies. The query reports uses of
|
||||
untrusted data in either the qualifier or as one of the arguments of external APIs.</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
|
||||
<p>For each result:</p>
|
||||
|
||||
<ul>
|
||||
<li>If the result highlights a known sink, confirm that the result is reported by the relevant query, or
|
||||
that the result is a false positive because this data is sanitized.</li>
|
||||
<li>If the result highlights an unknown sink for a problem, then add modeling for the sink to the relevant query,
|
||||
and confirm that the result is either found, or is safe due to appropriate sanitization.</li>
|
||||
<li>If the result represents a call to an external API that transfers taint, add the appropriate modeling, and
|
||||
re-run the query to determine what new results have appeared due to this additional modeling.</li>
|
||||
</ul>
|
||||
|
||||
<p>Otherwise, the result is likely uninteresting. Custom versions of this query can extend the <code>SafeExternalAPIFunction</code>
|
||||
class to exclude known safe external APIs from future analysis.</p>
|
||||
|
||||
</recommendation>
|
||||
<example>
|
||||
|
||||
<p>In this first example, input is read from <code>fgets</code> and then ultimately used in a call to the
|
||||
<code>fputs</code> external API:</p>
|
||||
|
||||
<sample src="ExternalAPISinkExample.cpp" />
|
||||
|
||||
<p>This is an XSS sink. The XSS query should therefore be reviewed to confirm that this sink is appropriately modeled,
|
||||
and if it is, to confirm that the query reports this particular result, or that the result is a false positive due to
|
||||
some existing sanitization.</p>
|
||||
|
||||
<p>In this second example, again a request parameter is read from <code>fgets</code>.</p>
|
||||
|
||||
<sample src="ExternalAPITaintStepExample.cpp" />
|
||||
|
||||
<p>If the query reported the call to <code>strcat</code> on line 9, this would suggest that this external API is
|
||||
not currently modeled as a taint step in the taint tracking library. The next step would be to model this as a taint step, then
|
||||
re-run the query to determine what additional results might be found. In this example, it seems likely that <code>buffer</code>
|
||||
will be executed as an SQL query, potentially leading to an SQL injection vulnerability.</p>
|
||||
|
||||
<p>Note that both examples are correctly handled by the standard taint tracking library and XSS query.</p>
|
||||
</example>
|
||||
<references>
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* @name Untrusted data passed to external API
|
||||
* @description Data provided remotely is used in this external API without sanitization, which could be a security risk.
|
||||
* @id cpp/untrusted-data-to-external-api
|
||||
* @kind path-problem
|
||||
* @precision low
|
||||
* @problem.severity error
|
||||
* @tags security external/cwe/cwe-20
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.dataflow.TaintTracking
|
||||
import ExternalAPIs
|
||||
import DataFlow::PathGraph
|
||||
|
||||
from UntrustedDataToExternalAPIConfig config, DataFlow::PathNode source, DataFlow::PathNode sink
|
||||
where config.hasFlowPath(source, sink)
|
||||
select sink, source, sink,
|
||||
"Call to " + sink.getNode().(ExternalAPIDataNode).getExternalFunction().toString() +
|
||||
" with untrusted data from $@.", source, source.toString()
|
||||
50
cpp/ql/src/Security/CWE/CWE-020/ir/ExternalAPIs.qll
Normal file
50
cpp/ql/src/Security/CWE/CWE-020/ir/ExternalAPIs.qll
Normal file
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* Definitions for reasoning about untrusted data used in APIs defined outside the
|
||||
* database.
|
||||
*/
|
||||
|
||||
private import cpp
|
||||
private import semmle.code.cpp.models.interfaces.DataFlow
|
||||
private import semmle.code.cpp.models.interfaces.Taint
|
||||
import ExternalAPIsSpecific
|
||||
|
||||
/** A node representing untrusted data being passed to an external API. */
|
||||
class UntrustedExternalAPIDataNode extends ExternalAPIDataNode {
|
||||
UntrustedExternalAPIDataNode() { any(UntrustedDataToExternalAPIConfig c).hasFlow(_, this) }
|
||||
|
||||
/** Gets a source of untrusted data which is passed to this external API data node. */
|
||||
DataFlow::Node getAnUntrustedSource() {
|
||||
any(UntrustedDataToExternalAPIConfig c).hasFlow(result, this)
|
||||
}
|
||||
}
|
||||
|
||||
private newtype TExternalAPI =
|
||||
TExternalAPIParameter(Function f, int index) {
|
||||
exists(UntrustedExternalAPIDataNode n |
|
||||
f = n.getExternalFunction() and
|
||||
index = n.getIndex()
|
||||
)
|
||||
}
|
||||
|
||||
/** An external API which is used with untrusted data. */
|
||||
class ExternalAPIUsedWithUntrustedData extends TExternalAPI {
|
||||
/** Gets a possibly untrusted use of this external API. */
|
||||
UntrustedExternalAPIDataNode getUntrustedDataNode() {
|
||||
this = TExternalAPIParameter(result.getExternalFunction(), result.getIndex())
|
||||
}
|
||||
|
||||
/** Gets the number of untrusted sources used with this external API. */
|
||||
int getNumberOfUntrustedSources() {
|
||||
result = strictcount(getUntrustedDataNode().getAnUntrustedSource())
|
||||
}
|
||||
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() {
|
||||
exists(Function f, int index, string indexString |
|
||||
if index = -1 then indexString = "qualifier" else indexString = "param " + index
|
||||
|
|
||||
this = TExternalAPIParameter(f, index) and
|
||||
result = f.toString() + " [" + indexString + "]"
|
||||
)
|
||||
}
|
||||
}
|
||||
51
cpp/ql/src/Security/CWE/CWE-020/ir/ExternalAPIsSpecific.qll
Normal file
51
cpp/ql/src/Security/CWE/CWE-020/ir/ExternalAPIsSpecific.qll
Normal file
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* Provides IR-specific definitions for use in the `ExternalAPI` library.
|
||||
*/
|
||||
|
||||
import semmle.code.cpp.ir.dataflow.TaintTracking
|
||||
private import semmle.code.cpp.security.FlowSources
|
||||
private import semmle.code.cpp.models.interfaces.DataFlow
|
||||
import SafeExternalAPIFunction
|
||||
|
||||
/** A node representing untrusted data being passed to an external API. */
|
||||
class ExternalAPIDataNode extends DataFlow::Node {
|
||||
Call call;
|
||||
int i;
|
||||
|
||||
ExternalAPIDataNode() {
|
||||
// Argument to call to a function
|
||||
(
|
||||
this.asExpr() = call.getArgument(i)
|
||||
or
|
||||
i = -1 and this.asExpr() = call.getQualifier()
|
||||
) and
|
||||
exists(Function f |
|
||||
f = call.getTarget() and
|
||||
// Defined outside the source archive
|
||||
not f.hasDefinition() and
|
||||
// Not already modeled as a dataflow or taint step
|
||||
not f instanceof DataFlowFunction and
|
||||
not f instanceof TaintFunction and
|
||||
// Not a call to a known safe external API
|
||||
not f instanceof SafeExternalAPIFunction
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the called API `Function`. */
|
||||
Function getExternalFunction() { result = call.getTarget() }
|
||||
|
||||
/** Gets the index which is passed untrusted data (where -1 indicates the qualifier). */
|
||||
int getIndex() { result = i }
|
||||
|
||||
/** Gets the description of the function being called. */
|
||||
string getFunctionDescription() { result = getExternalFunction().toString() }
|
||||
}
|
||||
|
||||
/** A configuration for tracking flow from `RemoteFlowSource`s to `ExternalAPIDataNode`s. */
|
||||
class UntrustedDataToExternalAPIConfig extends TaintTracking::Configuration {
|
||||
UntrustedDataToExternalAPIConfig() { this = "UntrustedDataToExternalAPIConfigIR" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof ExternalAPIDataNode }
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* Provides a class for modeling external functions that are "safe" from a security perspective.
|
||||
*/
|
||||
|
||||
private import cpp
|
||||
private import semmle.code.cpp.models.implementations.Pure
|
||||
|
||||
/**
|
||||
* A `Function` that is considered a "safe" external API from a security perspective.
|
||||
*/
|
||||
abstract class SafeExternalAPIFunction extends Function { }
|
||||
|
||||
/** The default set of "safe" external APIs. */
|
||||
private class DefaultSafeExternalAPIFunction extends SafeExternalAPIFunction {
|
||||
DefaultSafeExternalAPIFunction() {
|
||||
this instanceof PureStrFunction or
|
||||
this instanceof StrLenFunction or
|
||||
this instanceof PureMemFunction
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
private import implementations.Allocation
|
||||
private import implementations.Deallocation
|
||||
private import implementations.Fread
|
||||
private import implementations.Getenv
|
||||
private import implementations.Gets
|
||||
private import implementations.IdentityFunction
|
||||
private import implementations.Inet
|
||||
|
||||
21
cpp/ql/src/semmle/code/cpp/models/implementations/Getenv.qll
Normal file
21
cpp/ql/src/semmle/code/cpp/models/implementations/Getenv.qll
Normal file
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* Provides an implementation class modeling the POSIX function `getenv`.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.models.interfaces.FlowSource
|
||||
|
||||
/**
|
||||
* The POSIX function `getenv`.
|
||||
*/
|
||||
class Getenv extends LocalFlowFunction {
|
||||
Getenv() { this.hasGlobalName("getenv") }
|
||||
|
||||
override predicate hasLocalFlowSource(FunctionOutput output, string description) {
|
||||
(
|
||||
output.isReturnValueDeref() or
|
||||
output.isReturnValue()
|
||||
) and
|
||||
description = "an environment variable"
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ import semmle.code.cpp.models.interfaces.Taint
|
||||
import semmle.code.cpp.models.interfaces.Alias
|
||||
import semmle.code.cpp.models.interfaces.SideEffect
|
||||
|
||||
/** Pure string functions. */
|
||||
class PureStrFunction extends AliasFunction, ArrayFunction, TaintFunction, SideEffectFunction {
|
||||
PureStrFunction() {
|
||||
hasGlobalOrStdName([
|
||||
@@ -58,6 +59,7 @@ class PureStrFunction extends AliasFunction, ArrayFunction, TaintFunction, SideE
|
||||
}
|
||||
}
|
||||
|
||||
/** String standard `strlen` function, and related functions for computing string lengths. */
|
||||
class StrLenFunction extends AliasFunction, ArrayFunction, SideEffectFunction {
|
||||
StrLenFunction() {
|
||||
hasGlobalOrStdName(["strlen", "strnlen", "wcslen"])
|
||||
@@ -91,6 +93,7 @@ class StrLenFunction extends AliasFunction, ArrayFunction, SideEffectFunction {
|
||||
}
|
||||
}
|
||||
|
||||
/** Pure functions. */
|
||||
class PureFunction extends TaintFunction, SideEffectFunction {
|
||||
PureFunction() { hasGlobalOrStdName(["abs", "labs"]) }
|
||||
|
||||
@@ -106,3 +109,49 @@ class PureFunction extends TaintFunction, SideEffectFunction {
|
||||
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
}
|
||||
|
||||
/** Pure raw-memory functions. */
|
||||
class PureMemFunction extends AliasFunction, ArrayFunction, TaintFunction, SideEffectFunction {
|
||||
PureMemFunction() { hasGlobalOrStdName(["memchr", "memrchr", "rawmemchr", "memcmp", "memmem"]) }
|
||||
|
||||
override predicate hasArrayInput(int bufParam) {
|
||||
getParameter(bufParam).getUnspecifiedType() instanceof PointerType
|
||||
}
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
exists(ParameterIndex i |
|
||||
input.isParameter(i) and
|
||||
exists(getParameter(i))
|
||||
or
|
||||
input.isParameterDeref(i) and
|
||||
getParameter(i).getUnspecifiedType() instanceof PointerType
|
||||
) and
|
||||
(
|
||||
output.isReturnValueDeref() and
|
||||
getUnspecifiedType() instanceof PointerType
|
||||
or
|
||||
output.isReturnValue()
|
||||
)
|
||||
}
|
||||
|
||||
override predicate parameterNeverEscapes(int i) {
|
||||
getParameter(i).getUnspecifiedType() instanceof PointerType and
|
||||
not parameterEscapesOnlyViaReturn(i)
|
||||
}
|
||||
|
||||
override predicate parameterEscapesOnlyViaReturn(int i) {
|
||||
i = 0 and
|
||||
getUnspecifiedType() instanceof PointerType
|
||||
}
|
||||
|
||||
override predicate parameterIsAlwaysReturned(int i) { none() }
|
||||
|
||||
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
|
||||
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
|
||||
getParameter(i).getUnspecifiedType() instanceof PointerType and
|
||||
buffer = true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ import FunctionInputsAndOutputs
|
||||
import semmle.code.cpp.models.Models
|
||||
|
||||
/**
|
||||
* A library function which returns data read from a network connection.
|
||||
* A library function that returns data that may be read from a network connection.
|
||||
*/
|
||||
abstract class RemoteFlowFunction extends Function {
|
||||
/**
|
||||
@@ -19,3 +19,13 @@ abstract class RemoteFlowFunction extends Function {
|
||||
*/
|
||||
abstract predicate hasRemoteFlowSource(FunctionOutput output, string description);
|
||||
}
|
||||
|
||||
/**
|
||||
* A library function that returns data that is directly controlled by a user.
|
||||
*/
|
||||
abstract class LocalFlowFunction extends Function {
|
||||
/**
|
||||
* Holds if data described by `description` flows from `output` of a call to this function.
|
||||
*/
|
||||
abstract predicate hasLocalFlowSource(FunctionOutput output, string description);
|
||||
}
|
||||
|
||||
@@ -132,6 +132,16 @@ class FunctionInput extends TFunctionInput {
|
||||
* part of itself, or one of its other inputs.
|
||||
*/
|
||||
predicate isReturnValueDeref() { none() }
|
||||
|
||||
/**
|
||||
* Holds if `i >= 0` and `isParameterDeref(i)` holds for this is value, or
|
||||
* if `i = -1` and `isQualifierObject()` holds for this value.
|
||||
*/
|
||||
final predicate isParameterDerefOrQualifierObject(ParameterIndex i) {
|
||||
i >= 0 and this.isParameterDeref(i)
|
||||
or
|
||||
i = -1 and this.isQualifierObject()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -370,6 +380,16 @@ class FunctionOutput extends TFunctionOutput {
|
||||
* DEPRECATED: Use `isReturnValueDeref()` instead.
|
||||
*/
|
||||
deprecated final predicate isOutReturnPointer() { isReturnValueDeref() }
|
||||
|
||||
/**
|
||||
* Holds if `i >= 0` and `isParameterDeref(i)` holds for this is the value, or
|
||||
* if `i = -1` and `isQualifierObject()` holds for this value.
|
||||
*/
|
||||
final predicate isParameterDerefOrQualifierObject(ParameterIndex i) {
|
||||
i >= 0 and this.isParameterDeref(i)
|
||||
or
|
||||
i = -1 and this.isQualifierObject()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -7,38 +7,94 @@ import semmle.code.cpp.ir.dataflow.DataFlow
|
||||
private import semmle.code.cpp.ir.IR
|
||||
import semmle.code.cpp.models.interfaces.FlowSource
|
||||
|
||||
/** A data flow source of remote user input. */
|
||||
abstract class RemoteFlowSource extends DataFlow::Node {
|
||||
/** Gets a string that describes the type of this remote flow source. */
|
||||
/** A data flow source of user input, whether local or remote. */
|
||||
abstract class FlowSource extends DataFlow::Node {
|
||||
/** Gets a string that describes the type of this flow source. */
|
||||
abstract string getSourceType();
|
||||
}
|
||||
|
||||
private class TaintedReturnSource extends RemoteFlowSource {
|
||||
/** A data flow source of remote user input. */
|
||||
abstract class RemoteFlowSource extends FlowSource { }
|
||||
|
||||
/** A data flow source of local user input. */
|
||||
abstract class LocalFlowSource extends FlowSource { }
|
||||
|
||||
private class RemoteReturnSource extends RemoteFlowSource {
|
||||
string sourceType;
|
||||
|
||||
TaintedReturnSource() {
|
||||
RemoteReturnSource() {
|
||||
exists(RemoteFlowFunction func, CallInstruction instr, FunctionOutput output |
|
||||
asInstruction() = instr and
|
||||
instr.getStaticCallTarget() = func and
|
||||
func.hasRemoteFlowSource(output, sourceType) and
|
||||
output.isReturnValue()
|
||||
(
|
||||
output.isReturnValue()
|
||||
or
|
||||
output.isReturnValueDeref()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override string getSourceType() { result = sourceType }
|
||||
}
|
||||
|
||||
private class TaintedParameterSource extends RemoteFlowSource {
|
||||
private class RemoteParameterSource extends RemoteFlowSource {
|
||||
string sourceType;
|
||||
|
||||
TaintedParameterSource() {
|
||||
RemoteParameterSource() {
|
||||
exists(RemoteFlowFunction func, WriteSideEffectInstruction instr, FunctionOutput output |
|
||||
asInstruction() = instr and
|
||||
instr.getPrimaryInstruction().(CallInstruction).getStaticCallTarget() = func and
|
||||
func.hasRemoteFlowSource(output, sourceType) and
|
||||
output.isParameterDeref(instr.getIndex())
|
||||
output.isParameterDerefOrQualifierObject(instr.getIndex())
|
||||
)
|
||||
}
|
||||
|
||||
override string getSourceType() { result = sourceType }
|
||||
}
|
||||
|
||||
private class LocalReturnSource extends LocalFlowSource {
|
||||
string sourceType;
|
||||
|
||||
LocalReturnSource() {
|
||||
exists(LocalFlowFunction func, CallInstruction instr, FunctionOutput output |
|
||||
asInstruction() = instr and
|
||||
instr.getStaticCallTarget() = func and
|
||||
func.hasLocalFlowSource(output, sourceType) and
|
||||
(
|
||||
output.isReturnValue()
|
||||
or
|
||||
output.isReturnValueDeref()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override string getSourceType() { result = sourceType }
|
||||
}
|
||||
|
||||
private class LocalParameterSource extends LocalFlowSource {
|
||||
string sourceType;
|
||||
|
||||
LocalParameterSource() {
|
||||
exists(LocalFlowFunction func, WriteSideEffectInstruction instr, FunctionOutput output |
|
||||
asInstruction() = instr and
|
||||
instr.getPrimaryInstruction().(CallInstruction).getStaticCallTarget() = func and
|
||||
func.hasLocalFlowSource(output, sourceType) and
|
||||
output.isParameterDerefOrQualifierObject(instr.getIndex())
|
||||
)
|
||||
}
|
||||
|
||||
override string getSourceType() { result = sourceType }
|
||||
}
|
||||
|
||||
private class ArgvSource extends LocalFlowSource {
|
||||
ArgvSource() {
|
||||
exists(Parameter argv |
|
||||
argv.hasName("argv") and
|
||||
argv.getFunction().hasGlobalName("main") and
|
||||
this.asExpr() = argv.getAnAccess()
|
||||
)
|
||||
}
|
||||
|
||||
override string getSourceType() { result = "a command-line argument" }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user