mirror of
https://github.com/hohn/codeql-dataflow-sql-injection.git
synced 2025-12-16 02:03:05 +01:00
Insert updates from github.com:hohn/codeql.git
This commit is contained in:
committed by
=Michael Hohn
parent
9d130f1466
commit
dd664fe4ef
194
README.org
194
README.org
@@ -11,9 +11,9 @@
|
|||||||
./build.sh
|
./build.sh
|
||||||
|
|
||||||
# Prepare db
|
# Prepare db
|
||||||
./admin rm-db
|
./admin -r
|
||||||
./admin create-db
|
./admin -c
|
||||||
./admin show-db
|
./admin -s
|
||||||
|
|
||||||
# Add regular user interactively
|
# Add regular user interactively
|
||||||
./add-user 2>> users.log
|
./add-user 2>> users.log
|
||||||
@@ -22,34 +22,202 @@
|
|||||||
|
|
||||||
# Regular user via "external" process
|
# Regular user via "external" process
|
||||||
echo "User Outside" | ./add-user 2>> users.log
|
echo "User Outside" | ./add-user 2>> users.log
|
||||||
./admin show-db
|
|
||||||
|
|
||||||
# Check
|
# Check
|
||||||
./admin show-db
|
./admin -s
|
||||||
|
|
||||||
# Add Johnny Droptable
|
# Add Johnny Droptable
|
||||||
./add-user 2>> users.log
|
./add-user 2>> users.log
|
||||||
Johnny'); DROP TABLE users; --
|
Johnny'); DROP TABLE users; --
|
||||||
|
|
||||||
|
|
||||||
# And the problem:
|
# And the problem:
|
||||||
./admin show-db
|
./admin -s
|
||||||
|
|
||||||
# Check the log
|
# Check the log
|
||||||
|
tail users.log
|
||||||
#+END_SRC
|
#+END_SRC
|
||||||
|
|
||||||
|
** Identify the problem
|
||||||
|
=./add-user= is reading from =STDIN=, and writing to a database; looking at the code in
|
||||||
|
[[./add-user.c]] leads to
|
||||||
|
: count = read(STDIN_FILENO, buf, BUFSIZE - 1);
|
||||||
|
for the read and
|
||||||
|
: rc = sqlite3_exec(db, query, NULL, 0, &zErrMsg);
|
||||||
|
for the write.
|
||||||
|
|
||||||
|
This problem is thus a dataflow problem; in codeql terminology we have
|
||||||
|
- a /source/ at the =read(STDIN_FILENO, buf, BUFSIZE - 1);=
|
||||||
|
- a /sink/ at the =sqlite3_exec(db, query, NULL, 0, &zErrMsg);=
|
||||||
|
|
||||||
|
We write codeql to identify these two, and then connect them via
|
||||||
|
- a /dataflow configuration/ -- for this problem, the more general /taintflow
|
||||||
|
configuration/.
|
||||||
|
|
||||||
** Build codeql database
|
** Build codeql database
|
||||||
|
To get started, build the codeql database (adjust paths to your setup):
|
||||||
#+BEGIN_SRC sh
|
#+BEGIN_SRC sh
|
||||||
# Build the db with source commit id.
|
# Build the db with source commit id.
|
||||||
export PATH=$HOME/local/vmsync/codeql224:"$PATH"
|
export PATH=$HOME/local/vmsync/codeql250:"$PATH"
|
||||||
SRCDIR=$HOME/local/codeql-dataflow-sql-injection/
|
SRCDIR=$HOME/local/codeql-training-material.cpp-sqli/cpp/codeql-dataflow-sql-injection
|
||||||
DB=$HOME/local/db/codeql-dataflow-sql-injection-$(cd $SRCDIR && git rev-parse --short HEAD)
|
DB=$SRCDIR/cpp-sqli-$(cd $SRCDIR && git rev-parse --short HEAD)
|
||||||
|
|
||||||
echo $DB
|
echo $DB
|
||||||
test -d "$DB" && rm -fR "$DB"
|
test -d "$DB" && rm -fR "$DB"
|
||||||
mkdir -p "$DB"
|
mkdir -p "$DB"
|
||||||
|
|
||||||
cd $SRCDIR
|
cd $SRCDIR && codeql database create --language=cpp -s . -j 8 -v $DB --command='./build.sh'
|
||||||
codeql database create --language=cpp -s $SRCDIR -j 8 -v $DB --command='./build.sh'
|
|
||||||
#+END_SRC
|
#+END_SRC
|
||||||
|
|
||||||
|
Then add this database directory to your VS Code =DATABASES= tab.
|
||||||
|
|
||||||
|
|
||||||
|
** Build codeql database in steps
|
||||||
|
For larger projects, using a single command to build everything is costly when
|
||||||
|
any part of the build fails.
|
||||||
|
|
||||||
|
To build a database in steps, use the following sequence, adjusting paths to
|
||||||
|
your setup:
|
||||||
|
#+BEGIN_SRC sh
|
||||||
|
# Build the db with source commit id.
|
||||||
|
export PATH=$HOME/local/vmsync/codeql250:"$PATH"
|
||||||
|
SRCDIR=$HOME/local/codeql-training-material.cpp-sqli/cpp/codeql-dataflow-sql-injection
|
||||||
|
DB=$SRCDIR/cpp-sqli-$(cd $SRCDIR && git rev-parse --short HEAD)
|
||||||
|
|
||||||
|
# Check paths
|
||||||
|
echo $DB
|
||||||
|
echo $SRCDIR
|
||||||
|
|
||||||
|
# Prepare db directory
|
||||||
|
test -d "$DB" && rm -fR "$DB"
|
||||||
|
mkdir -p "$DB"
|
||||||
|
|
||||||
|
# Run the build
|
||||||
|
cd $SRCDIR
|
||||||
|
codeql database init --language=cpp -s . -v $DB
|
||||||
|
# Repeat trace-command as needed to cover all targets
|
||||||
|
codeql database trace-command -v $DB -- make
|
||||||
|
codeql database finalize -j4 $DB
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
Then add this database directory to your VS Code =DATABASES= tab.
|
||||||
|
|
||||||
|
** Develop the query bottom-up
|
||||||
|
1. Identify the /source/ part of the
|
||||||
|
: read(STDIN_FILENO, buf, BUFSIZE - 1);
|
||||||
|
expression, the =buf= argument.
|
||||||
|
Start from a =from..where..select=, then convert to a predicate.
|
||||||
|
|
||||||
|
2. Identify the /sink/ part of the
|
||||||
|
: sqlite3_exec(db, query, NULL, 0, &zErrMsg);
|
||||||
|
expression, the =query= argument. Again start from =from..where..select=,
|
||||||
|
then convert to a predicate.
|
||||||
|
|
||||||
|
3. Fill in the /taintflow configuration/ boilerplate
|
||||||
|
#+BEGIN_SRC java
|
||||||
|
class CppSqli extends TaintTracking::Configuration {
|
||||||
|
CppSqli() { this = "CppSqli" }
|
||||||
|
|
||||||
|
override predicate isSource(DataFlow::Node node) {
|
||||||
|
none()
|
||||||
|
}
|
||||||
|
|
||||||
|
override predicate isSink(DataFlow::Node node) {
|
||||||
|
none()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
Note that an inout-argument in C/C++ (the =buf= pointer is passed to =read=
|
||||||
|
and points to updated data after the return) is accessed as a codeql source
|
||||||
|
via
|
||||||
|
: source.(DataFlow::PostUpdateNode).getPreUpdateNode().asExpr()
|
||||||
|
instead of the usual
|
||||||
|
: source.asExpr()
|
||||||
|
|
||||||
|
The final query (without =isAdditionalTaintStep=) is
|
||||||
|
#+BEGIN_SRC java
|
||||||
|
/**
|
||||||
|
,* @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 semmle.code.cpp.dataflow.TaintTracking
|
||||||
|
import DataFlow::PathGraph
|
||||||
|
|
||||||
|
class SqliFlowConfig extends TaintTracking::Configuration {
|
||||||
|
SqliFlowConfig() { this = "SqliFlow" }
|
||||||
|
|
||||||
|
override predicate isSource(DataFlow::Node source) {
|
||||||
|
// count = read(STDIN_FILENO, buf, BUFSIZE);
|
||||||
|
exists(FunctionCall read |
|
||||||
|
read.getTarget().getName() = "read" and
|
||||||
|
read.getArgument(1) = source.(DataFlow::PostUpdateNode).getPreUpdateNode().asExpr()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override predicate isSink(DataFlow::Node sink) {
|
||||||
|
// rc = sqlite3_exec(db, query, NULL, 0, &zErrMsg);
|
||||||
|
exists(FunctionCall exec |
|
||||||
|
exec.getTarget().getName() = "sqlite3_exec" and
|
||||||
|
exec.getArgument(1) = sink.asExpr()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
from SqliFlowConfig conf, DataFlow::PathNode source, DataFlow::PathNode sink
|
||||||
|
where conf.hasFlowPath(source, sink)
|
||||||
|
select sink, source, sink, "Possible SQL injection"
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
** Optional: sarif file review of the results
|
||||||
|
Query results are available in several output formats using the cli. The
|
||||||
|
following produces the sarif format, a json-based result description.
|
||||||
|
|
||||||
|
#+BEGIN_SRC sh
|
||||||
|
# The setup information from before
|
||||||
|
export PATH=$HOME/local/vmsync/codeql250:"$PATH"
|
||||||
|
SRCDIR=$HOME/local/codeql-training-material.cpp-sqli/cpp/codeql-dataflow-sql-injection
|
||||||
|
DB=$SRCDIR/cpp-sqli-$(cd $SRCDIR && git rev-parse --short HEAD)
|
||||||
|
|
||||||
|
# Check paths
|
||||||
|
echo $DB
|
||||||
|
echo $SRCDIR
|
||||||
|
|
||||||
|
# To see the help
|
||||||
|
codeql database analyze -h
|
||||||
|
|
||||||
|
# Run a query
|
||||||
|
codeql database analyze \
|
||||||
|
-v \
|
||||||
|
--ram=14000 \
|
||||||
|
-j12 \
|
||||||
|
--rerun \
|
||||||
|
--search-path ~/local/vmsync/ql \
|
||||||
|
--format=sarif-latest \
|
||||||
|
--output cpp-sqli.sarif \
|
||||||
|
-- \
|
||||||
|
$DB \
|
||||||
|
$SRCDIR/SqlInjection.ql
|
||||||
|
|
||||||
|
# Examine the file in an editor
|
||||||
|
edit cpp-sqli.sarif
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
An example of using the sarif data is in the the jq script [[./sarif-summary.jq]].
|
||||||
|
When run against the sarif input via
|
||||||
|
#+BEGIN_SRC sh
|
||||||
|
jq --raw-output --join-output -f sarif-summary.jq < cpp-sqli.sarif > cpp-sqli.txt
|
||||||
|
#+END_SRC
|
||||||
|
it produces output in a form close to that of compiler error messages:
|
||||||
|
#+BEGIN_SRC text
|
||||||
|
query-id: message line
|
||||||
|
Path
|
||||||
|
...
|
||||||
|
Path
|
||||||
|
...
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
|||||||
@@ -42,14 +42,17 @@ void abort_on_exec_error(int rc, sqlite3 *db, char* zErrMsg) {
|
|||||||
char* get_user_info() {
|
char* get_user_info() {
|
||||||
#define BUFSIZE 1024
|
#define BUFSIZE 1024
|
||||||
char* buf = (char*) malloc(BUFSIZE * sizeof(char));
|
char* buf = (char*) malloc(BUFSIZE * sizeof(char));
|
||||||
|
if(buf==NULL) abort();
|
||||||
int count;
|
int count;
|
||||||
// Disable buffering to avoid need for fflush
|
// Disable buffering to avoid need for fflush
|
||||||
// after printf().
|
// after printf().
|
||||||
setbuf( stdout, NULL );
|
setbuf( stdout, NULL );
|
||||||
printf("*** Welcome to sql injection ***\n");
|
printf("*** Welcome to sql injection ***\n");
|
||||||
printf("Please enter name: ");
|
printf("Please enter name: ");
|
||||||
count = read(STDIN_FILENO, buf, BUFSIZE);
|
count = read(STDIN_FILENO, buf, BUFSIZE - 1);
|
||||||
if (count <= 0) abort();
|
if (count <= 0) abort();
|
||||||
|
// ensure the buffer is zero-terminated
|
||||||
|
buf[count] = '\0';
|
||||||
/* strip trailing whitespace */
|
/* strip trailing whitespace */
|
||||||
while (count && isspace(buf[count-1])) {
|
while (count && isspace(buf[count-1])) {
|
||||||
buf[count-1] = 0; --count;
|
buf[count-1] = 0; --count;
|
||||||
@@ -90,6 +93,7 @@ int main(int argc, char* argv[]) {
|
|||||||
info = get_user_info();
|
info = get_user_info();
|
||||||
id = get_new_id();
|
id = get_new_id();
|
||||||
write_info(id, info);
|
write_info(id, info);
|
||||||
|
free(info);
|
||||||
/*
|
/*
|
||||||
* show_info(id);
|
* show_info(id);
|
||||||
*/
|
*/
|
||||||
|
|||||||
43
admin
43
admin
@@ -1,5 +1,24 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
rm-db () {
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
script=$(basename "$0")
|
||||||
|
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
MAGENTA='\033[0;95m'
|
||||||
|
NC='\033[0m'
|
||||||
|
RED='\033[0;31m'
|
||||||
|
YELLOW='\033[0;33m'
|
||||||
|
|
||||||
|
help() {
|
||||||
|
echo -e "Usage: ./${script} [options]" \
|
||||||
|
"\n${YELLOW}Options: ${NC}" \
|
||||||
|
"\n\t -h ${GREEN}Show Help ${NC}" \
|
||||||
|
"\n\t -c ${MAGENTA}Creates a users table ${NC}" \
|
||||||
|
"\n\t -s ${MAGENTA}Shows all records in the users table ${NC}" \
|
||||||
|
"\n\t -r ${RED}Removes users table ${NC}"
|
||||||
|
}
|
||||||
|
remove-db () {
|
||||||
rm users.sqlite
|
rm users.sqlite
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -18,4 +37,24 @@ show-db () {
|
|||||||
' | sqlite3 users.sqlite
|
' | sqlite3 users.sqlite
|
||||||
}
|
}
|
||||||
|
|
||||||
eval $@
|
if [ $# == 0 ]; then
|
||||||
|
help
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
while getopts "h?csr" option
|
||||||
|
do
|
||||||
|
case "${option}"
|
||||||
|
in
|
||||||
|
h|\?)
|
||||||
|
help
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
c) create-db
|
||||||
|
;;
|
||||||
|
s) show-db
|
||||||
|
;;
|
||||||
|
r) remove-db
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|||||||
@@ -4,8 +4,14 @@
|
|||||||
"path": "."
|
"path": "."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "[codeql-dataflow-sql-injection-d5b28fb source archive]",
|
"path": "../../../vmsync/ql"
|
||||||
"uri": "codeql-zip-archive://0-66/Users/hohn/local/db/codeql-dataflow-sql-injection-d5b28fb/src.zip/"
|
},
|
||||||
|
{
|
||||||
|
"name": "[cpp-sqli-89900b3 source archive]",
|
||||||
|
"uri": "codeql-zip-archive://0-110/Users/hohn/local/codeql-training-material.cpp-sqli/cpp/codeql-dataflow-sql-injection/cpp-sqli-89900b3/src.zip"
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
"settings": {
|
||||||
|
"codeQL.runningQueries.autoSave": true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -65,7 +65,7 @@ If you get stuck, try searching our documentation and blog posts for help and id
|
|||||||
- [Using the CodeQL extension for VS Code](https://help.semmle.com/codeql/codeql-for-vscode.html)
|
- [Using the CodeQL extension for VS Code](https://help.semmle.com/codeql/codeql-for-vscode.html)
|
||||||
|
|
||||||
## Codeql Recap
|
## Codeql Recap
|
||||||
This is a brief review of codeql taken from the [full
|
This is a brief review of CodeQL taken from the [full
|
||||||
introduction](https://git.io/JJqdS). For more details, see the [documentation
|
introduction](https://git.io/JJqdS). For more details, see the [documentation
|
||||||
links](#documentation-links). We will revisit all of this during the tutorial.
|
links](#documentation-links). We will revisit all of this during the tutorial.
|
||||||
|
|
||||||
@@ -89,7 +89,7 @@ select /* ... expressions ... */
|
|||||||
|
|
||||||
The `from` clause specifies some variables that will be used in the query. The
|
The `from` clause specifies some variables that will be used in the query. The
|
||||||
`where` clause specifies some conditions on those variables in the form of logical
|
`where` clause specifies some conditions on those variables in the form of logical
|
||||||
formulas. The `select` clauses speciifes what the results should be, and can refer
|
formulas. The `select` clauses specifies what the results should be, and can refer
|
||||||
to variables defined in the `from` clause.
|
to variables defined in the `from` clause.
|
||||||
|
|
||||||
The `from` clause is defined as a series of variable declarations, where each
|
The `from` clause is defined as a series of variable declarations, where each
|
||||||
@@ -206,9 +206,9 @@ This program can be compiled and linked, and a simple sqlite db created via
|
|||||||
./build.sh
|
./build.sh
|
||||||
|
|
||||||
# Prepare db
|
# Prepare db
|
||||||
./admin rm-db
|
./admin -r
|
||||||
./admin create-db
|
./admin -c
|
||||||
./admin show-db
|
./admin -s
|
||||||
```
|
```
|
||||||
|
|
||||||
Users can be added via `stdin` in several ways; the second is a pretend "server"
|
Users can be added via `stdin` in several ways; the second is a pretend "server"
|
||||||
@@ -226,14 +226,14 @@ echo "User Outside" | ./add-user 2>> users.log
|
|||||||
Check the db and log:
|
Check the db and log:
|
||||||
```
|
```
|
||||||
# Check
|
# Check
|
||||||
./admin show-db
|
./admin -s
|
||||||
|
|
||||||
tail -4 users.log
|
tail -4 users.log
|
||||||
```
|
```
|
||||||
|
|
||||||
Looks ok:
|
Looks ok:
|
||||||
```
|
```
|
||||||
0:$ ./admin show-db
|
0:$ ./admin -s
|
||||||
87797|First User
|
87797|First User
|
||||||
87808|User Outside
|
87808|User Outside
|
||||||
|
|
||||||
@@ -252,8 +252,8 @@ Johnny'); DROP TABLE users; --
|
|||||||
And then we have this:
|
And then we have this:
|
||||||
```sh
|
```sh
|
||||||
# And the problem:
|
# And the problem:
|
||||||
./admin show-db
|
./admin -s
|
||||||
0:$ ./admin show-db
|
0:$ ./admin -s
|
||||||
Error: near line 2: no such table: users
|
Error: near line 2: no such table: users
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -580,7 +580,7 @@ the process of building and exploring the data flow path.
|
|||||||
One such feature is adding additional taint steps. This is useful if you use
|
One such feature is adding additional taint steps. This is useful if you use
|
||||||
libraries which are not modelled by the default taint tracking. You can implement
|
libraries which are not modelled by the default taint tracking. You can implement
|
||||||
this by overriding `isAdditionalTaintStep` predicate. This has two parameters, the
|
this by overriding `isAdditionalTaintStep` predicate. This has two parameters, the
|
||||||
`from` and the `to` node, and essentially allows you to add extra edges into the
|
`from` and the `to` node, and it essentially allows you to add extra edges into the
|
||||||
taint tracking or data flow graph.
|
taint tracking or data flow graph.
|
||||||
|
|
||||||
A starting configuration can look like the following, with details to be filled
|
A starting configuration can look like the following, with details to be filled
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
name: cpp_sql_injection
|
name: cpp-sql-injection
|
||||||
version: 0.0.0
|
version: 0.0.0
|
||||||
libraryPathDependencies: codeql-cpp
|
libraryPathDependencies: codeql-cpp
|
||||||
|
|||||||
Reference in New Issue
Block a user