1,原理
SQL 注入就是指 Web 应用程序对用户输入的数据合法性没有过滤或者是判断,攻击者可以在Web应用程序中事先定义好的查询语句的结尾上添加额外的SQL语句,在管理员不知情的情况下实现非法操作,以此来实现欺骗数据库服务器执行非授权的任意查询,从而进一步得到相应的数据信息。
我的理解就是构造闭合,使用mysql语句,让后台执行mysql语句并泄露数据库信息
比如我们在传入用户名和密码时,或者最简单的传入id值,后台会有这样一句php
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";
这是sql-labs less1中的sql注入关键语句,其中$id就是我们传入的参数id,如果我们正常传入一个参数,那它就会被正常的包裹起来。
我们在url栏中传入1,那它的sql语句就是
$sql="SELECT * FROM users WHERE id='1' LIMIT 0,1";
这样显然是一个没有错误的sql语句,它会执行正常的功能,但要是我们传入1'会这么样呢
$sql="SELECT * FROM users WHERE id='1'' LIMIT 0,1";
这样传入,明显多了一个',造成了闭合错误,那页面会是这么样呢?
可以看到,闭合错误后,页面会跳出提示我们mysql语句错误,这样也会提醒我们存在sql注入。
例题
这边就以sql-labs的less1作为例题,好好了解每个payload的作用是什么。
在sql注入前,先要了解什么是mysql
mysql就是一个用于存储数据的数据库,而它的结构通常由库,表,字段组成
而知道了结构我们就可以对其逐一的爆破,从库到字段全都可以知道。
1,联合注入
sql-labs的第一题就是联合注入,这是最基本的注入方法,而联合注入分为get和post两种类型。但他们其实都是同一种方法,只是传参的方式不同。
union联合注入得名于union语句,union可以把多个表的数据一起显示,但这也是有条件的。
通常联合注入只适用于有回显的注入,并且union再联合时只能联合字段数一样的表,所以通常还需要知道字段数。(有些地方也叫做列数)
从less-1的源码我们可以知道,存在这样的sql语句
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";
这句sql语句的意思就是
1,select * from users从uers表中,搜索所有字段(*是通配符)
2,where后面跟的过滤条件,从字段中匹配传入的id值,并只选择匹配的记录
3,limit 0,1表示从第0行(第一行),返回查询到所有数据中的第1条数据
那么在了解了基本的sql注入规则后,我们就可以开始注入了。
首先我们要判断字段数(列数)
这需要用到order by,用法是order by + 数字
这个会对某个字段(列)进行排序,如果要排序的列数大于表中存在的列,sql就会报错。
如图传入?id=1' order by 3 --+ 其中--+是注释符,用于省略掉后面的语句,就会如下图所示
$sql="SELECT * FROM users WHERE id='-1' union select 1,2,3 --+' LIMIT 0,1";
这样sql语句就变成了搜索id为1的记录,并对第三个字段进行排序
而当我们将3改为4,就会提示出现位置的字段
所以当前表中的字段是3行
然后就是判断回显位置
这里就要用到union了,我们传入
?id=-1' union select 1,2,3 --+
$sql="SELECT * FROM users WHERE id='-1' union select 1,2,database() --+' LIMIT 0,1";
这里传入-1是为了不然mysql在查找到数据并返回,方便判断回显。
这样就会在users表中搜索id='-1'的记录,同时搜索1,2,3(这三个数会直接拼接到对应的字段中,并会返回1,2,3)(从这里也可以看出当前表中的第一个字段是id,第二个字段是uname,第三个字段是pword)
之前也说过,union联合查询必须满足段数相等,因此我们在3后再加一个4,就会报错,因为sql不知道将4合并到哪一个字段中 查完回显后,我们就可以爆破库名了
暴库名的操作很简单,随意替换1,2,3中任意一个为database()就行了
这里通常选择3,因为不用加括号(database()是mysql中用于查询当前数据库名的函数)
$sql="SELECT * FROM users WHERE id='-1' union select 1,2,database() --+' LIMIT 0,1";
这就就爆出库名了
爆出库名之后就是查表名
传参数
?id=-1' union select 1,2,(select group_concat(table_name) from information_schema.tables where table_schema=database())--+
这里就会用到常用的group_concat(),这个函数用于将字符串链接起来,这里链接的是表名
table_name就是表名的意思,而这个表名从哪里来的呢?
在 MySQL5.0 版本后,MySQL 默认在数据库中存放一个information_schema的数据库,该数据库中包含了当前系统中所有的数据库、表、列、索引、视图等相关的元数据信息,是MySql自身信息元数据的存储库,我们需要记住三个表名,分别是 schemata,tables,columns
所以我们是将数据库中的所有表名都连接在一起,但是这个数据也有范围,即where后面限制的table_schema=database()(当前数据库)。
然后就会回显出当前数据库的所有表名
爆完表名就可以爆字段名(列名)了
传入参数
?id=-1' union select 1,2,group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='users' --+
这里就只是将合并字符串的参数换为column_name(字段名)并限定表名为'users'
这里注意一下,你爆表名就用information_schema.tables
爆字段名就用information_schema.columns
点后面是所有数据库中具体要查询的内容
然后就是爆字段了
之前爆出字段名有id-username-password
传入参数
?id=-1' union select 1,2,(select group_concat(id,"-",username,"-",password) from users)--+
这里group_concat中传入的参数"-"意思是用-将他们链接起来,而且这里的users不用加双引号
总结
至此,一道基本的sql注入就完毕了,至于post类与get一样,只用将参数用post传参即可.
sql注入的程序
查字段-查回显-爆库名-爆表名-爆字段名-爆字段