Introduction
The SQL Tamper Framework is a context-aware SQL transformation engine for WAF bypass, together with a set of SQLMap-compatible tamper scripts built on it. Its defining stance is that transforming a SQL injection payload correctly is a parsing problem: to mutate SQL so it evades a firewall’s signatures while still executing on the database, you have to understand the SQL well enough to know what each token is and which clause it lives in. This case study documents how the framework is built, the specific bugs in naive tamper scripts that motivated it, the lexer/context/transformer/AST architecture that resolves them, the transformation modules and WAF scripts layered on top, and the tests and benchmarks the project publishes.
I rewrote this project from scratch for v2.0.0 specifically to fix a class of bugs that plague hand-written tamper scripts, and the whole architecture follows from three concrete failures I kept hitting: broken multi-character operators, desynchronized position tracking, and context-blind encoding. Everything below is organized around solving those three.
Problem
A WAF bypass tamper script has one job: change the surface form of a SQL payload so it no longer matches the firewall’s rules, without changing what the database actually executes. That last clause is where naive scripts fail. SQL has structure, and string-level manipulation ignores it.
Three failures recur. First, multi-character operators: a script that URL-encodes character by character turns >= into %3E followed by a literal =, producing %3E=, which is not a valid operator and breaks the query. Second, position tracking: a script that remembers “the token at position 10 is SELECT” desynchronizes the instant SELECT is wrapped into /*!50000SELECT*/, because every position after it has now shifted. Third, context blindness: the same = operator should be encoded inside a WHERE clause but left alone in a SELECT list, and a script that cannot tell which clause it is in either under-transforms (fails to bypass) or over-transforms (breaks the query). All three produce invalid SQL, which the database rejects, which defeats the entire exercise.
Background
The framework is a Python project (99.6% Python, with a Dockerfile), GPL v2, published on PyPI as sqlmap-tamper-framework and on GitHub. It targets MySQL 5.7+/MariaDB 10.x syntax. It has two documented release lines: v2.0.0 (December 2025), a complete rewrite introducing the token-based framework, and v2.1.0 (February 2026), which added the advanced transformation modules, five more WAF scripts, and the meta-combiner. (An interim v2.0.1 on PyPI carried the same v2.0.0 framework.) The project is deliberately scoped to authorized testing, with a prominent legal disclaimer citing the CFAA, the UK Computer Misuse Act, and India’s IT Act 2000 Section 66.
Threat Model and Ethical Posture
This is an offensive tool, and its documentation is unusually explicit about scope. Permitted use is enumerated (systems you own, written authorization, authorized pentest engagements, in-scope bug bounties, and local vulnerable apps like DVWA, bWAPP, and SQLi-labs) and prohibited use is enumerated just as clearly (unauthorized systems, production systems without permission, anything illegal). A deliberate design choice reinforces the posture: the framework’s output is deterministic, with no built-in randomization, precisely so it cannot be used as a generator of unpredictable malicious variants. The author frames determinism as both an engineering property (reproducible for research and verification) and a safety property (no random mutation engine to abuse). That is a coherent stance: the tool is built to be auditable, not to spray.
Architecture
The architecture is a four-stage engine with a modular transformation layer on top. The Lexer tokenizes a SQL query into typed tokens, assigns each a UUID, and, critically, recognizes multi-character operators as single tokens. The Context Tracker walks the token stream and maintains the current clause state (SELECT, FROM, WHERE, and so on) plus nesting depth, so any transformation can ask “which clause am I in?” The Transformer applies a set of transformation rules, each of which declares the token types and clauses it targets, with reapplication protection so running the same transform twice does not double-encode. The AST Builder provides a hierarchical view for the harder cases: nested subqueries and function calls that a flat token stream handles poorly.
The transformation modules are separate files composed onto a transformer as rules: the core set (keyword_wrap, space_replace, case_alternate, value_encode) and the v2.1.0 advanced set (homoglyph, function_wrap, numeric_obfuscation, comment_chaos, logical_operator_swap, hex_encode, version_comment_vary). WAF-specific tamper scripts are pre-built chains of these modules tuned to a particular firewall, and meta_tamper.py combines chains based on environment variables.
The Three Critical Fixes
The README documents three fixes, and they are the heart of the design.
Multi-character operator support. The naive approach lexes character by character, so >= becomes two tokens and encoding yields the broken %3E=. The fix is to check multi-character operators first in the lexer, so >=, <=, <>, and != are recognized as single tokens and encode atomically to, for example, %3E%3D. This sounds trivial and is not: operator-first lexing is exactly the ordering discipline that separates a correct lexer from a broken one.
UUID-based token tracking. Position-based tracking breaks because transformations change token lengths: wrap SELECT into /*!50000SELECT*/ and position 10 no longer means what it did. The fix is to give every token a UUID at lex time that never changes, so a transformation can be tracked by identity rather than by a position that shifts underneath it. This is what makes reapplication protection and multi-pass transformation chains reliable.
Context awareness. The same operator must be encoded in WHERE but not in SELECT. The fix is the Context Tracker maintaining clause state, so a rule can declare allowed_clauses=[ClauseType.WHERE] and fire only there. This is what lets value_encode encode id>=5 in a WHERE clause while leaving a * in the SELECT list untouched.
Implementation
The public API reflects the architecture directly. A SQLTransformer is constructed, rules are added with add_rule(...), and transform(sql) returns the tampered string. Rules are created by factory functions like create_keyword_wrap_rule() and create_value_encode_rule(), or hand-built as a TransformationRule with a name, a transform_func(token, context), a list of target_types (e.g. TokenType.OPERATOR), and allowed_clauses (e.g. [ClauseType.WHERE]). The custom-transform example in the docs shows a function that inspects context.clause == ClauseType.WHERE and transforms only there, which is the whole framework in miniature: token in, context consulted, token out.
The flagship script, cloudflare2025.py, chains four transformations: keyword wrapping (SELECT to /*!50000SELECT*/), space replacement (space to /**/), value encoding (operators URL-encoded in WHERE/HAVING only), and case alternation (SELECT to sElEcT). The documented end-to-end example is worth reproducing because it shows all four composing: SELECT * FROM users WHERE id>=5 becomes /*!50000sElEcT*//**/*/**//*!50000fRoM*//**/users/**//*!50000wHeRe*//**/id%3E%3D5. Every keyword is version-wrapped and case-alternated, every space is a comment, and the operator is correctly encoded as %3E%3D (not the broken %3E=) and only because it is in the WHERE clause.
The Transformation Modules
The core four modules cover the classic MySQL WAF-bypass repertoire: version-comment keyword wrapping (which executes on MySQL but reads as a comment to a naive matcher), space-to-/**/ replacement (defeats rules keyed on whitespace), alternating case (defeats case-sensitive signatures), and operator URL encoding (defeats operator-based rules, applied context-sensitively).
The v2.1.0 advanced modules broaden the repertoire considerably. Homoglyphs substitute Unicode lookalike characters. Function wrapping hides values inside IF()/CASE expressions. Numeric obfuscation rewrites numbers as hex, float, or arithmetic expressions. Comment chaos varies comment styles. Logical-operator swapping turns AND/OR into &&/||. Hex encoding converts strings to hex literals. Version-comment variation randomizes, deterministically, the MySQL version number in the wrapping comments. Each is a separate module, which is the design’s real strength: a new evasion technique is a new file and a new rule, not a change to the engine.
WAF-Specific Scripts and the Meta-Combiner
On top of the modules sit seven pre-built tamper scripts, each a chain tuned to a specific firewall’s known behavior: cloudflare2025.py (version comments, case, space), awswaf2026.py (hex encoding, &&/||, v50700 comments), azurewaf2026.py (hex strings, comment chaos), modsec_crs2026.py (case first, math numbers), imperva2026.py (homoglyphs, function wrap), and akamai2026.py (float numbers). The seventh, meta_tamper.py, is an auto-select combiner that chains transformations based on environment variables, so a tester can drive the choice of evasion chain from configuration rather than by editing code. This turns the framework from a library into a ready-to-use kit: point SQLMap at the script matching the target’s WAF and run.
Testing
The project ships a test suite, and this is where an honest discrepancy appears. The README body and the v2.0.0 release notes state 33/33 tests passing, broken down as 10 lexer tests, 10 transformer tests, and 13 integration tests, covering multi-character operators, string literals, comments, UUID tracking, all transformations, context awareness, deterministic output, and real SQLMap payloads. The README’s test badge, however, reads “49+ passing.” I preserve both rather than reconciling them: the itemized 33 (10+10+13) is the number the documentation actually enumerates, while 49+ is the badge figure, and the gap most likely reflects tests added after the itemized breakdown was written. The integration tests specifically exercising real SQLMap payloads and complex/edge-case queries are the ones that matter most for a tool whose failure mode is “produces invalid SQL.”
Performance
The README publishes its own benchmarks, quoted here as repository-reported figures: a simple query (10 tokens) transforms in about 1ms, a complex query (100 tokens) in about 5ms, and a nested subquery in about 10ms. It also frames the token-based versus AST-based tradeoff in performance terms: token-based transformation is faster and simpler and is recommended for most cases, while AST-based transformation is more accurate and handles nesting and is recommended for complex queries. These are millisecond-scale operations, which is appropriate for a tool that runs inline in a SQLMap request pipeline where network latency dominates anyway.
Design Decisions
The central decision was to treat tampering as parsing: lex first, track context, transform by rule, reassemble. That decision is what makes the three critical fixes possible and what separates this from a regex tamper script. A second decision was UUID token identity over position tracking, which is what makes multi-pass and reapplication-safe transformation reliable. A third was modular transformations composed as rules, so the technique library grows without touching the engine. A fourth was deterministic, non-random output, chosen for both reproducibility and abuse resistance. A fifth was shipping pre-built, WAF-specific chains plus an env-driven combiner, so the framework is usable out of the box, not just as a library.
Tradeoffs
Parsing-based tampering trades implementation complexity (a real lexer, context tracker, and AST builder) for correctness that regex scripts cannot achieve. UUID tracking trades a little per-token overhead for stable identity across transformations. Deterministic output trades the evasion unpredictability that randomization would give for reproducibility and auditability, a deliberate safety choice. The MySQL/MariaDB focus trades cross-database generality for correctness on one dialect; the docs are explicit that PostgreSQL, MSSQL, and Oracle are not targets. Token-based versus AST-based is itself a tradeoff the framework exposes to the user: speed and simplicity versus accuracy on nested structure.
Limitations
The documented limitations are candid. The framework targets MySQL/MariaDB syntax and may not work against PostgreSQL, MSSQL, or Oracle. It is not a full SQL parser, so complex nested queries may hit edge cases, with the documented workaround being to simplify query structure. And it offers no universal bypass guarantee: effectiveness varies by WAF configuration, which is inherent to the problem, since a WAF bypass is always relative to a specific ruleset. Deeply nested subqueries are called out specifically as a potential failure point.
Future Work
The repository does not publish a roadmap. Grounded, near-term directions implied by the current tree include broadening the architecture layer, expanding the fuzzing corpus categories beyond loader and trace, quantifying decompiler and recovery quality, and publishing the benchmark set. Any specific committed roadmap is Additional validation required.
Lessons Learned
The clearest lesson is that correct SQL tampering is a lexing discipline: the operator-first ordering fix and the UUID-identity fix are both small, precise changes that only become obvious once you have been burned by the broken alternatives. A second lesson is that context is what separates over-transforming from correct-transforming; without clause tracking, you either fail to bypass or break the query. A third is that modularity pays off directly in a fast-moving domain: because each evasion technique is its own module, the jump from four core transformations to eleven-plus in v2.1.0, and from two WAF scripts to seven, was additive rather than a rewrite. A fourth is that determinism can be a feature rather than a limitation: it makes the tool testable, reproducible, and harder to weaponize as a random-variant generator.
Conclusion
The SQL Tamper Framework is a disciplined answer to a problem usually solved with fragile hacks. By lexing SQL into UUID-tracked tokens, tracking clause context, and applying deterministic, reapplication-safe transformation rules, it produces WAF-bypass payloads that remain valid SQL, fixing the multi-character-operator, position-tracking, and context-blindness bugs that break naive tamper scripts. It ships a modular transformation library, seven WAF-specific scripts and an env-driven combiner, a documented test suite, and its own benchmarks, all under an explicit authorized-use-only posture reinforced by deterministic output. It is a small tool built with real engineering discipline, and it is honest about its MySQL focus and its lack of any universal-bypass promise.
REGAAN R