mirror of
https://github.com/hohn/codeql-workshop-sql-injection-java.git
synced 2025-12-15 18:23:04 +01:00
Initial clone from https://github.com/advanced-security/codeql-workshops-staging/tree/master/java/codeql-dataflow-sql-injection
This commit is contained in:
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
*~
|
||||
*.html
|
||||
7
README.org
Normal file
7
README.org
Normal file
@@ -0,0 +1,7 @@
|
||||
* SQL injection example
|
||||
This is an introductory codeql workshop for Java.
|
||||
1. The problem and its source code are in [[./src]] along with a full description in
|
||||
[[./src/README.org]].
|
||||
2. The developed queries are in [[./solutions]] and their tests in [[./tests]]. The
|
||||
derivation of those queries is described in [[./solutions/README.org]]
|
||||
|
||||
BIN
codeql-overview-for-workshop.pdf
Normal file
BIN
codeql-overview-for-workshop.pdf
Normal file
Binary file not shown.
10
java-demo.code-workspace
Normal file
10
java-demo.code-workspace
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"folders": [
|
||||
{
|
||||
"path": "."
|
||||
}
|
||||
],
|
||||
"settings": {
|
||||
"git.ignoreLimitWarning": true
|
||||
}
|
||||
}
|
||||
366
solutions/README.html
Normal file
366
solutions/README.html
Normal file
@@ -0,0 +1,366 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
||||
<head>
|
||||
<!-- 2023-08-16 Wed 10:26 -->
|
||||
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>‎</title>
|
||||
<meta name="author" content="Michael Hohn" />
|
||||
<meta name="generator" content="Org Mode" />
|
||||
<style>
|
||||
#content { max-width: 60em; margin: auto; }
|
||||
.title { text-align: center;
|
||||
margin-bottom: .2em; }
|
||||
.subtitle { text-align: center;
|
||||
font-size: medium;
|
||||
font-weight: bold;
|
||||
margin-top:0; }
|
||||
.todo { font-family: monospace; color: red; }
|
||||
.done { font-family: monospace; color: green; }
|
||||
.priority { font-family: monospace; color: orange; }
|
||||
.tag { background-color: #eee; font-family: monospace;
|
||||
padding: 2px; font-size: 80%; font-weight: normal; }
|
||||
.timestamp { color: #bebebe; }
|
||||
.timestamp-kwd { color: #5f9ea0; }
|
||||
.org-right { margin-left: auto; margin-right: 0px; text-align: right; }
|
||||
.org-left { margin-left: 0px; margin-right: auto; text-align: left; }
|
||||
.org-center { margin-left: auto; margin-right: auto; text-align: center; }
|
||||
.underline { text-decoration: underline; }
|
||||
#postamble p, #preamble p { font-size: 90%; margin: .2em; }
|
||||
p.verse { margin-left: 3%; }
|
||||
pre {
|
||||
border: 1px solid #e6e6e6;
|
||||
border-radius: 3px;
|
||||
background-color: #f2f2f2;
|
||||
padding: 8pt;
|
||||
font-family: monospace;
|
||||
overflow: auto;
|
||||
margin: 1.2em;
|
||||
}
|
||||
pre.src {
|
||||
position: relative;
|
||||
overflow: auto;
|
||||
}
|
||||
pre.src:before {
|
||||
display: none;
|
||||
position: absolute;
|
||||
top: -8px;
|
||||
right: 12px;
|
||||
padding: 3px;
|
||||
color: #555;
|
||||
background-color: #f2f2f299;
|
||||
}
|
||||
pre.src:hover:before { display: inline; margin-top: 14px;}
|
||||
/* Languages per Org manual */
|
||||
pre.src-asymptote:before { content: 'Asymptote'; }
|
||||
pre.src-awk:before { content: 'Awk'; }
|
||||
pre.src-authinfo::before { content: 'Authinfo'; }
|
||||
pre.src-C:before { content: 'C'; }
|
||||
/* pre.src-C++ doesn't work in CSS */
|
||||
pre.src-clojure:before { content: 'Clojure'; }
|
||||
pre.src-css:before { content: 'CSS'; }
|
||||
pre.src-D:before { content: 'D'; }
|
||||
pre.src-ditaa:before { content: 'ditaa'; }
|
||||
pre.src-dot:before { content: 'Graphviz'; }
|
||||
pre.src-calc:before { content: 'Emacs Calc'; }
|
||||
pre.src-emacs-lisp:before { content: 'Emacs Lisp'; }
|
||||
pre.src-fortran:before { content: 'Fortran'; }
|
||||
pre.src-gnuplot:before { content: 'gnuplot'; }
|
||||
pre.src-haskell:before { content: 'Haskell'; }
|
||||
pre.src-hledger:before { content: 'hledger'; }
|
||||
pre.src-java:before { content: 'Java'; }
|
||||
pre.src-js:before { content: 'Javascript'; }
|
||||
pre.src-latex:before { content: 'LaTeX'; }
|
||||
pre.src-ledger:before { content: 'Ledger'; }
|
||||
pre.src-lisp:before { content: 'Lisp'; }
|
||||
pre.src-lilypond:before { content: 'Lilypond'; }
|
||||
pre.src-lua:before { content: 'Lua'; }
|
||||
pre.src-matlab:before { content: 'MATLAB'; }
|
||||
pre.src-mscgen:before { content: 'Mscgen'; }
|
||||
pre.src-ocaml:before { content: 'Objective Caml'; }
|
||||
pre.src-octave:before { content: 'Octave'; }
|
||||
pre.src-org:before { content: 'Org mode'; }
|
||||
pre.src-oz:before { content: 'OZ'; }
|
||||
pre.src-plantuml:before { content: 'Plantuml'; }
|
||||
pre.src-processing:before { content: 'Processing.js'; }
|
||||
pre.src-python:before { content: 'Python'; }
|
||||
pre.src-R:before { content: 'R'; }
|
||||
pre.src-ruby:before { content: 'Ruby'; }
|
||||
pre.src-sass:before { content: 'Sass'; }
|
||||
pre.src-scheme:before { content: 'Scheme'; }
|
||||
pre.src-screen:before { content: 'Gnu Screen'; }
|
||||
pre.src-sed:before { content: 'Sed'; }
|
||||
pre.src-sh:before { content: 'shell'; }
|
||||
pre.src-sql:before { content: 'SQL'; }
|
||||
pre.src-sqlite:before { content: 'SQLite'; }
|
||||
/* additional languages in org.el's org-babel-load-languages alist */
|
||||
pre.src-forth:before { content: 'Forth'; }
|
||||
pre.src-io:before { content: 'IO'; }
|
||||
pre.src-J:before { content: 'J'; }
|
||||
pre.src-makefile:before { content: 'Makefile'; }
|
||||
pre.src-maxima:before { content: 'Maxima'; }
|
||||
pre.src-perl:before { content: 'Perl'; }
|
||||
pre.src-picolisp:before { content: 'Pico Lisp'; }
|
||||
pre.src-scala:before { content: 'Scala'; }
|
||||
pre.src-shell:before { content: 'Shell Script'; }
|
||||
pre.src-ebnf2ps:before { content: 'ebfn2ps'; }
|
||||
/* additional language identifiers per "defun org-babel-execute"
|
||||
in ob-*.el */
|
||||
pre.src-cpp:before { content: 'C++'; }
|
||||
pre.src-abc:before { content: 'ABC'; }
|
||||
pre.src-coq:before { content: 'Coq'; }
|
||||
pre.src-groovy:before { content: 'Groovy'; }
|
||||
/* additional language identifiers from org-babel-shell-names in
|
||||
ob-shell.el: ob-shell is the only babel language using a lambda to put
|
||||
the execution function name together. */
|
||||
pre.src-bash:before { content: 'bash'; }
|
||||
pre.src-csh:before { content: 'csh'; }
|
||||
pre.src-ash:before { content: 'ash'; }
|
||||
pre.src-dash:before { content: 'dash'; }
|
||||
pre.src-ksh:before { content: 'ksh'; }
|
||||
pre.src-mksh:before { content: 'mksh'; }
|
||||
pre.src-posh:before { content: 'posh'; }
|
||||
/* Additional Emacs modes also supported by the LaTeX listings package */
|
||||
pre.src-ada:before { content: 'Ada'; }
|
||||
pre.src-asm:before { content: 'Assembler'; }
|
||||
pre.src-caml:before { content: 'Caml'; }
|
||||
pre.src-delphi:before { content: 'Delphi'; }
|
||||
pre.src-html:before { content: 'HTML'; }
|
||||
pre.src-idl:before { content: 'IDL'; }
|
||||
pre.src-mercury:before { content: 'Mercury'; }
|
||||
pre.src-metapost:before { content: 'MetaPost'; }
|
||||
pre.src-modula-2:before { content: 'Modula-2'; }
|
||||
pre.src-pascal:before { content: 'Pascal'; }
|
||||
pre.src-ps:before { content: 'PostScript'; }
|
||||
pre.src-prolog:before { content: 'Prolog'; }
|
||||
pre.src-simula:before { content: 'Simula'; }
|
||||
pre.src-tcl:before { content: 'tcl'; }
|
||||
pre.src-tex:before { content: 'TeX'; }
|
||||
pre.src-plain-tex:before { content: 'Plain TeX'; }
|
||||
pre.src-verilog:before { content: 'Verilog'; }
|
||||
pre.src-vhdl:before { content: 'VHDL'; }
|
||||
pre.src-xml:before { content: 'XML'; }
|
||||
pre.src-nxml:before { content: 'XML'; }
|
||||
/* add a generic configuration mode; LaTeX export needs an additional
|
||||
(add-to-list 'org-latex-listings-langs '(conf " ")) in .emacs */
|
||||
pre.src-conf:before { content: 'Configuration File'; }
|
||||
|
||||
table { border-collapse:collapse; }
|
||||
caption.t-above { caption-side: top; }
|
||||
caption.t-bottom { caption-side: bottom; }
|
||||
td, th { vertical-align:top; }
|
||||
th.org-right { text-align: center; }
|
||||
th.org-left { text-align: center; }
|
||||
th.org-center { text-align: center; }
|
||||
td.org-right { text-align: right; }
|
||||
td.org-left { text-align: left; }
|
||||
td.org-center { text-align: center; }
|
||||
dt { font-weight: bold; }
|
||||
.footpara { display: inline; }
|
||||
.footdef { margin-bottom: 1em; }
|
||||
.figure { padding: 1em; }
|
||||
.figure p { text-align: center; }
|
||||
.equation-container {
|
||||
display: table;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
}
|
||||
.equation {
|
||||
vertical-align: middle;
|
||||
}
|
||||
.equation-label {
|
||||
display: table-cell;
|
||||
text-align: right;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.inlinetask {
|
||||
padding: 10px;
|
||||
border: 2px solid gray;
|
||||
margin: 10px;
|
||||
background: #ffffcc;
|
||||
}
|
||||
#org-div-home-and-up
|
||||
{ text-align: right; font-size: 70%; white-space: nowrap; }
|
||||
textarea { overflow-x: auto; }
|
||||
.linenr { font-size: smaller }
|
||||
.code-highlighted { background-color: #ffff00; }
|
||||
.org-info-js_info-navigation { border-style: none; }
|
||||
#org-info-js_console-label
|
||||
{ font-size: 10px; font-weight: bold; white-space: nowrap; }
|
||||
.org-info-js_search-highlight
|
||||
{ background-color: #ffff00; color: #000000; font-weight: bold; }
|
||||
.org-svg { }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="content" class="content">
|
||||
<div id="table-of-contents" role="doc-toc">
|
||||
<h2>Table of Contents</h2>
|
||||
<div id="text-table-of-contents" role="doc-toc">
|
||||
<ul>
|
||||
<li><a href="#org791c6f8">1. Develop the query bottom-up</a></li>
|
||||
<li><a href="#orga2253a0">2. Optional: sarif file review of the results</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div id="outline-container-org791c6f8" class="outline-2">
|
||||
<h2 id="org791c6f8"><span class="section-number-2">1.</span> Develop the query bottom-up</h2>
|
||||
<div class="outline-text-2" id="text-1">
|
||||
<ol class="org-ol">
|
||||
<li><p>
|
||||
Identify the <i>source</i> part of the
|
||||
</p>
|
||||
<pre class="example">
|
||||
System.console().readLine();
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
expression, the <code>buf</code> argument.
|
||||
Start from a <code>from..where..select</code>, then convert to a predicate.
|
||||
</p></li>
|
||||
|
||||
<li><p>
|
||||
Identify the <i>sink</i> part of the
|
||||
</p>
|
||||
<pre class="example">
|
||||
conn.createStatement().executeUpdate(query);
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
expression, the <code>query</code> argument. Again start from <code>from..where..select</code>,
|
||||
then convert to a predicate.
|
||||
</p></li>
|
||||
|
||||
<li><p>
|
||||
Fill in the <i>taintflow configuration</i> boilerplate
|
||||
</p>
|
||||
<div class="org-src-container">
|
||||
<pre class="src src-java"><span style="color: #a020f0;">class</span> <span style="color: #228b22;">SqliFlowConfig</span> <span style="color: #a020f0;">extends</span> <span style="color: #228b22;">TaintTracking</span>::Configuration {
|
||||
SqliFlowConfig() { <span style="color: #a020f0;">this</span> = <span style="color: #8b2252;">"SqliFlow"</span> }
|
||||
|
||||
override <span style="color: #228b22;">predicate</span> <span style="color: #0000ff;">isSource</span>(DataFlow::Node node) {
|
||||
none()
|
||||
}
|
||||
|
||||
override <span style="color: #228b22;">predicate</span> <span style="color: #0000ff;">isSink</span>(DataFlow::Node node) {
|
||||
none()
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
</div></li>
|
||||
</ol>
|
||||
|
||||
<p>
|
||||
The final query (without <code>isAdditionalTaintStep</code>) is
|
||||
</p>
|
||||
<div class="org-src-container">
|
||||
<pre class="src src-java"><span style="color: #8b2252;">/**</span>
|
||||
<span style="color: #8b2252;"> * </span><span style="color: #008b8b;">@name</span><span style="color: #8b2252;"> SQLI Vulnerability</span>
|
||||
<span style="color: #8b2252;"> * </span><span style="color: #008b8b;">@description</span><span style="color: #8b2252;"> Using untrusted strings in a sql query allows sql injection attacks.</span>
|
||||
<span style="color: #8b2252;"> * </span><span style="color: #008b8b;">@kind</span><span style="color: #8b2252;"> path-problem</span>
|
||||
<span style="color: #8b2252;"> * </span><span style="color: #008b8b;">@id</span><span style="color: #8b2252;"> java/SQLIVulnerable</span>
|
||||
<span style="color: #8b2252;"> * </span><span style="color: #008b8b;">@problem</span><span style="color: #8b2252;">.severity warning</span>
|
||||
<span style="color: #8b2252;"> */</span>
|
||||
|
||||
<span style="color: #a020f0;">import</span> <span style="color: #228b22;">java</span>
|
||||
<span style="color: #a020f0;">import</span> <span style="color: #008b8b;">semmle</span>.<span style="color: #008b8b;">code</span>.<span style="color: #008b8b;">java</span>.<span style="color: #008b8b;">dataflow</span>.<span style="color: #228b22;">TaintTracking</span>
|
||||
<span style="color: #a020f0;">import</span> <span style="color: #228b22;">DataFlow</span>::PathGraph
|
||||
|
||||
<span style="color: #a020f0;">class</span> SqliFlowConfig <span style="color: #a020f0;">extends</span> <span style="color: #228b22;">TaintTracking</span>::Configuration {
|
||||
SqliFlowConfig() { <span style="color: #a020f0;">this</span> = <span style="color: #8b2252;">"SqliFlow"</span> }
|
||||
|
||||
override <span style="color: #228b22;">predicate</span> <span style="color: #0000ff;">isSource</span>(DataFlow::Node source) {
|
||||
<span style="color: #b22222;">// </span><span style="color: #b22222;">System.console().readLine();</span>
|
||||
exists(<span style="color: #228b22;">Call</span> <span style="color: #0000ff;">read</span> |
|
||||
read.getCallee().getName() = <span style="color: #8b2252;">"readLine"</span> and
|
||||
read = source.asExpr()
|
||||
)
|
||||
}
|
||||
|
||||
override <span style="color: #228b22;">predicate</span> <span style="color: #0000ff;">isSink</span>(DataFlow::Node sink) {
|
||||
<span style="color: #b22222;">// </span><span style="color: #b22222;">conn.createStatement().executeUpdate(query);</span>
|
||||
exists(<span style="color: #228b22;">Call</span> <span style="color: #0000ff;">exec</span> |
|
||||
exec.getCallee().getName() = <span style="color: #8b2252;">"executeUpdate"</span> and
|
||||
exec.getArgument(0) = sink.asExpr()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
from <span style="color: #228b22;">SqliFlowConfig</span> <span style="color: #a0522d;">conf</span>, <span style="color: #a0522d;">DataFlow</span>::PathNode source, <span style="color: #0000ff;">DataFlow</span>::PathNode sink
|
||||
where conf.hasFlowPath(source, sink)
|
||||
select sink, source, sink, <span style="color: #8b2252;">"Possible SQL injection"</span>
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="outline-container-orga2253a0" class="outline-2">
|
||||
<h2 id="orga2253a0"><span class="section-number-2">2.</span> Optional: sarif file review of the results</h2>
|
||||
<div class="outline-text-2" id="text-2">
|
||||
<p>
|
||||
Query results are available in several output formats using the cli. The
|
||||
following produces the sarif format, a json-based result description.
|
||||
</p>
|
||||
|
||||
<div class="org-src-container">
|
||||
<pre class="src src-sh"><span style="color: #b22222;"># </span><span style="color: #b22222;">The setup information from before</span>
|
||||
<span style="color: #483d8b;">export</span> <span style="color: #a0522d;">PATH</span>=$<span style="color: #a0522d;">HOME</span>/local/vmsync/codeql250:<span style="color: #8b2252;">"$PATH"</span>
|
||||
<span style="color: #a0522d;">SRCDIR</span>=$<span style="color: #a0522d;">HOME</span>/local/codeql-training-material.java-sqli/java/codeql-dataflow-sql-injection
|
||||
<span style="color: #a0522d;">DB</span>=$<span style="color: #a0522d;">SRCDIR</span>/java-sqli-$(<span style="color: #ff00ff;">cd $SRCDIR && git rev-parse --short HEAD</span>)
|
||||
|
||||
<span style="color: #b22222;"># </span><span style="color: #b22222;">Check paths</span>
|
||||
<span style="color: #483d8b;">echo</span> $<span style="color: #a0522d;">DB</span>
|
||||
<span style="color: #483d8b;">echo</span> $<span style="color: #a0522d;">SRCDIR</span>
|
||||
|
||||
<span style="color: #b22222;"># </span><span style="color: #b22222;">To see the help</span>
|
||||
codeql database analyze -h
|
||||
|
||||
<span style="color: #b22222;"># </span><span style="color: #b22222;">Run a query</span>
|
||||
codeql database analyze <span style="color: #8b2252;">\</span>
|
||||
-v <span style="color: #8b2252;">\</span>
|
||||
--ram=14000 <span style="color: #8b2252;">\</span>
|
||||
-j12 <span style="color: #8b2252;">\</span>
|
||||
--rerun <span style="color: #8b2252;">\</span>
|
||||
--search-path ~/local/vmsync/ql <span style="color: #8b2252;">\</span>
|
||||
--format=sarif-latest <span style="color: #8b2252;">\</span>
|
||||
--output java-sqli.sarif <span style="color: #8b2252;">\</span>
|
||||
-- <span style="color: #8b2252;">\</span>
|
||||
$<span style="color: #a0522d;">DB</span> <span style="color: #8b2252;">\</span>
|
||||
$<span style="color: #a0522d;">SRCDIR</span>/SqlInjection.ql
|
||||
|
||||
<span style="color: #b22222;"># </span><span style="color: #b22222;">Examine the file in an editor</span>
|
||||
edit java-sqli.sarif
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
<p>
|
||||
An example of using the sarif data is in the the jq script <a href="./sarif-summary.jq">./sarif-summary.jq</a>.
|
||||
When run against the sarif input via
|
||||
</p>
|
||||
<div class="org-src-container">
|
||||
<pre class="src src-sh">jq --raw-output --join-output -f sarif-summary.jq < java-sqli.sarif > java-sqli.txt
|
||||
</pre>
|
||||
</div>
|
||||
<p>
|
||||
it produces output in a form close to that of compiler error messages:
|
||||
</p>
|
||||
<div class="org-src-container">
|
||||
<pre class="src src-text">query-id: message line
|
||||
Path
|
||||
...
|
||||
Path
|
||||
...
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="postamble" class="status">
|
||||
<p class="author">Author: Michael Hohn</p>
|
||||
<p class="date">Created: 2023-08-16 Wed 10:26</p>
|
||||
<p class="validation"><a href="https://validator.w3.org/check?uri=referer">Validate</a></p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
119
solutions/README.org
Normal file
119
solutions/README.org
Normal file
@@ -0,0 +1,119 @@
|
||||
* Develop the query bottom-up
|
||||
1. Identify the /source/ part of the
|
||||
: System.console().readLine();
|
||||
expression, the =buf= argument.
|
||||
Start from a =from..where..select=, then convert to a predicate.
|
||||
|
||||
2. Identify the /sink/ part of the
|
||||
: conn.createStatement().executeUpdate(query);
|
||||
expression, the =query= argument. Again start from =from..where..select=,
|
||||
then convert to a predicate.
|
||||
|
||||
3. Fill in the /taintflow configuration/ boilerplate
|
||||
XX: update for new module approach;
|
||||
See ~/local/codeql-workshop-dataflow-c/exercises/Exercise16.ql
|
||||
[[file:~/local/codeql-workshop-dataflow-c/exercises/Exercise16.ql::module InputTypesToTypeValidation = DataFlow::Make<InputTypesToTypeValidationConfig>;]]
|
||||
or
|
||||
[[file:~/local/codeql-workshop-dataflow-c/solutions/Exercise16.ql::module InputTypesToTypeValidation = DataFlow::Make<InputTypesToTypeValidationConfig>;]]
|
||||
|
||||
#+BEGIN_SRC java
|
||||
class SqliFlowConfig extends TaintTracking::Configuration {
|
||||
SqliFlowConfig() { this = "SqliFlow" }
|
||||
|
||||
override predicate isSource(DataFlow::Node node) {
|
||||
none()
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node node) {
|
||||
none()
|
||||
}
|
||||
}
|
||||
#+END_SRC
|
||||
|
||||
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 java/SQLIVulnerable
|
||||
,* @problem.severity warning
|
||||
,*/
|
||||
|
||||
import java
|
||||
import semmle.code.java.dataflow.TaintTracking
|
||||
import DataFlow::PathGraph
|
||||
|
||||
class SqliFlowConfig extends TaintTracking::Configuration {
|
||||
SqliFlowConfig() { this = "SqliFlow" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
// System.console().readLine();
|
||||
exists(Call read |
|
||||
read.getCallee().getName() = "readLine" and
|
||||
read = source.asExpr()
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
// conn.createStatement().executeUpdate(query);
|
||||
exists(Call exec |
|
||||
exec.getCallee().getName() = "executeUpdate" and
|
||||
exec.getArgument(0) = 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.java-sqli/java/codeql-dataflow-sql-injection
|
||||
DB=$SRCDIR/java-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 java-sqli.sarif \
|
||||
-- \
|
||||
$DB \
|
||||
$SRCDIR/SqlInjection.ql
|
||||
|
||||
# Examine the file in an editor
|
||||
edit java-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 < java-sqli.sarif > java-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
|
||||
|
||||
6
solutions/qlpack.yml
Normal file
6
solutions/qlpack.yml
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
library: false
|
||||
name: codeql-workshop/java-sql-injection
|
||||
version: 0.0.1
|
||||
dependencies:
|
||||
codeql/java-all: "*"
|
||||
45
src/AddUser.java
Normal file
45
src/AddUser.java
Normal file
@@ -0,0 +1,45 @@
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.SQLException;
|
||||
|
||||
public class AddUser {
|
||||
public static Connection connect() {
|
||||
Connection conn = null;
|
||||
try {
|
||||
String url = "jdbc:sqlite:users.sqlite";
|
||||
conn = DriverManager.getConnection(url);
|
||||
System.out.println("Connected...");
|
||||
} catch (SQLException e) {
|
||||
System.out.println(e.getMessage());
|
||||
}
|
||||
return conn;
|
||||
}
|
||||
|
||||
static String get_user_info() {
|
||||
System.out.println("Enter name:");
|
||||
return System.console().readLine();
|
||||
}
|
||||
|
||||
static void write_info(int id, String info) {
|
||||
try (Connection conn = connect()) {
|
||||
String query = String.format("INSERT INTO users VALUES (%d, '%s')", id, info);
|
||||
conn.createStatement().executeUpdate(query);
|
||||
System.err.printf("Sent: %s", query);
|
||||
} catch (SQLException e) {
|
||||
System.out.println(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
static int get_new_id() {
|
||||
return (int)(Math.random()*100000);
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
String info;
|
||||
int id;
|
||||
|
||||
info = get_user_info();
|
||||
id = get_new_id();
|
||||
write_info(id, info);
|
||||
}
|
||||
}
|
||||
26
src/Makefile
Normal file
26
src/Makefile
Normal file
@@ -0,0 +1,26 @@
|
||||
add-user: add-user.c
|
||||
clang -Wall add-user.c -lsqlite3 -o add-user
|
||||
|
||||
clean:
|
||||
rm -f README.html add-user cpp-sqli.sarif cpp-sqli.txt users.log
|
||||
rm -f users.sqlite *.bak *~ cpp-sqli-demo.zip
|
||||
|
||||
ZIPLIST := \
|
||||
Makefile \
|
||||
README.org \
|
||||
SqlInjection.ql \
|
||||
add-user.c \
|
||||
add-user.sh \
|
||||
admin \
|
||||
build.sh \
|
||||
codeql-dataflow-sql-injection.md \
|
||||
codeql-overview-for-workshop.pdf \
|
||||
cpp-sqli.code-workspace \
|
||||
dataflow-cropped.pdf \
|
||||
qlpack.yml \
|
||||
sarif-summary.jq \
|
||||
session.ql
|
||||
|
||||
demo-zip:
|
||||
rm -f cpp-sqli-demo.zip
|
||||
zip cpp-sqli-demo.zip $(ZIPLIST)
|
||||
218
src/README.org
Normal file
218
src/README.org
Normal file
@@ -0,0 +1,218 @@
|
||||
* SQL injection example
|
||||
** Setup and sample run
|
||||
The jdbc connector at https://github.com/xerial/sqlite-jdbc, from [[https://github.com/xerial/sqlite-jdbc/releases/download/3.36.0.1/sqlite-jdbc-3.36.0.1.jar][here]] is
|
||||
included in the git repository.
|
||||
|
||||
#+BEGIN_SRC sh
|
||||
# Use a simple headline prompt
|
||||
PS1='
|
||||
\033[32m---- SQL injection demo ----\[\033[33m\033[0m\]
|
||||
$?:$ '
|
||||
|
||||
|
||||
# Build
|
||||
./build.sh
|
||||
|
||||
# Prepare db
|
||||
./admin -r
|
||||
./admin -c
|
||||
./admin -s
|
||||
|
||||
# Add regular user interactively
|
||||
./add-user 2>> users.log
|
||||
First User
|
||||
|
||||
# Check
|
||||
./admin -s
|
||||
|
||||
# Add Johnny Droptable
|
||||
./add-user 2>> users.log
|
||||
Johnny'); DROP TABLE users; --
|
||||
|
||||
# And the problem:
|
||||
./admin -s
|
||||
|
||||
# Check the log
|
||||
tail users.log
|
||||
#+END_SRC
|
||||
|
||||
** Identify the problem
|
||||
=./add-user= is reading from =STDIN=, and writing to a database; looking at the code in
|
||||
[[./AddUser.java]] leads to
|
||||
: System.console().readLine();
|
||||
for the read and
|
||||
: conn.createStatement().executeUpdate(query);
|
||||
for the write.
|
||||
|
||||
This problem is thus a dataflow problem; in codeql terminology we have
|
||||
- a /source/ at the =System.console().readLine();=
|
||||
- a /sink/ at the =conn.createStatement().executeUpdate(query);=
|
||||
|
||||
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
|
||||
To get started, build the codeql database (adjust paths to your setup):
|
||||
#+BEGIN_SRC sh
|
||||
# Build the db with source commit id.
|
||||
export PATH=$HOME/local/vmsync/codeql250:"$PATH"
|
||||
SRCDIR=$(pwd)
|
||||
DB=$SRCDIR/java-sqli-$(cd $SRCDIR && git rev-parse --short HEAD)
|
||||
|
||||
echo $DB
|
||||
test -d "$DB" && rm -fR "$DB"
|
||||
mkdir -p "$DB"
|
||||
|
||||
cd $SRCDIR && codeql database create --language=java -s . -j 8 -v $DB --command='./build.sh'
|
||||
|
||||
# Check for AddUser in the db
|
||||
unzip -v $DB/src.zip | grep AddUser
|
||||
#+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.java-sqli/java/codeql-dataflow-sql-injection
|
||||
DB=$SRCDIR/java-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=java -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
|
||||
: System.console().readLine();
|
||||
expression, the =buf= argument.
|
||||
Start from a =from..where..select=, then convert to a predicate.
|
||||
|
||||
2. Identify the /sink/ part of the
|
||||
: conn.createStatement().executeUpdate(query);
|
||||
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 SqliFlowConfig extends TaintTracking::Configuration {
|
||||
SqliFlowConfig() { this = "SqliFlow" }
|
||||
|
||||
override predicate isSource(DataFlow::Node node) {
|
||||
none()
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node node) {
|
||||
none()
|
||||
}
|
||||
}
|
||||
#+END_SRC
|
||||
|
||||
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 java/SQLIVulnerable
|
||||
,* @problem.severity warning
|
||||
,*/
|
||||
|
||||
import java
|
||||
import semmle.code.java.dataflow.TaintTracking
|
||||
import DataFlow::PathGraph
|
||||
|
||||
class SqliFlowConfig extends TaintTracking::Configuration {
|
||||
SqliFlowConfig() { this = "SqliFlow" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
// System.console().readLine();
|
||||
exists(Call read |
|
||||
read.getCallee().getName() = "readLine" and
|
||||
read = source.asExpr()
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
// conn.createStatement().executeUpdate(query);
|
||||
exists(Call exec |
|
||||
exec.getCallee().getName() = "executeUpdate" and
|
||||
exec.getArgument(0) = 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.java-sqli/java/codeql-dataflow-sql-injection
|
||||
DB=$SRCDIR/java-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 java-sqli.sarif \
|
||||
-- \
|
||||
$DB \
|
||||
$SRCDIR/SqlInjection.ql
|
||||
|
||||
# Examine the file in an editor
|
||||
edit java-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 < java-sqli.sarif > java-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
|
||||
|
||||
44
src/SqlInjection.ql
Normal file
44
src/SqlInjection.ql
Normal file
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* @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 java
|
||||
import semmle.code.java.dataflow.TaintTracking
|
||||
import DataFlow::PathGraph
|
||||
|
||||
class SqliFlowConfig extends TaintTracking::Configuration {
|
||||
SqliFlowConfig() { this = "SqliFlow" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
// System.console().readLine();
|
||||
exists(Call read |
|
||||
read.getCallee().getName() = "readLine" and
|
||||
read = source.asExpr()
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node sanitizer) { none() }
|
||||
|
||||
override predicate isAdditionalTaintStep(DataFlow::Node into, DataFlow::Node out) {
|
||||
// Extra taint step
|
||||
// String.format("INSERT INTO users VALUES (%d, '%s')", id, info);
|
||||
// Not needed here, but may be needed for larger libraries.
|
||||
none()
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
// conn.createStatement().executeUpdate(query);
|
||||
exists(Call exec |
|
||||
exec.getCallee().getName() = "executeUpdate" and
|
||||
exec.getArgument(0) = sink.asExpr()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
from SqliFlowConfig conf, DataFlow::PathNode source, DataFlow::PathNode sink
|
||||
where conf.hasFlowPath(source, sink)
|
||||
select sink, source, sink, "Possible SQL injection"
|
||||
3
src/add-user
Executable file
3
src/add-user
Executable file
@@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
java -cp ".:sqlite-jdbc-3.36.0.1.jar" AddUser $@
|
||||
|
||||
60
src/admin
Executable file
60
src/admin
Executable file
@@ -0,0 +1,60 @@
|
||||
#!/bin/bash
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
create-db () {
|
||||
echo '
|
||||
CREATE TABLE users (
|
||||
user_id INTEGER not null,
|
||||
name TEXT NOT NULL
|
||||
);
|
||||
' | sqlite3 users.sqlite
|
||||
}
|
||||
|
||||
show-db () {
|
||||
echo '
|
||||
SELECT * FROM users;
|
||||
' | sqlite3 users.sqlite
|
||||
}
|
||||
|
||||
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
|
||||
3
src/build.sh
Executable file
3
src/build.sh
Executable file
@@ -0,0 +1,3 @@
|
||||
#!/bin/bash
|
||||
javac AddUser.java
|
||||
|
||||
60
src/sarif-summary.jq
Normal file
60
src/sarif-summary.jq
Normal file
@@ -0,0 +1,60 @@
|
||||
# -*- sh -*-
|
||||
.runs | .[] | .results | .[] |
|
||||
( (.ruleId, ": ",
|
||||
(.message.text | split("\n") | ( .[0], " [", length-1 , " more]")),
|
||||
"\n")
|
||||
,
|
||||
(if (.codeFlows != null) then
|
||||
(.codeFlows | .[] |
|
||||
(" Path\n"
|
||||
,
|
||||
( .threadFlows | .[] | .locations | .[] | .location | " "
|
||||
,
|
||||
( .physicalLocation | ( .artifactLocation.uri, ":", .region.startLine, ":"))
|
||||
,
|
||||
(.message.text, " ")
|
||||
,
|
||||
"\n"
|
||||
)))
|
||||
else
|
||||
(.locations | .[] |
|
||||
( " "
|
||||
,
|
||||
(.physicalLocation | ( .artifactLocation.uri, ":", .region.startLine, ":"))
|
||||
))
|
||||
,
|
||||
# .message.text,
|
||||
"\n"
|
||||
end)
|
||||
) | tostring
|
||||
|
||||
# This script extracts the following parts of the sarif output:
|
||||
#
|
||||
# # problem
|
||||
# "runs" : [ {
|
||||
# "results" : [ {
|
||||
# "ruleId" : "cpp/UncheckedErrorCode",
|
||||
|
||||
# # path problem
|
||||
# "runs" : [ {
|
||||
# "tool" : {
|
||||
# "driver" : {
|
||||
# "rules" : [ {
|
||||
# "properties" : {
|
||||
# "kind" : "path-problem",
|
||||
|
||||
# "runs" : [ {
|
||||
# "results" : [ {
|
||||
# "ruleId" : "cpp/DangerousArithmetic",
|
||||
# "ruleIndex" : 6,
|
||||
# "message" : {
|
||||
# "text" : "Potential overflow (conversion: int -> unsigned int)\nPotential overflow (con
|
||||
|
||||
# "runs" : [ {
|
||||
# "results" : [ {
|
||||
# "codeFlows" : [ {
|
||||
# "threadFlows" : [ {
|
||||
# "locations" : [ {
|
||||
# "location" : {
|
||||
# "message" : {
|
||||
# "text" : "buff"
|
||||
BIN
src/sqlite-jdbc-3.36.0.1.jar
Normal file
BIN
src/sqlite-jdbc-3.36.0.1.jar
Normal file
Binary file not shown.
Reference in New Issue
Block a user