SEEDLab-SQL注入攻击
SEEDLab-SQL注入攻击
SQL注入是一种代码注入技术,它利用了Web应用程序与数据库服务器接口中的漏洞。当Web应用在将用户输入发送到后端数据库服务器之前,没有对输入进行正确检查时,就会产生这种漏洞。
许多Web应用程序会接收用户输入,并使用这些输入来构造SQL查询,从而从数据库中提取信息。Web应用也会使用SQL查询将信息存储到数据库中。这些都是Web应用开发中的常见做法。当SQL查询没有被谨慎构造时,就可能出现SQL注入漏洞。SQL 注入攻击是针对Web应用最常见的攻击之一。
在本实验中,我们对一个名为phpBB的Web应用进行了修改,并禁用了phpBB2实现的一些防护措施。结果是,我们创建了一个存在SQL注入漏洞的phpBB版本。尽管这些修改是人为设计的,但它们反映了许多Web开发者常犯的错误。本实验的目标是让学生找到利用SQL注入漏洞的方法,展示攻击可能造成的危害,并掌握防御此类攻击的技术。
任务1 针对SELECT语句的SQL注入攻击
对于此任务,您将使用虚拟机内通过URL https://www.sqllabmysqlphpbb.com可访问的网络应用程序,这是一个配置了MySQL数据库的phpBB2。 在使用phpBB2之前,系统会要求您登录。身份验证由服务器端的login.php程序实现。该程序会向用户显示一个登录窗口,并要求用户输入其用户名和密码。登录窗口显示如下:
一旦用户输入用户名和密码,login.php程序将使用用户提供的数据来查找它们是否与数据库中任何记录的用户名和用户密码字段匹配。如果存在匹配项,则意味着用户提供了正确的用户名和密码组合,应允许其登录。
与大多数其他网络应用程序一样,PHP程序使用标准SQL语言与其后端数据库交互。在phpBB2中,login.php中构造了以下SQL查询以验证用户身份:
在上述SQL语句中,USERS_TABLE是PHP中的一个宏,它将被替换为实际的用户表名:phpbb_users;\(username是一个变量,用于保存“用户名”文本框中输入的字符串;\)password是一个变量,用于保存“密码”文本框中输入的字符串。用户在这两个文本框中的输入被直接放入SQL查询字符串中。 登录时的SQL注入攻击:上述查询中存在一个SQL注入漏洞。尝试实现目标? • 能否在不知道正确密码的情况下登录他人的账户? • 能否找到一种方法来修改数据库(仍使用上述SQL查询)?例如,您能否向数据库添加一个新账户,或删除一个现有用户账户?显然,上述SQL语句是一个仅用于查询的语句,无法更新数据库。然而,利用SQL注入,您可以将上述语句变成两个语句,其中第二个是更新语句。请尝试此方法,并查看您是否能够成功更新数据库。 事实上,无法更新目标。这是因为MySQL实现了一种特定的防御机制。在报告中,您应该向我们展示您为了修改数据库进行了哪些尝试。您应该找出攻击失败的原因,以及MySQL中的哪种机制阻止了此类攻击。您可以(从互联网上)查找(二手)证据来支持您的结论。然而,一手证据将获得更多分数(运用您自己的创造力来发现一手证据)。如果您确实找到了成功攻击的方法,您将获得加分。

首先,要利用SQL注入,首先需要关闭magic_quotes机制,如图2所示。之后打开目标实验网站,在用户名中输入admin’#,如图3所示。此时,SELECT语句中的条件子句会变为WHERE
username=’admin’# and
…,对密码的判断会被注释掉,只对用户名进行判断,通过这种方式成功登录admin账号。

如果要对用户表中的数据进行修改,那么光靠SELECT语句是无法做到的,还需要构造UPDATE语句,因此利用分号作为分隔符尝试在SELECT后继续运行UPDATE,如图4所示。不幸的是,并没有利用成功,如图5所示。这是由于在MySQL中不允许一次运行两条语句,即使它们用分号隔开。
任务2 针对UPDATE语句的SQL注入攻击
当用户想要在phpBB2中更新其个人资料时,他们可以点击“个人资料“链接,然后填写表单以更新个人资料信息。用户将更新请求发送到服务器后,系统会在include/usercp_register.php中构造一个UPDATE SQL语句。此语句的目的是修改phpbb_users表中当前用户的个人资料信息。此SQL语句中存在一个SQL注入漏洞。请找到此漏洞,然后利用它完成以下操作:
- 在不获取他人密码的情况下更改其个人资料。例如,如果您当前以Alice的身份登录,您的目标就是利用此漏洞修改Ted的个人资料信息,包括Ted的密码。攻击成功后,应该能够登录Ted的账户。
在任务一中由于收到软件机制的影响,无法利用SQL注入修改数据表中的数据。但“天无绝人之路”,在修改profile功能的PHP源码中发现了执行UPDATE语句的代码,因此可以在在表单填写相关语句实现注入。但是在邮箱等表项中存在输入数据的检查,所以难以完成SQL注入,但是通过Tamper
Data插件,可以在POST请求发送出去之前对内容进行更改,这样就可以修改如language等因为输入受限而没有检查机制的内容了。
注入内容:%27%2C+user_password%3Dmd5%28%27123%27%29+WHER
E+username%3D%27ted%27+%23+
如图7所示,构造好SQL注入内容(转换成URL格式)后,将其添加在dataformat字串的末尾,成功屏蔽了多余的检查机制,并将Ted的密码修改为123。

如图8、9所示,攻击成功。
任务3 防御措施
SQL注入漏洞的根本问题在于未能将代码与数据分离。在构造SQL语句时,程序(例如PHP程序)知道哪部分是数据,哪部分是代码。遗憾的是,当SQL语句被发送到数据库时,这个边界消失了;如果代码被注入到数据字段中,SQL解释器看到的边界可能与原始边界不同。为了解决这个问题,确保服务器端代码和数据库中看到的边界视图一致至关重要。有多种方法可以实现这个目标。
任务 3.1: 使用magic_quotes_gpc转义特殊字符。
在PHP代码中,如果一个数据变量是字符串类型,它需要用一对单引号符号(’)括起来。例如,在上面列出的SQL查询中,我们看到username = ’\(username'。包围\)username的单引号符号本质上是“试图”将\(username变量中的数据与代码分开。遗憾的是,如果\)username的内容包含任何单引号,这种分离就会失败。因此,我们需要一种机制来告诉数据库,$username中的单引号应被视为数据的一部分,而不是SQL中的特殊字符。我们只需要在单引号前加一个反斜杠(\)即可。
PHP提供了一种机制,可以在单引号(’)、双引号(“)、反斜杠(\)和NULL字符前自动添加反斜杠。如果此选项被启用,来自用户输入的所有这些字符将自动被转义。要启用此选项,请转到/etc/php5/apache2/php.ini,并添加magic_quotes_gpc = On(在提供给您的虚拟机中,此选项已启用)。
请打开/关闭魔术引号机制,并观察它如何帮助进行防护。请注意,从PHP5.3.0版本开始(我们提供的虚拟机中的版本是5.2.6),此功能已被弃用,原因如下: – 可移植性:假设其为开或关会影响可移植性。大多数代码必须使用一个名为 get_magic_quotes_gpc()的函数来检查此项,并据此调整代码。 – 性能与不便:并非所有用户输入都用于SQL查询,因此强制转义所有数据不仅影响性能,而且在某些本不应被转义的数据上会变得令人困扰。
首先在php.ini启动magic_quotes_gpc机制,之后再按照task1中的方式尝试登录,发现无法成功登录,如图10所示。与下文task3.2相同,都是引号被转义导致的。
任务 3.2: 使用addslashes()转义特殊字符。
一个名为addslashes()的PHP函数也可以实现魔术引号的功能。原始phpBB2代码在魔术引号未开启时使用addslashes()来防御SQL注入攻击。请查看/var/www/SQL/SQLLabMysqlPhpbb中的common.php文件(common.php被login.php包含,所以每当login.php执行时它也会被执行)。我们实际上注释掉了phpBB2中的防护代码以使SQL注入成为可能。请通过从以下行中移除“and
FALSE“来重新开启防护,以观察差异(我们添加了“and
False“来绕过这段代码)。请描述此防护方案如何帮助防御您的SQL注入攻击:
if( !get_magic_quotes_gpc() and FALSE ) 移除“and
False“后,如果魔术引号机制被关闭,if块内的防护措施将被执行。(即使php.ini中的魔术引号选项(magic_quotes_gpc)被打开,common.php文件开头的一个语句也在运行时关闭了魔术引号。这是通过使用set_magic_quotes_runtime(0)来实现的。这就是为什么get_magic_quotes_gpc()会返回false的原因。需要注意的是,在运行时开启魔术引号不会影响用户提供的输入(例如那些在\(_GET和\)_POST中的变量)。它只影响其他输入(例如来自文件的输入等)。set_magic_quotes_runtime()函数也从PHP
5.3.0版本开始被弃用了。)为了帮助描述您的观察,您应该打印出SQL查询,并观察该机制如何影响查询。

一种简单的试信息打印的方法是将所有信息打印到一个文件中。例如,服务器端PHP程序可以使用以下代码片段将一个变量的值打印到文件中:
首先将magic_quotes_gpc保护机制关闭,然后打开addslashes,同时在login.php中添加打印输出到文件的代码,如图11所示。重新尝试进行SQL注入攻击,发现攻击失败,检查输出,发现与上文一样,引号被自动转义,无法进行攻击。
任务 3.3: 使用mysql_real_escape_string转义特殊字符。
一种更好的防御SQL注入的数据转义方法是使用数据库特定的转义机制,而不是依靠魔术引号之类的功能。MySQL提供了一种名为mysql_real_escape_string()的转义机制,它会在几个特殊字符前添加反斜杠,包括\x00、\n、\r、`、’、“和\x1A`。请使用此函数来修复前面任务中发现的SQL注入漏洞。在开始此任务前,您应禁用前面任务中描述的其他防护方案。
上文中的防护机制仅针对一些特定的字符,而my_real_escape_string则是对一系列可能造成影响的特殊字符都进行转义,从而达到更好的效果。如图13和15所示,将SQL语句用escape_string处理后相关字符被加上反斜杠无法正常运行。不过这仍旧没有从根本上解决数据与代码混乱的问题。
任务 3.4: 预处理语句(Prepare Statement)。
一种更通用的分离数据和SQL逻辑的方法是明确告诉数据库哪部分是数据部分,哪部分是逻辑部分。为此,MySQL提供了预处理语句机制。
使用预处理语句机制,我们将发送SQL语句到数据库的过程分为两步。第一步是发送代码,即不包含稍后需要插入数据的SQL语句。这是准备步骤。在此步骤之后,我们然后使用bind_param()将数据发送到数据库。数据库将在此步骤中发送的所有内容仅视为数据,而不再是代码。 请使用预处理语句机制修复phpBB2代码中的SQL注入漏洞。在bind_param函数中,第一个参数“si“意味着第一个参数(\(user)是字符串类型,第二个参数(\)age)是整数类型。
使用预处理指令,将会把SQL语句的代码部分进行提前解析,并在之后将数据“传入”解析好的指令中,这样数据就不会被视为指令的一部分,从而根本上解决了代码数据混淆的问题。