mirror of
https://github.com/hohn/codeql-c-sqli.git
synced 2025-12-16 18:33:04 +01:00
workshop discussion source
This commit is contained in:
committed by
=Michael Hohn
parent
4a808e1e46
commit
ef070e191c
@@ -15,7 +15,7 @@ module SqliFlowConfig implements DataFlow::ConfigSig {
|
|||||||
// count = read(STDIN_FILENO, buf, BUFSIZE);
|
// count = read(STDIN_FILENO, buf, BUFSIZE);
|
||||||
exists(FunctionCall read |
|
exists(FunctionCall read |
|
||||||
read.getTarget().getName() = "read" and
|
read.getTarget().getName() = "read" and
|
||||||
read.getArgument(1) = source.(DataFlow::PostUpdateNode).getPreUpdateNode().asIndirectArgument()
|
read.getArgument(1) = source.asDefiningArgument()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
],
|
],
|
||||||
"settings": {
|
"settings": {
|
||||||
"codeQL.runningQueries.autoSave": true,
|
"codeQL.runningQueries.autoSave": true,
|
||||||
"makefile.configureOnOpen": false
|
"makefile.configureOnOpen": false,
|
||||||
|
"sarif-viewer.connectToGithubCodeScanning": "off"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
43
models.ql
Normal file
43
models.ql
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
import cpp
|
||||||
|
|
||||||
|
import semmle.code.cpp.models.Models
|
||||||
|
import semmle.code.cpp.commons.Scanf
|
||||||
|
|
||||||
|
import semmle.code.cpp.models.implementations.Strcpy
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* sources. To use this QL library, create a QL class extending `DataFlowFunction` with a
|
||||||
|
* characteristic predicate that selects the function or set of functions you
|
||||||
|
* are modeling. Within that class, override the predicates provided by
|
||||||
|
* `RemoteFlowSourceFunction` or `RemoteFlowSinkFunction` to match the flow within that
|
||||||
|
*/
|
||||||
|
class SDF extends DataFlowFunction {
|
||||||
|
// see import semmle.code.cpp.models.implementations.Strcpy
|
||||||
|
SDF () { any() }
|
||||||
|
|
||||||
|
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) { any()}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
import semmle.code.cpp.models.implementations.Fread
|
||||||
|
// Fread extends RemoteFlowSourceFunction
|
||||||
|
|
||||||
|
import semmle.code.cpp.models.interfaces.DataFlow
|
||||||
|
import semmle.code.cpp.models.interfaces.FlowSource
|
||||||
|
import semmle.code.cpp.models.implementations.Recv
|
||||||
|
// See
|
||||||
|
// private class Recv extends AliasFunction, ArrayFunction, SideEffectFunction,
|
||||||
|
// RemoteFlowSourceFunction
|
||||||
|
// for "read"
|
||||||
|
|
||||||
|
// // Find all *definitions*
|
||||||
|
// from DataFlowFunction dff
|
||||||
|
// select dff
|
||||||
|
|
||||||
|
// Find *uses* (via Call)
|
||||||
|
from DataFlowFunction dff, Call cl
|
||||||
|
where cl.getTarget() = dff
|
||||||
|
select cl.getLocation().getFile(), cl, dff
|
||||||
|
|
||||||
29
models1.ql
Normal file
29
models1.ql
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import cpp
|
||||||
|
import semmle.code.cpp.models.Models
|
||||||
|
import semmle.code.cpp.models.interfaces.FlowSource
|
||||||
|
|
||||||
|
// get sources / sinks from stdlib, use in our flow
|
||||||
|
|
||||||
|
// from RemoteFlowSourceFunction rfs, FunctionOutput output, string description
|
||||||
|
// where rfs.hasRemoteFlowSource(output, description)
|
||||||
|
// select rfs, rfs.getACallToThisFunction(), output, description
|
||||||
|
|
||||||
|
import semmle.code.cpp.models.interfaces.Sql
|
||||||
|
|
||||||
|
from SqlExecutionFunction sef
|
||||||
|
select sef, sef.getACallToThisFunction()
|
||||||
|
|
||||||
|
|
||||||
|
class Foo extends Expr {
|
||||||
|
Foo () { this.getNumChild() = 1 }
|
||||||
|
Expr getTheChild() {
|
||||||
|
result = this.getChild(0)
|
||||||
|
// given set f, in python: {element.getChild(0) for element in f}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// from Foo f
|
||||||
|
// select f, f.getTheChild()
|
||||||
|
|
||||||
|
// from BinaryOperation bin
|
||||||
|
// select bin, bin.getAChild()
|
||||||
68
models2.ql
Normal file
68
models2.ql
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
/**
|
||||||
|
* @name Uncontrolled data in SQL query
|
||||||
|
* @description Including user-supplied data in a SQL query without
|
||||||
|
* neutralizing special elements can make code vulnerable
|
||||||
|
* to SQL Injection.
|
||||||
|
* @kind path-problem
|
||||||
|
* @problem.severity error
|
||||||
|
* @security-severity 8.8
|
||||||
|
* @precision high
|
||||||
|
* @id cpp/sql-injection
|
||||||
|
* @tags security
|
||||||
|
* external/cwe/cwe-089
|
||||||
|
*/
|
||||||
|
|
||||||
|
import cpp
|
||||||
|
import semmle.code.cpp.security.Security
|
||||||
|
import semmle.code.cpp.security.FlowSources
|
||||||
|
import semmle.code.cpp.security.FunctionWithWrappers
|
||||||
|
import semmle.code.cpp.ir.IR
|
||||||
|
import semmle.code.cpp.ir.dataflow.TaintTracking
|
||||||
|
import SqlTainted::PathGraph
|
||||||
|
|
||||||
|
class SqlLikeFunction extends FunctionWithWrappers {
|
||||||
|
SqlLikeFunction() { sqlArgument(this.getName(), _) }
|
||||||
|
|
||||||
|
override predicate interestingArg(int arg) { sqlArgument(this.getName(), arg) }
|
||||||
|
}
|
||||||
|
|
||||||
|
Expr asSinkExpr(DataFlow::Node node) {
|
||||||
|
result = node.asIndirectArgument()
|
||||||
|
or
|
||||||
|
// We want the conversion so we only get one node for the expression
|
||||||
|
result = node.asExpr()
|
||||||
|
}
|
||||||
|
|
||||||
|
module SqlTaintedConfig implements DataFlow::ConfigSig {
|
||||||
|
predicate isSource(DataFlow::Node node) { node instanceof FlowSource }
|
||||||
|
|
||||||
|
predicate isSink(DataFlow::Node node) {
|
||||||
|
exists(SqlLikeFunction runSql | runSql.outermostWrapperFunctionCall(asSinkExpr(node), _))
|
||||||
|
}
|
||||||
|
|
||||||
|
predicate isBarrier(DataFlow::Node node) {
|
||||||
|
node.asExpr().getUnspecifiedType() instanceof IntegralType
|
||||||
|
}
|
||||||
|
|
||||||
|
predicate isBarrierIn(DataFlow::Node node) {
|
||||||
|
exists(SqlBarrierFunction sql, int arg, FunctionInput input |
|
||||||
|
node.asIndirectArgument() = sql.getACallToThisFunction().getArgument(arg) and
|
||||||
|
input.isParameterDeref(arg) and
|
||||||
|
sql.barrierSqlArgument(input, _)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module SqlTainted = TaintTracking::Global<SqlTaintedConfig>;
|
||||||
|
|
||||||
|
from
|
||||||
|
SqlLikeFunction runSql, Expr taintedArg, FlowSource taintSource, SqlTainted::PathNode sourceNode,
|
||||||
|
SqlTainted::PathNode sinkNode, string callChain
|
||||||
|
where
|
||||||
|
runSql.outermostWrapperFunctionCall(taintedArg, callChain) and
|
||||||
|
SqlTainted::flowPath(sourceNode, sinkNode) and
|
||||||
|
taintedArg = asSinkExpr(sinkNode.getNode()) and
|
||||||
|
taintSource = sourceNode.getNode()
|
||||||
|
select taintedArg, sourceNode, sinkNode,
|
||||||
|
"This argument to a SQL query function is derived from $@ and then passed to " + callChain + ".",
|
||||||
|
taintSource, "user input (" + taintSource.getSourceType() + ")"
|
||||||
74
models3.ql
Normal file
74
models3.ql
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
/**
|
||||||
|
* @name Uncontrolled data in SQL query
|
||||||
|
* @description Including user-supplied data in a SQL query without
|
||||||
|
* neutralizing special elements can make code vulnerable
|
||||||
|
* to SQL Injection.
|
||||||
|
* @kind path-problem
|
||||||
|
* @problem.severity error
|
||||||
|
* @security-severity 8.8
|
||||||
|
* @precision high
|
||||||
|
* @id cpp/sql-injection
|
||||||
|
* @tags security
|
||||||
|
* external/cwe/cwe-089
|
||||||
|
*/
|
||||||
|
|
||||||
|
import cpp
|
||||||
|
import semmle.code.cpp.security.Security
|
||||||
|
import semmle.code.cpp.security.FlowSources
|
||||||
|
import semmle.code.cpp.security.FunctionWithWrappers
|
||||||
|
import semmle.code.cpp.ir.IR
|
||||||
|
import semmle.code.cpp.ir.dataflow.TaintTracking
|
||||||
|
import SqlTainted::PathGraph
|
||||||
|
|
||||||
|
class SqlLikeFunction extends FunctionWithWrappers {
|
||||||
|
SqlLikeFunction() { sqlArgument(this.getName(), _) }
|
||||||
|
|
||||||
|
override predicate interestingArg(int arg) { sqlArgument(this.getName(), arg) }
|
||||||
|
}
|
||||||
|
|
||||||
|
Expr asSinkExpr(DataFlow::Node node) {
|
||||||
|
result = node.asIndirectArgument()
|
||||||
|
or
|
||||||
|
// We want the conversion so we only get one node for the expression
|
||||||
|
result = node.asExpr()
|
||||||
|
}
|
||||||
|
|
||||||
|
module SqlTaintedConfig implements DataFlow::ConfigSig {
|
||||||
|
predicate isSource(DataFlow::Node node) { node instanceof FlowSource }
|
||||||
|
|
||||||
|
predicate isSink(DataFlow::Node node) {
|
||||||
|
exists(SqlLikeFunction runSql | runSql.outermostWrapperFunctionCall(asSinkExpr(node), _))
|
||||||
|
}
|
||||||
|
|
||||||
|
predicate isBarrier(DataFlow::Node node) {
|
||||||
|
node.asExpr().getUnspecifiedType() instanceof IntegralType
|
||||||
|
}
|
||||||
|
|
||||||
|
predicate isBarrierIn(DataFlow::Node node) {
|
||||||
|
exists(SqlBarrierFunction sql, int arg, FunctionInput input |
|
||||||
|
node.asIndirectArgument() = sql.getACallToThisFunction().getArgument(arg) and
|
||||||
|
input.isParameterDeref(arg) and
|
||||||
|
sql.barrierSqlArgument(input, _)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module SqlTainted = TaintTracking::Global<SqlTaintedConfig>;
|
||||||
|
|
||||||
|
from
|
||||||
|
SqlLikeFunction runSql, Expr taintedArg, FlowSource taintSource, SqlTainted::PathNode sourceNode,
|
||||||
|
SqlTainted::PathNode sinkNode, string callChain
|
||||||
|
where
|
||||||
|
runSql.outermostWrapperFunctionCall(taintedArg, callChain) and
|
||||||
|
SqlTainted::flowPath(sourceNode, sinkNode) and
|
||||||
|
taintedArg = asSinkExpr(sinkNode.getNode()) and
|
||||||
|
taintSource = sourceNode.getNode()
|
||||||
|
select taintedArg, sourceNode, sinkNode,
|
||||||
|
"This argument to a SQL query function is derived from $@ and then passed to " + callChain + ".",
|
||||||
|
taintSource, "user input (" + taintSource.getSourceType() + ")"
|
||||||
|
|
||||||
|
// add
|
||||||
|
// char* get_user_info()
|
||||||
|
|
||||||
|
import mycustom
|
||||||
|
|
||||||
84
models4.ql
Normal file
84
models4.ql
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
/**
|
||||||
|
* @name Uncontrolled data in SQL query
|
||||||
|
* @description Including user-supplied data in a SQL query without
|
||||||
|
* neutralizing special elements can make code vulnerable
|
||||||
|
* to SQL Injection.
|
||||||
|
* @kind path-problem
|
||||||
|
* @problem.severity error
|
||||||
|
* @security-severity 8.8
|
||||||
|
* @precision high
|
||||||
|
* @id cpp/sql-injection
|
||||||
|
* @tags security
|
||||||
|
* external/cwe/cwe-089
|
||||||
|
*/
|
||||||
|
|
||||||
|
import cpp
|
||||||
|
import semmle.code.cpp.security.Security
|
||||||
|
import semmle.code.cpp.security.FlowSources
|
||||||
|
import semmle.code.cpp.security.FunctionWithWrappers
|
||||||
|
import semmle.code.cpp.ir.IR
|
||||||
|
import semmle.code.cpp.ir.dataflow.TaintTracking
|
||||||
|
import SqlTainted::PathGraph
|
||||||
|
|
||||||
|
class SqlLikeFunction extends FunctionWithWrappers {
|
||||||
|
SqlLikeFunction() { sqlArgument(this.getName(), _) }
|
||||||
|
|
||||||
|
override predicate interestingArg(int arg) { sqlArgument(this.getName(), arg) }
|
||||||
|
}
|
||||||
|
|
||||||
|
Expr asSinkExpr(DataFlow::Node node) {
|
||||||
|
result = node.asIndirectArgument()
|
||||||
|
or
|
||||||
|
// We want the conversion so we only get one node for the expression
|
||||||
|
result = node.asExpr()
|
||||||
|
}
|
||||||
|
|
||||||
|
module SqlTaintedConfig implements DataFlow::ConfigSig {
|
||||||
|
predicate isSource(DataFlow::Node node) { node instanceof FlowSource }
|
||||||
|
|
||||||
|
predicate isSink(DataFlow::Node node) {
|
||||||
|
exists(SqlLikeFunction runSql | runSql.outermostWrapperFunctionCall(asSinkExpr(node), _))
|
||||||
|
}
|
||||||
|
|
||||||
|
predicate isBarrier(DataFlow::Node node) {
|
||||||
|
node.asExpr().getUnspecifiedType() instanceof IntegralType
|
||||||
|
}
|
||||||
|
|
||||||
|
predicate isBarrierIn(DataFlow::Node node) {
|
||||||
|
exists(SqlBarrierFunction sql, int arg, FunctionInput input |
|
||||||
|
node.asIndirectArgument() = sql.getACallToThisFunction().getArgument(arg) and
|
||||||
|
input.isParameterDeref(arg) and
|
||||||
|
sql.barrierSqlArgument(input, _)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module SqlTainted = TaintTracking::Global<SqlTaintedConfig>;
|
||||||
|
|
||||||
|
from
|
||||||
|
SqlLikeFunction runSql, Expr taintedArg, FlowSource taintSource, SqlTainted::PathNode sourceNode,
|
||||||
|
SqlTainted::PathNode sinkNode, string callChain
|
||||||
|
where
|
||||||
|
runSql.outermostWrapperFunctionCall(taintedArg, callChain) and
|
||||||
|
SqlTainted::flowPath(sourceNode, sinkNode) and
|
||||||
|
taintedArg = asSinkExpr(sinkNode.getNode()) and
|
||||||
|
taintSource = sourceNode.getNode()
|
||||||
|
select taintedArg, sourceNode, sinkNode,
|
||||||
|
"This argument to a SQL query function is derived from $@ and then passed to " + callChain + ".",
|
||||||
|
taintSource, "user input (" + taintSource.getSourceType() + ")"
|
||||||
|
|
||||||
|
// add
|
||||||
|
// char* get_user_info()
|
||||||
|
|
||||||
|
class MyFS extends LocalFlowSource {
|
||||||
|
MyFS () {
|
||||||
|
exists(ReturnStmt rs |
|
||||||
|
rs.getEnclosingFunction().getName() = "get_user_info"
|
||||||
|
and this.asExpr() = rs.getExpr()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
override string getSourceType() {
|
||||||
|
result = "foo"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
17
mycustom.qll
Normal file
17
mycustom.qll
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
|
||||||
|
|
||||||
|
import cpp
|
||||||
|
import semmle.code.cpp.security.Security
|
||||||
|
import semmle.code.cpp.security.FlowSources
|
||||||
|
|
||||||
|
class MyFS extends LocalFlowSource {
|
||||||
|
MyFS () {
|
||||||
|
exists(ReturnStmt rs |
|
||||||
|
rs.getEnclosingFunction().getName() = "get_user_info"
|
||||||
|
and this.asExpr() = rs.getExpr()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
override string getSourceType() {
|
||||||
|
result = "foo"
|
||||||
|
}
|
||||||
|
}
|
||||||
73
session.ql
73
session.ql
@@ -1,19 +1,23 @@
|
|||||||
|
/**
|
||||||
|
* @name SQLI Vulnerability
|
||||||
|
* @description Using untrusted strings in a sql query allows sql injection attacks.
|
||||||
|
* @kind path-problem
|
||||||
|
* @id cpp/sqlivulnerable
|
||||||
|
* @problem.severity warning
|
||||||
|
*/
|
||||||
|
|
||||||
import cpp
|
import cpp
|
||||||
|
|
||||||
// from Call cl
|
// from Call cl
|
||||||
// select cl
|
// select cl
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
* int get_new_id() {
|
||||||
int get_new_id() {
|
* int id = getpid();
|
||||||
int id = getpid();
|
* return id;
|
||||||
return id;
|
* }
|
||||||
}
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Goal: Find connection
|
// Goal: Find connection
|
||||||
|
|
||||||
// 1. reading user data -- source
|
// 1. reading user data -- source
|
||||||
// count = read(STDIN_FILENO, buf, BUFSIZE - 1);
|
// count = read(STDIN_FILENO, buf, BUFSIZE - 1);
|
||||||
// ^^^
|
// ^^^
|
||||||
@@ -21,20 +25,27 @@ int get_new_id() {
|
|||||||
// where read.getTarget().getName() = "read"
|
// where read.getTarget().getName() = "read"
|
||||||
// and buf = read.getArgument(1)
|
// and buf = read.getArgument(1)
|
||||||
// select read, buf
|
// select read, buf
|
||||||
|
|
||||||
predicate isSource(Expr buf) {
|
predicate isSource(Expr buf) {
|
||||||
exists(FunctionCall read |
|
exists(FunctionCall read |
|
||||||
read.getTarget().getName() = "read"
|
read.getTarget().getName() = "read" and
|
||||||
and buf = read.getArgument(1)
|
buf = read.getArgument(1)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// from Expr buf
|
// from Expr buf
|
||||||
// where isSource(buf)
|
// where isSource(buf)
|
||||||
// select buf
|
// select buf
|
||||||
|
class MySource extends Expr {
|
||||||
|
MySource() {
|
||||||
|
exists(FunctionCall read |
|
||||||
|
read.getTarget().getName() = "read" and
|
||||||
|
this = read.getArgument(1)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// from MySource buf
|
||||||
|
// select buf
|
||||||
// 2. writing sql -- sink
|
// 2. writing sql -- sink
|
||||||
// rc = sqlite3_exec(db, query, NULL, 0, &zErrMsg);
|
// rc = sqlite3_exec(db, query, NULL, 0, &zErrMsg);
|
||||||
// ^^^^^
|
// ^^^^^
|
||||||
@@ -44,13 +55,45 @@ predicate isSource(Expr buf) {
|
|||||||
// select exec, query
|
// select exec, query
|
||||||
predicate isSink(Expr query) {
|
predicate isSink(Expr query) {
|
||||||
exists(FunctionCall exec |
|
exists(FunctionCall exec |
|
||||||
exec.getTarget().getName() = "sqlite3_exec"
|
exec.getTarget().getName() = "sqlite3_exec" and
|
||||||
and query = exec.getArgument(1)
|
query = exec.getArgument(1)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// from Expr query
|
// from Expr query
|
||||||
// where isSink(query)
|
// where isSink(query)
|
||||||
// select query
|
// select query
|
||||||
|
class MySink extends Expr {
|
||||||
|
FunctionCall exec;
|
||||||
|
|
||||||
|
MySink() {
|
||||||
|
exec.getTarget().getName() = "sqlite3_exec" and
|
||||||
|
this = exec.getArgument(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
FunctionCall getTheCall() { result = exec }
|
||||||
|
}
|
||||||
|
|
||||||
|
// from MySink query
|
||||||
|
// select query, query.getTheCall()
|
||||||
// 3. find call path between 1 and 2 them
|
// 3. find call path between 1 and 2 them
|
||||||
|
import semmle.code.cpp.dataflow.new.TaintTracking
|
||||||
|
|
||||||
|
module SqliFlowConfig implements DataFlow::ConfigSig {
|
||||||
|
predicate isSource(DataFlow::Node source) {
|
||||||
|
exists(MySource ms | ms = source.asDefiningArgument())
|
||||||
|
}
|
||||||
|
|
||||||
|
predicate isSink(DataFlow::Node sink) { exists(MySink ms | ms = sink.asIndirectArgument()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
module MyFlow = TaintTracking::Global<SqliFlowConfig>;
|
||||||
|
|
||||||
|
import MyFlow::PathGraph
|
||||||
|
|
||||||
|
from MyFlow::PathNode source, MyFlow::PathNode sink
|
||||||
|
where MyFlow::flowPath(source, sink)
|
||||||
|
select sink, source, sink, "Possible SQL injection"
|
||||||
|
|
||||||
|
// from MySource ms
|
||||||
|
// select ms.getASuccessor*()
|
||||||
Reference in New Issue
Block a user