这个系列会从头详细整理一些关于sql注入的东西。主要以sqli lab为实验。
在url后输入?id=1 可以得到正常输出,就是表中id为1的数据。为了理解方便,我改动了PHP源码使得网页会返回sql语句。
在1之后输入单引号,我们可以看到错误提示。
首先我们用union语句来遍历表的列数。Union语句可以将几个搜索结果合并在一起,但前提是几个结果的列数要一致。如果几个搜索结果的列数不一致就会报错。所以我们可以用union语句来遍历出表的列数:
127.0.0.1/sqli-labs7/Less-1/?id=1' union select 1
输入union语句,我们可以看到数据库报出了语法错误。
为了解决这个问题我们需要把语句之后的limit注释掉。如果直接在url中输入#,#会被转码成其他字符:
我们用%23代替#(#ascii码的hex是23):
select 1的时候可以看到数据库报错说列数不对,所以我们继续尝试。尝试到3的时候页面可以正常返回,这说明这个表有三列:
Limit的作用是返回搜索结果中的前几个。比如说Limit 0,1中,0是offset就是从第0个数开始算。1是返回的数量。所以Limit 0,1就是返回结果中的第一列。比如结果是{1,2,3,4},limit 0,1之后的结果就是1,limit 1,2的结果就是2,3,即跳过第0个数,之后的两个结果。
但是我们在上面这个语句中把limit给注释掉了,同样的语句在mysql中跑应该会得到这样的结果:
为什么网页只有第一行的结果呢?
查看PHP源码:
看到mysqli_fetch_array函数只被调用了一次,这个函数会取出一行数据库查询的结果。所以不管我们的sql语句怎么变,网页只会返回查询结果的第一行。
为了能看到我们输入语句的结果,我们需要让我们的结果作为查询结果的第一行返回。其中一个方法是将第一个查询的结果变成空,比如说输入一个不存在的id:
这样就可以显示之后的查询结果了。
由于我们只能输出两个查询的数据,所以我们必须要将多个查询的结果合并成一条返回。SQL语句中的concat函数。它可以将多个查询合并到一条返回
如果想要在每个结果中间加入分隔符,我们可以用concat_ws( concatenate with separator),
user()返回数据库用户的用户名;
database()返回当前使用的数据库名;
version()是当前数据库的版本
接下来我们要找到这个数据库中其他的表名。要得到这个,我们需要利用information_schema 这个数据库。这是一个系统数据库,里面有当前各个数据库、表、列、用户权限等的信息。转几个常用的表的功能:
注意在information_schema中查询的时候where语句后面不能直接跟英文。要将英文前后加上单引号或者用16进制表示。
将查询语句放到sql注入中执行:
可以看到依然只能返回一个结果。我们可以用limit的offset来遍历所有表名:
但遍历超过范围时,将不会返回查询结果:
这样我们就可以得到所有四个表名。
接着,用colums表我们可以得到users表中的列名:
知道了列名,我们就可以遍历得到admin的密码: