diff --git a/FindFunction.ql b/FindFunction.ql new file mode 100644 index 0000000..67cb1a0 --- /dev/null +++ b/FindFunction.ql @@ -0,0 +1,12 @@ +/** + * @name Find Function + * @description List certain functions in a DB + * @kind problem + * @id csharp/intro/FindFunction + * @problem.severity warning + */ + +import csharp + +from Method me +select me, "Method found" diff --git a/SqliDemo/Injectable.cs b/SqliDemo/Injectable.cs index 46983ed..e590051 100644 --- a/SqliDemo/Injectable.cs +++ b/SqliDemo/Injectable.cs @@ -19,29 +19,29 @@ class Injectable return Process.GetCurrentProcess().Id; } -static void WriteInfo(int id, string info) -{ - const string connectionString = "Data Source=users.sqlite"; - using (var connection = new SqliteConnection(connectionString)) + static void WriteInfo(int id, string info) { - connection.Open(); - // '{info.Replace("'", "''")}')" has no vulnerability - string query = $"INSERT INTO users VALUES ({id}, '{info}')"; - Console.WriteLine($"Running query: {query}"); - - using (var command = new SqliteCommand(query, connection)) + const string connectionString = "Data Source=users.sqlite"; + using (var connection = new SqliteConnection(connectionString)) { - try + connection.Open(); + // '{info.Replace("'", "''")}')" has no vulnerability + string query = $"INSERT INTO users VALUES ({id}, '{info}')"; + Console.WriteLine($"Running query: {query}"); + + using (var command = new SqliteCommand(query, connection)) { - command.ExecuteNonQuery(); - } - catch (Exception ex) - { - Console.WriteLine($"Error executing query: {ex.Message}"); + try + { + command.ExecuteNonQuery(); + } + catch (Exception ex) + { + Console.WriteLine($"Error executing query: {ex.Message}"); + } } } } -} static void Main() diff --git a/admin b/admin new file mode 100755 index 0000000..5d3b70d --- /dev/null +++ b/admin @@ -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 diff --git a/admin.ps1 b/admin.ps1 new file mode 100644 index 0000000..008e2c3 --- /dev/null +++ b/admin.ps1 @@ -0,0 +1,92 @@ +# Set strict mode to stop execution on errors +Set-StrictMode -Version Latest + +# Script name +$script = $MyInvocation.MyCommand.Name + +# Color variables +$GREEN = "`e[0;32m" +$MAGENTA = "`e[0;95m" +$NC = "`e[0m" +$RED = "`e[0;31m" +$YELLOW = "`e[0;33m" + +# Determine the SQLite executable name +if ($IsWindows) { + $sqlite = "sqlite3.exe" +} else { + $sqlite = "sqlite3" +} + +# Function to show help +function Show-Help { + Write-Host "Usage: ./${script} [options]" -NoNewline + Write-Host "`n${YELLOW}Options:${NC}" -NoNewline + Write-Host "`n`t-h ${GREEN}Show Help ${NC}" -NoNewline + Write-Host "`n`t-c ${MAGENTA}Creates a users table ${NC}" -NoNewline + Write-Host "`n`t-s ${MAGENTA}Shows all records in the users table ${NC}" -NoNewline + Write-Host "`n`t-r ${RED}Removes users table ${NC}" +} + +# Function to remove the database +function Remove-DB { + if (Test-Path "users.sqlite") { + Remove-Item -Force "users.sqlite" + Write-Host "${GREEN}Database removed.${NC}" + } else { + Write-Host "${YELLOW}Database does not exist.${NC}" + } +} + +# Function to create the database +function Create-DB { + $createTableSQL = @" + CREATE TABLE IF NOT EXISTS users ( + user_id INTEGER not null, + name TEXT NOT NULL + ); +"@ + $createTableSQL | & $sqlite users.sqlite + Write-Host "${GREEN}Database created.${NC}" +} + +# Function to show database records +function Show-DB { + $querySQL = "SELECT * FROM users;" + $querySQL | & $sqlite users.sqlite +} + +# If no arguments, show help and exit +if ($args.Count -eq 0) { + Show-Help + exit +} + +# Parse arguments +while ($args.Count -gt 0) { + switch ($args[0]) { + '-h' { + Show-Help + exit + } + '-c' { + Create-DB + } + '-s' { + Show-DB + } + '-r' { + Remove-DB + } + default { + Write-Host "${RED}Invalid option: $($args[0])${NC}" + Show-Help + exit + } + } + if ($args.Count -gt 1) { + $args = $args[1..($args.Count - 1)] + } else { + $args = @() + } +} diff --git a/build.ps1 b/build.ps1 new file mode 100644 index 0000000..5cfa7c1 --- /dev/null +++ b/build.ps1 @@ -0,0 +1,7 @@ +# Change directory +Set-Location -Path "$HOME\work-gh\codeql-intro-csharp\SqliDemo" + +# Build the project +dotnet build + +Set-Location -Path "$HOME\work-gh\codeql-intro-csharp\" diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..ba9eabc --- /dev/null +++ b/build.sh @@ -0,0 +1,3 @@ +#!/bin/bash +cd ~/work-gh/codeql-intro-csharp/SqliDemo +dotnet build diff --git a/codeql-intro-csharp.code-workspace b/codeql-intro-csharp.code-workspace new file mode 100644 index 0000000..517e0b2 --- /dev/null +++ b/codeql-intro-csharp.code-workspace @@ -0,0 +1,8 @@ +{ + "folders": [ + { + "path": "." + } + ], + "settings": {} +} \ No newline at end of file diff --git a/codeql-pack.lock.yml b/codeql-pack.lock.yml new file mode 100644 index 0000000..d96ac38 --- /dev/null +++ b/codeql-pack.lock.yml @@ -0,0 +1,24 @@ +--- +lockVersion: 1.0.0 +dependencies: + codeql/controlflow: + version: 1.0.12 + codeql/csharp-all: + version: 3.1.1 + codeql/dataflow: + version: 1.1.6 + codeql/mad: + version: 1.0.12 + codeql/ssa: + version: 1.0.12 + codeql/threat-models: + version: 1.0.12 + codeql/tutorial: + version: 1.0.12 + codeql/typetracking: + version: 1.0.12 + codeql/util: + version: 1.0.12 + codeql/xml: + version: 1.0.12 +compiled: false diff --git a/csharp-sqli.sarif b/csharp-sqli.sarif new file mode 100644 index 0000000..1869880 --- /dev/null +++ b/csharp-sqli.sarif @@ -0,0 +1,383 @@ +{ + "$schema": "https://json.schemastore.org/sarif-2.1.0.json", + "version": "2.1.0", + "runs": [ + { + "tool": { + "driver": { + "name": "CodeQL", + "organization": "GitHub", + "semanticVersion": "2.19.2", + "notifications": [ + { + "id": "cs/baseline/expected-extracted-files", + "name": "cs/baseline/expected-extracted-files", + "shortDescription": { + "text": "Expected extracted files" + }, + "fullDescription": { + "text": "Files appearing in the source archive that are expected to be extracted." + }, + "defaultConfiguration": { + "enabled": true + }, + "properties": { + "tags": [ + "expected-extracted-files", + "telemetry" + ] + } + }, + { + "id": "cli/sip-enablement", + "name": "cli/sip-enablement", + "shortDescription": { + "text": "macOS SIP enablement status" + }, + "fullDescription": { + "text": "macOS SIP enablement status" + }, + "defaultConfiguration": { + "enabled": true + } + } + ], + "rules": [ + { + "id": "csharp/intro/FindFunction", + "name": "csharp/intro/FindFunction", + "shortDescription": { + "text": "Find Function" + }, + "fullDescription": { + "text": "List certain functions in a DB" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning" + }, + "properties": { + "description": "List certain functions in a DB", + "id": "csharp/intro/FindFunction", + "kind": "problem", + "name": "Find Function", + "problem.severity": "warning" + } + } + ] + }, + "extensions": [ + { + "name": "sample/csharp-sql-injection", + "semanticVersion": "0.0.1", + "locations": [ + { + "uri": "file:///Users/hohn/work-gh/codeql-intro-csharp/", + "description": { + "text": "The QL pack root directory." + }, + "properties": { + "tags": [ + "CodeQL/LocalPackRoot" + ] + } + }, + { + "uri": "file:///Users/hohn/work-gh/codeql-intro-csharp/qlpack.yml", + "description": { + "text": "The QL pack definition file." + }, + "properties": { + "tags": [ + "CodeQL/LocalPackDefinitionFile" + ] + } + } + ] + }, + { + "name": "codeql/csharp-all", + "semanticVersion": "3.1.1+de325133c7a95d84489acdf5a6ced07886ff5c6d", + "locations": [ + { + "uri": "file:///Users/hohn/.codeql/packages/codeql/csharp-all/3.1.1/", + "description": { + "text": "The QL pack root directory." + }, + "properties": { + "tags": [ + "CodeQL/LocalPackRoot" + ] + } + }, + { + "uri": "file:///Users/hohn/.codeql/packages/codeql/csharp-all/3.1.1/qlpack.yml", + "description": { + "text": "The QL pack definition file." + }, + "properties": { + "tags": [ + "CodeQL/LocalPackDefinitionFile" + ] + } + } + ] + }, + { + "name": "codeql/threat-models", + "semanticVersion": "1.0.12+de325133c7a95d84489acdf5a6ced07886ff5c6d", + "locations": [ + { + "uri": "file:///Users/hohn/.codeql/packages/codeql/threat-models/1.0.12/", + "description": { + "text": "The QL pack root directory." + }, + "properties": { + "tags": [ + "CodeQL/LocalPackRoot" + ] + } + }, + { + "uri": "file:///Users/hohn/.codeql/packages/codeql/threat-models/1.0.12/qlpack.yml", + "description": { + "text": "The QL pack definition file." + }, + "properties": { + "tags": [ + "CodeQL/LocalPackDefinitionFile" + ] + } + } + ] + } + ] + }, + "invocations": [ + { + "toolExecutionNotifications": [ + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "SqliDemo/Injectable.cs", + "uriBaseId": "%SRCROOT%", + "index": 0 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cs/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "HelloWorld/Program.cs", + "uriBaseId": "%SRCROOT%", + "index": 1 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cs/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "message": { + "text": "" + }, + "level": "note", + "timeUtc": "2024-12-03T18:57:27.937528Z", + "descriptor": { + "id": "cli/sip-enablement", + "index": 1 + }, + "properties": { + "attributes": { + "isEnabled": true + }, + "visibility": { + "statusPage": false, + "telemetry": true + } + } + } + ], + "executionSuccessful": true + } + ], + "artifacts": [ + { + "location": { + "uri": "SqliDemo/Injectable.cs", + "uriBaseId": "%SRCROOT%", + "index": 0 + } + }, + { + "location": { + "uri": "HelloWorld/Program.cs", + "uriBaseId": "%SRCROOT%", + "index": 1 + } + } + ], + "results": [ + { + "ruleId": "csharp/intro/FindFunction", + "ruleIndex": 0, + "rule": { + "id": "csharp/intro/FindFunction", + "index": 0 + }, + "message": { + "text": "Method found" + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "SqliDemo/Injectable.cs", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 8, + "startColumn": 19, + "endColumn": 31 + } + } + } + ], + "partialFingerprints": { + "primaryLocationLineHash": "e9487b577ff946ef:1", + "primaryLocationStartColumnFingerprint": "14" + } + }, + { + "ruleId": "csharp/intro/FindFunction", + "ruleIndex": 0, + "rule": { + "id": "csharp/intro/FindFunction", + "index": 0 + }, + "message": { + "text": "Method found" + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "SqliDemo/Injectable.cs", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 17, + "startColumn": 16, + "endColumn": 24 + } + } + } + ], + "partialFingerprints": { + "primaryLocationLineHash": "7aff2558b806fcc8:1", + "primaryLocationStartColumnFingerprint": "11" + } + }, + { + "ruleId": "csharp/intro/FindFunction", + "ruleIndex": 0, + "rule": { + "id": "csharp/intro/FindFunction", + "index": 0 + }, + "message": { + "text": "Method found" + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "SqliDemo/Injectable.cs", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 22, + "startColumn": 13, + "endColumn": 22 + } + } + } + ], + "partialFingerprints": { + "primaryLocationLineHash": "727c2f8457995f1b:1", + "primaryLocationStartColumnFingerprint": "12" + } + }, + { + "ruleId": "csharp/intro/FindFunction", + "ruleIndex": 0, + "rule": { + "id": "csharp/intro/FindFunction", + "index": 0 + }, + "message": { + "text": "Method found" + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "SqliDemo/Injectable.cs", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 47, + "startColumn": 17, + "endColumn": 21 + } + } + } + ], + "partialFingerprints": { + "primaryLocationLineHash": "e39b50fafc292b5d:1", + "primaryLocationStartColumnFingerprint": "12" + } + } + ], + "columnKind": "utf16CodeUnits", + "properties": { + "semmle.formatSpecifier": "sarif-latest" + } + } + ] +} diff --git a/csharp-sqli.txt b/csharp-sqli.txt new file mode 100644 index 0000000..70e0b9f --- /dev/null +++ b/csharp-sqli.txt @@ -0,0 +1,8 @@ +csharp/intro/FindFunction: Method found [0 more] + SqliDemo/Injectable.cs:8: +csharp/intro/FindFunction: Method found [0 more] + SqliDemo/Injectable.cs:17: +csharp/intro/FindFunction: Method found [0 more] + SqliDemo/Injectable.cs:22: +csharp/intro/FindFunction: Method found [0 more] + SqliDemo/Injectable.cs:47: diff --git a/qlpack.yml b/qlpack.yml new file mode 100644 index 0000000..692afcf --- /dev/null +++ b/qlpack.yml @@ -0,0 +1,5 @@ +library: false +name: workshop/csharp-sql-injection +version: 0.0.1 +dependencies: + codeql/csharp-all: "*" diff --git a/sarif-summary.jq b/sarif-summary.jq new file mode 100644 index 0000000..1f60353 --- /dev/null +++ b/sarif-summary.jq @@ -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"