一、先判断是否为注入点 (个人观点,仅供参考)
1.如果输入'或者"就直接报错,说明他与数据库交互了,则该处为注入点
2.即使1中没有报错,也不能说明无注入点,可能是后台做了过滤,可以尝试逻辑判断语句,例如对于数字型,and 1=1,页面正常,大概率就可以说明有sql注入了,如果and 1=2 页面报错,那就基本存在了(如果and 1=2正常那就是字符注入了);对于字符型,1‘ and ’1‘=’1 页面正常,而1' and '1'='1 页面报错(这里的报错是指页面直接返回了注入的字符等等)
注:还可以使用
') and 1=2,
") and 1=2,
) and 1=2,
")) and 1=2等等,总之要不断尝试,看看能不能让它报错
注:有些地方直接写') and 1=2--+实际上我感觉是有些问题的,那些人想的是同时把让页面报错和寻找闭合方式结合成同一步(即直接让页面正常,这也其实没什么问题,但对于新手不太好理解,这样就要回过来再判断到底是什么类型的注入,比如说报错类型,盲注),因为我想的是先让它报错,再观察页面变化,就方便下一步的payload类型注入(比如字符型还是updatexml()报错注入)
3.即使没有报错信息,但是也可能存在注入点,因为有时侯注入成功了,但是数据库不会回显,这时就可能存在盲注以及报错注入了,报错注入是由于后台没有对数据库的报错信息进行屏蔽
3.1 报错注入的快速判断方法:1' and updatexml(1,'~',3)--+(观察页面是否有类似XPATH syntax error:'~'出现,有则为报错注入)
problem:为什么用' and updataxml(1,concat(0x7e,(select password from users limit 0,1)))--+,同时改变为limit 1,1和limit 2,1来翻页查看,而不是' and updatexml(1,(select group_concat(password) from users),1)--+
答:updatexml()和extractvalue()函数最多只能显示32个字符,而group_concat()为聚合函数,不能和limit联用,也就是说当数据过多时,只能显示部分数据,而concat配合limit可以翻页查看所有的数据(可以在爆字段内容之前先用select count(*) from 表名来判断有多少行)
(下面以‘为例,当然也可以为"等等,目的是为了让其报错)
3.2输入’不会报错,但是页面有变化,则为布尔盲注,我们就要让前面那个条件为真,通过不断改变payload来得到信息(通过页面是否变化来判断payload的真伪),例如:admin' and select length(database())=7#,通过改变7处的值直到页面变化,就可以获得数据库的长度,注意这里用and,并且admin是已知为真
3.3当输入'时没有报错,并且页面没有变化,但是响应时间变化了,说明数据库与其交互了,有时间盲注(这和xss是十分相似的,xss也是注入到代码中,与代码交互了,alert()的注入弹窗就相当于这里的页面变化,响应时间的变化)
二、寻找闭合方式,因为要使sql语法正确,才可以执行后面的payload,所以这一步的目的在于使异常(报错)的页面变成正常
这一步就没什么好说的了,就是字符型要注意用上注释符(--+)(--)(#)
三、接下来就是判断列数了(当用union查询时)
1.使用union查询实际上不仅要列数对应,还应该数据类型兼容(不是相同),但这与数据库的检查也有关系,mysql,postgresql,sqlite可以隐式把数字转化为字符串,所以可以用' union select 1,2,3--+来判断,
但是对于oracle检查严格,这时就可以用NULL来判断了(NULL可以转换成任意数据类型),即 ' union select NULL,NULL,NULL,--(所以对于数据库类型未知的推荐用NULL)
四、接下来就是确定回显位了
' union select 'a',NULL,NULL--
‘ union select NULL,’a‘,NULL--
’ union select NULL,NULL,'a'--
五、确定了回显位,就十分简单了,网上一堆教程,接下来介绍有些绕过技巧(大小写,双写就不说了)
1.例如select关键字被过滤了,在mysql中可以用CHAR(ASCII码),即CHAR(83)||CHAR(69)||CHAR(76)||CHAR(69)||CHAR(67)||CHAR(84),其中||为连接符,
当然也可以十六进制编码
2.空格过滤:
2.1可以用/**/来代替空格,如果要求必须包含某些关键字(如foo)才可以绕过,直接在注释中插入/*foo*/
2.2括号代替空格
union select 1,2==union(select(1),(2))
2.3url编码代替空格
%0a(换行符)、%0b(垂直制表符)、%0c(换页符)、%0D(回车符)、%09(水平指标符)
3.'-0-'绕过(当WAF过滤了or,and,union等等)(注意:这个技巧在MySQL 8.0+可能失效 -- 因为MySQL 8.0增强了类型安全)
后端代码:select * from users where username='xxx';
我们可以输入admin'-0-',代码变为 select * from users where username='admin'-0-'',看成'admin'-0的运算
由sql转换规则可知'admin'=0,然后就是0-'',可知''转化为0结果就是0-0=0,于是就变成了username=0,这就很有用了
,username=0回和所有用户名比较,而且比较时用户名会被转化为数字,'admin'=0为真,'husao'=0为真
'0aaa'=0为真,也就是说,所有以字符串开头的用户名或者前导0加上字母的与username=0匹配都会返回true
(select "007";结果为7是不行的 )
注:类似的还有'+0+', '*1' , admin'e'0 (e为科学计数法)
4.mysql的行内注释:/*!关键字*/(不加版本号默认执行)
4.1如果过滤了user()
select/*!user*/()==select user()
同理 ' or 1=1--+ 等效于' or 1/*!=*/1--+
4.2还有一个有趣点,如果一个注释以!开头,紧接着是数据库版本字符串,只要数据库版本>=那个字符串,程序就会把注释内容识别为sql语句,否则不执行
例如:/*!32302 and 1=0*/只要mysql版本高于3.23.02,注入上面的语句会使select的where语句为假,主要用来判断版本号
注意:/*!sele*/*!ct/ username from users;这是错误的,注释不能嵌套
以上仅为个人观点,可能会有错误的地方,希望可以指出,总之,网安之路,妙趣横生,后续还有有趣的内容会补充的