软件安全实验——lab8(SQL注入)(下)(旧虚拟机SEEDUbuntu12.04版本实验)

Posted 大灬白

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了软件安全实验——lab8(SQL注入)(下)(旧虚拟机SEEDUbuntu12.04版本实验)相关的知识,希望对你有一定的参考价值。

2、实验室环境

您需要为这个实验室使用我们提供的虚拟机映像。支持这个实验室的虚拟机映像的名称是SEEDUbuntu12.04.zip。如果您碰巧有我们预构建的VM镜像的老版本,您需要下载最新的版本,因为老版本不支持本实验室。请访问我们的SEED页面e (http://www.cis.syr.edu/˜wedu/seed/)获取SEEDUbuntu VM镜像。

2.1环境配置

在这个实验中,我们需要三个工具,它们应该安装在提供的SEEDubuntu VM镜像中:(1)Firefox web浏览器,(2)Apache web服务器,(3)Employee Management web应用程序。对于浏览器,我们需要使用Firefox的LiveHTTPHeaders 扩展来检查HTTP请求和响应。提供给您的预构建的SEEDUbunu VM映像已经安装了带有所需扩展的Firefox web浏览器。Employee Management应用程序没有安装在VM中,但我们稍后将向您展示如何安装它。
启动Apache服务器。Apache web服务器也包含在预构建的Ubuntu镜像中,并且是默认启动的。手动启动web服务器,命令如下:
% sudo service apache2 start
配置DNS。我们已经为这个实验室配置了以下URL。要访问这个URL,需要先启动Apache 服务器:
URL: http://www.SEEDLabSQLInjection.com
文件夹: /var/www/SQLInjection/
上面的URL只能从虚拟机内部访问,因为我们已经修改了/etc/hosts文件,将每个URL的域名映射到虚拟机的本地IP地址(127.0.0.1)。您可以使用/etc/hosts将任何域名映射到特定的IP地址。例如,在/etc/hosts中添加如下条目,将http://www.example.com映射到本地IP地址:

127.0.0.1    www.example.com

如果您的web服务器和浏览器运行在两台不同的机器上,您需要修改/etc/hosts在浏览器的机器上,相应地将这些域名映射到网络服务器的IP地址,而不是127.0.0.1。

配置Apache服务器。在我们预构建的VM映像中,我们使用Apache服务器托管所有的web实验室使用的站点。Apache中基于名称的虚拟主机功能可以用于托管多个web站点(或url)。在“/etc/apache2/sites-available”目录下创建一个名为default的配置文件包含了必要的配置指令:
1.指令“NameVirtualHost *”指示web服务器使用机器中的所有IP地址(有些机器可能有多个IP地址)。
2.每个网站都有一个VirtualHost块,它指定网站的URL和包含网站源的文件系统中的目录。例如,配置一个URL为http://www.example1.com的网站,源文件位于/var/www/Example_1/目录下,和配置URL为http://www.example2.com的网站,源代码在目录/var/www/Example_2/,我们可以使用以下块:

<VirtualHost *>
ServerName http://www.example1.com
DocumentRoot /var/www/Example_1/
</VirtualHost>
<VirtualHost *>
ServerName http://www.example2.com
DocumentRoot /var/www/Example_2/
</VirtualHost>

您可以通过访问上述目录中的源代码来修改 web应用程序。例如,使用上面的配置,可以通过修改目录/var/wwwl example_1中的源来修改web应用程序 http://www.example1.com.

2.2关闭对策

php提供了一种自动防御SQL注入攻击的机制。这个方法被称为魔术引用。让我们关掉这个保护。

1. 打开/etc/php5/apache2/php.ini.
2. 找到这一行: magic_quotes_gpc = On.
3. 把它改成这样: magic_quotes_gpc = Off.
4. 重启apache服务通过"sudo service apache2 restart".

重启apache2服务器

sudo service apache2 restart

2.3通过修补已有虚拟机,添加Web应用

我们为本实验室开发了一个简单的员工管理 web应用程序,该web应用程序用于存储员工个人信息。我们己经为这个应用程序创建了几个员工帐户。要查看所有员工的账号信息,可以以管理员身份登录www.SEEDLabSQLInjection.com(员工ID为99999)。
您从我们的课程网站下载的SEEDUbuntu12.04图片不包括这个web应用程序。你需要为这个实验打补丁。你可以从我们的课程网站下载补丁文件patch.tar.gz。该文件包括 web应用程序和一个脚本,该脚本将安装本实验室所需的所有文件。将 patch.tar.g放在任意文件夹中,解压缩,并运行一个名为bootstrap.sh的脚本。VM现在将为这个实验室做好准备。

$ tar -zxvf ./patch.tar.gz
$ cd patch
$ chmod a+x bootstrap.sh
$ ./bootstrap.sh

sh脚本在我们现有的SEEDUbuntu VM中创建一个名为Users的新数据库,将数据加载到数据库中,设置虚拟主机URL,最后修改本地DNS配置。如果您有兴趣手动进行设置,请参阅附录部分了解详细信息。

解压缩补丁,运行脚本:

3、实验室的任务

我们创建了一个web应用程序。并将其托管在www.SEEDLabsQLInjection.com上。这个web应用程序是一个简单的员工管理应用程序。员工可以通过这个 web应用程序在数据库中获取信息查看和更新他们的个人信息。在这个 web应用程序中主要有两种角色:管理员是一个特权角色,可以管理每个员工的个人信息;员工为正常角色,可以查看或更新自己的个人资料。所有员工信息如下表所示。

3.1任务1:mysql控制台

本任务的目标是通过使用提供的数据库来熟悉SQL命令。我们已经创建了一个名为Users 的数据库,其中包含一个名为credental的表;该表存储每个员工的个人信息(例如eid、密码、工资、ssn等)。管理员可以修改所有员工的个人信息,但每个员工只能修改自己的个人信息。在本任务中,您需要使用数据库来熟悉SQL查询。
MysQL是一个开源的关系数据库管理系统。我们已经在_SEEDubuntu虚拟机镜像中安装了MysQL。用户名为root,密码为sedubuntu。请使用以下命令登录MySQL控制台:

$ mysql -u root -pseedubuntu

登录后,可以创建新的数据库或加载现有数据库。因为我们已经为您创建了Users 数据库,您只需要使用以下命令加载这个现有的数据库:

mysql> use Users;

要显示Users 数据库中有哪些表,可以使用以下命令打印所选数据库的所有表。

mysql> show tables;

运行以上命令后,需要使用SQL命令打印员工Alice的所有配置文件信息。请提供你的结果的截图。
//进入MYSQL数据库,seedubuntu是mysql的密码

$ mysql -u root -p seedubuntu 

//显示数据库

show databases;    


//使用数据库

mysql> use Users;   

//显示表

mysql> show tables;  

打印雇员Alice的所有配置文件信息

3.2任务2:针对SELECT语句的SQL注入攻击

SQL注入基本上是一种技术,攻击者可以通过它执行自己的恶意SQL语句,通常称为恶意负载。通过恶意SQL语句,攻击者可以窃取受害者数据库中的信息;更糟糕的是,他们可能能够对数据库进行更改。我们的员工管理web应用程序有SQL注入漏洞,这模仿了开发人员经常犯的错误。
您可以登录我们的 web 应用程序的入口页面 www.SEEDLabsQLlnjection.com,在那里您将被要求提供员工D和密码登录。登录页面显示如图1所示。身份验证基于员工ID和密码,因此只有知道自己ID和密码的员工才能查看/更新个人信息。作为攻击者,您的任务是在不知道任何员工的凭证的情况下登录应用程序。

为了帮助您开始这个任务,我们将解释如何在我们的 web应用程序中实现身份验证。PHP代码unsafe_credential.php位于/var/www/SQLInjection目录下,用于进行用户身份验证。下面的代码片段展示了如何对用户进行身份验证。

$conn = getDB();
$sql = "SELECT id, name, eid, salary, birth, ssn,
phonenumber, address, email, nickname, Password
FROM credential
WHERE eid= ’$input_eid’ and password=’$input_pwd’";
$result = $conn->query($sql))
// The following is psuedo code
if(name==’admin’){
return All employees information.
} else if(name!=NULL){
return employee information.
} else {
authentication fails.
}

上面的SQL语句从凭据表中选择个人员工信息,如 id、姓名、工资,ssn等。变量 input_eid和input_pwd保存用户在登录页面中键入的字符串。基本上,程序检查是否有任何记录与员工ID和密码;如果匹配,则成功地对用户进行身份验证,并给出相应的员工信息。如果不匹配,则认证失败。

任务2.1

·任务2.1:来自网页的 SQL注入攻击。您的任务是以管理员的身份从登录页面登录到web应用,可以看到所有员工的信息。我们假设您知道管理员的帐户名,即admin,但不知道ID或密码。您需要决定在Employee ID和Password字段中键入什么才能成功进行攻击。
SELECT语句的SQL注入攻击

(1)在不知道任何员工凭证的情况下,登陆应用程序

在雇员ID中输入 'or TRUE or ’

成功登陆第一个用户Alice的账户:

查看验证登陆的代码unsafe_credential.php:

所以我们的输入’or TRUE or '传递给SQL语句之后就是:

"SELECT id, name, eid, salary, birth, ssn, phoneNumber, address, email,nickname,Password 
           FROM credential 
           WHERE eid= ''or TRUE or '' and Password=''";

这样所有的账户都满足这个查询条件,都会返回到array()数组中,只是后来转换成json数据格式之后,只提取了json的第一条数据也就是Alice返回到前端页面登录:

(2)隔断的方式登录admin账户

知道admin账户名就更简单了,输入:’ or name = ‘admin’ or ’

所以我们的输入’ or name = ‘admin’ or 传递给SQL语句之后就是:

"SELECT id, name, eid, salary, birth, ssn, phoneNumber, address, email,nickname,Password 
           FROM credential 
           WHERE eid= '' or name = 'admin' or '' and Password=''";

这样就会返回admin账户的查询结果

(3)#注释代码的方式登录admin账户

另外,我们也可以通过使用#把后面的代码注释掉的方式来登录admin账户,输入:’ or name = ‘admin’ #

所以我们的输入’ or name = ‘admin’ #传递给SQL语句之后就是:

"SELECT id, name, eid, salary, birth, ssn, phoneNumber, address, email,nickname,Password 
           FROM credential 
           WHERE eid= '' or name = 'admin' #' and Password=''";

#会把后面的’ and Password=’’";给注释掉,也能给我们返回admin账户的查询结果

任务2.2

·任务2.2:命令行 SQL注入攻击。你的任务是重复任务2.1,但你不需要使用网页。您可以使用命令行工具,例如 curl,它可以发送HTTP请求。值得一提的是,如果你想在HTTP请求中包含多个参数,你需要把URL和参数放在一对单引号之间;否则,用于分隔参数的特殊字符(如&)将由 shell程序解释,从而改变命令的含义。下面的例子展示了如何发送一个HTTP GET请求到我们的 web应用程序,带有两个参数(SUID 和Password ):
curl ’www.SeedLabSQLInjection.com/index.php?SUID=10000&Password=111’
如果需要在SUID和 Password字段中包含特殊字符,则需要正确地对它们进行编码,否则它们可能会改变请求的含义。如果你想在这些字段中包含单引号,你应该使用%27;如果你想包含空格,你应该使用%20。在这个任务中,您确实需要在使用curl发送请求时处理HTTP编码。

请求登陆任务2中的没有任何提示信息的账户:

curl http://www.seedlabsqlinjection.com/unsafe_credential.php?EID=%27or+TRUE+or%27&Password=

请求登陆任务2.1中的admin管理员的账户:curl 'http://www.seedlabsqlinjection.com/unsafe_credential.php?EID=%27+or+EID%3D%28select+EID+from+credential+where+Name+%3D+%27admin%27%29+or+EID%3D%27&Password=%92or+TRUE+or%92'

成功返回admin账户的数据包。

任务2.3

·任务2.3:添加一个新的SQL语句。在以上两种攻击中,我们只能
从数据库中窃取信息;如果我们可以在登录页面使用相同的漏洞来修改数据库,那就更好了。一种想法是使用sQL注入攻击将一条SQL语句变成两条,第二条语句是更新或删除语句。在 SQL中,两个SQL语句之间用分号()隔开。请描述如何使用登录页面让服务器运行两条SQL语句。尝试从数据库中删除一条记录,并描述您的观察结果。

最开始数据库中的数据:
select * from credential

使用查询select语句加更新update语句:select * from credential where EID = ''or TRUE;update credential set Salary = ‘55555’ where Name = ‘Alice’;

再看数据库中的数据,发现Alice的薪水已经被改成了55555:

接下来在登录页面中使用相同的漏洞修改数据库,Employee ID和Password框都可以作为攻击的点,输入:'or TRUE;update credential set Salary = ‘55555’ where Name = ‘Alice’;

结果失败了,因为在MySQL中实现了一种特殊的保护机制mysq_query,它不允许一次提交多个请求,导致我们两个连续的请求就会报错。
在MYSQL中,线上容易不小心UPDATE,DELETE的时候,无加限制条件,从而引起灾难。所以MYSQL有开关可以防止误操作,一定程度上减少发生机会。可以设置:sql_safe_updates。
其中:当sql_safe_updates设置为1时,update:要有where,并查询条件必须使用为索引字段,或者使用limit,或者两个条件同时存在,才能正常执行。delete:where条件中带有索引字段可删除,where中查询条件不是索引,得必须有limit。主要是防止update和deLete没有使用索引导致变更及删除大量数据。参数默认值为0。

3.3任务3:针对UPDATE语句的SQL注入攻击

如果UPDATE语句出现SQL注入漏洞,则危害会更大,因为攻击者可以利用该漏洞修改数据库。在我们的Employee Management应用程序中,有一个Edit Profile页面(图2),允许员工更新他们的 Profile信息,包括昵称、电子邮件、地址、电话号码和密码。要进入这个页面,员工需要先登录。
当员工通过Edit_Profile页面更新他们的信息时,将执行以下SQL UPDATE查询。在unsafe_edit.php文件中实现的PHP代码用于更新员工的概要信息。PHP文件位于/var/www/SQLInjection目录下。

$conn = getDB();
$sql = "UPDATE credential SET nickname=’$nickname’,
email=’$email’,
address=’$address’,
phonenumber=’$phonenumber’,
Password=’$pwd’
WHERE id= ’$input_id’ ";
$conn->query($sql))

任务3.1

·任务3.1:SQL注入攻击 UPDATE语句-修改工资。如Edit Profile页面所示,员工只能更新昵称、电子邮件、地址、电话号码和密码;他们无权改变工资。只有管理员可以更改工资。如果你是一个恶意的员工(比如Alice),你在这个任务中的目标是通过编辑个人资料页面增加自己的工资。我们假设您确实知道工资存储在一个名为salary 的列中。
假设我是Alice,从前面我们知道了她的Employee ID: 10000 salary: 20000 birth: 9/20 ssn: 10211002 :

知不知道她的密码也没关系了,
输入10000’ #

或者输入’ or name = ‘Alice’ or ’

都能登上Alice账户:

点击Edit Profile

输入:Alice1’, Salary='1234567

返回查看

Nick Name被改成了Alice1,Salary被改成了1234567
查看unsafe_edit.php文件源代码:

所以我们的输入拼接成的SQL语句就是

sql = "UPDATE credential SET nickname='Alice1', Salary='1234567',email='',address='',PhoneNumber='' where ID=$input_id;";

从而把Nick Name改成了Alice1,Salary改成了1234567。

任务3.2

·任务3.2:SQL注入攻击UPDATE语句-修改其他人的密码。使用上述UPDATE语句中的相同漏洞,恶意员工也可以更改其他人的数据。此任务的目标是修改另一名员工的密码,然后演示您可以使用新密码成功登录到受害者的帐户。这里的假设是,你已经知道你想攻击的员工(例如Ryan)的名字。这里值得一提的是,数据库存储的是密码的散列值,而不是明文密码字符串。您可以再次查看unsafe_edit.php 代码,以了解密码是如何存储的。使用SHA1哈希函数生成密码的哈希值。
为了确保你的注入字符串不包含任何语法错误,你可以在启动真正的攻击我们的 web应用程序之前在MysQL控制台测试你的注入字符串。
密码在数据库中是用SHA1哈希函数生成密码的哈希值,在unsafe_edit.php 代码中将$input_pwd转换sha1哈希值之后再进行比较:

所以我们先对一个密码1234取sha1哈希值得到7110eda4d09e062aa5e4a390b0a572ac0d2c0220:

输入:
Alice1’,Password=‘7110eda4d09e062aa5e4a390b0a572ac0d2c0220’ where name = ‘Ryan’#

数据库中的值已经被修改了:

我们在登录页面输入Employee ID:30000和Password:1234

成功登录Ryan账户:

3.4任务4:对策-准备声明

SQL注入漏洞的根本问题是无法将代码与数据分离。当构造SQL语句时,程序(例如PHP程序)知道哪一部分是数据,哪一部分是代码。不幸的是,当SQL语句被发送到数据库时,边界已经消失了;属性设置的初始边界可能与SQL解释器看到的边界不同

开发人员。要解决这个问题,确保边界视图在服务器端代码和数据库中是一致的是很重要的。最安全的方法是使用预备语句。
要理解预准备语句如何防止 SQL注入,我们需要了解当SQL服务器接收到查询时会发生什么。图3显示了查询如何执行的高级工作流。在编译步骤中,查询首先经过解析和规范化阶段,在此阶段将根据语法和语义检查查询。下一阶段是编译阶段,关键字(例如SELECT、FROM、UPDATE等)被转换成机器可以理解的格式。基本上,在这个阶段,查询是解释的。在查询优化阶段,将考虑执行查询的不同计划的数量,从中选择最佳的优化计划。选择的计划存储在缓存中,因此每当下一个查询进入时,它将根据缓存中的内容进行检查;如果它已经存在于缓存中,则解析、编译和查询优化阶段将被跳过。然后,编译后的查询被传递到实际执行的执行阶段。
预编译语句出现在编译之后但在执行步骤之前。预编译语句将经过编译步骤,并转换为带有空占位符的预编译查询。要运行这个预编译的查询,需要提供数据,但这些数据不会经过编译步骤;相反,它们被直接插入到预编译的查询中,并发送到执行引擎。因此,即使数据中有sQL代码,不经过编译步骤,代码也将被简单地视为数据的一部分,没有任何特殊含义。预处理语句就是这样防止SQL注入攻击的。
下面是一个如何用PHP编写准备语句的示例。在下面的示例中,我们使用了SELECT 语句。我们将展示如何使用预处理语句重写容易受到SQL注入攻击的代码。

$conn = getDB();
$sql = "SELECT name, local, gender
FROM USER_TABLE
WHERE id = $id AND password =’$pwd’ ";
$result = $conn->query($sql))

以上代码容易受到SQL注入攻击。它可以被改写为以下内容

$conn = getDB();
$stmt = $conn->prepare("SELECT name, local, gender
FROM USER_TABLE
WHERE id = ? and password = ? ");
// Bind parameters to the query
$stmt->bind_param("is", $id, $pwd);
$stmt->execute();
$stmt->bind_result($bind_name, $bind_local, $bind_gender);
$stmt->fetch();

使用预处理语句机制,我们将向数据库发送SQL语句的过程分为两个步骤。第一步是只发送代码部分,即没有实际数据的SQL语句。这是准备步骤。正如我们从上面的代码片段中所看到的,实际数据被问号(?)所代替。在这一步之后,我们使用bind_param()将数据发送到数据库。数据库将只将这一步中发送的所有内容视为数据,而不再视为代码。它将数据绑定到预处理语句的相应问号。在bind_sparam()方法中,第一个参数"is"表示参数的类型:"i"表示 i d 中 的 数 据 为 整 数 类 型 , " s " 表 示 id中的数据为整数类型,"s"表示 id"s"pwd中的数据为字符串类型。
对于此任务,请使用准备语句机制修复您在前一个任务中利用的SQL注入漏洞。然后,检查是否仍然可以利用该漏洞。

原来有SQL注入漏洞的代码:

以上代码容易受到SQL注入攻击。它可以被改写为以下内容:

  /* start make change for prepared statement */
   $sql = "SELECT id, name, eid, salary, birth, ssn, phoneNumber, address, email,nickname,Password 
           FROM credential 
           WHERE eid=  ?  and Password= ? ";
   if (!$stmt = $conn->prepare($sql)) {
       die('There was an error running the query [' . $conn->error . ']\\n');
   }
	/* bind parameters for markers */
	$stmt->bind_param("is", $input_eid, $input_pwd);
	/* execute query */
	$stmt->execute();
	/* bind result variables */
	$stmt->bind_result($id, $name, $eid,$salary,$birth,$ssn,$phoneNumber,$address,$email,$nickname,$pwd);	
	/* fetch value */
	$stmt->fetch();
	……
$stmt->close();

修改之后的完整的代码文件unsafe_credential.php:

<!-- 
SEED Lab: SQL Injection Education Web plateform
Author: Kailiang Ying
Email: kying@syr.edu
-->

<!DOCTYPE html>
<html>
<body>

<!-- link to ccs-->
<link href="style_home.css" type="text/css" rel="stylesheet">

<div class=wrapperR>
<p>
<button onclick="location.href = 'logoff.php';" id="logoffBtn" >LOG OFF</button>
</p>
</div>


<?php
   $input_eid = $_GET['EID'];
   $input_pwd = $_GET['Password']; 
   $input_pwd = sha1($input_pwd);

   // check if it has exist login session
   session_start(); 
   if($input_eid=="" and $input_pwd==sha1("") and $_SESSION['name']!="" and $_SESSION['pwd']!=""){
      $input_eid = $_SESSION['eid'];
      $input_pwd = $_SESSION['pwd']; 
   } 
   
   $conn = getDB();

   /* start make change for prepared statement */
   $sql = "SELECT id, name, eid, salary, birth, ssn, phoneNumber, address, email,nickname,Password 
           FROM credential 
           WHERE eid=  ?  and Password= ? ";
   if (!$stmt = $conn->prepare($sql)) {
       die('There was an error running the query [' . $conn->error . ']\\n');
   }
	/* bind parameters for markers */
	$stmt->bind_param("is", $input_eid, $input_pwd);
	/* execute query */
	$stmt->execute();
	/* bind result variables */
	$stmt->bind_result($id, $name, $eid,$salary,$birth,$ssn,$phoneNumber,$address,$email,$nickname,$pwd);	
	/* fetch value */
	$stmt->fetch();

   if($id!=""){
   	drawLayout($id,$name,$eid,$salary,$birth,$ssn,$pwd,$nickname,$email,$address,$phoneNumber); 
   }else{
	echo "The account information your provide does not exist\\n"; 
	return;
   }
   /* end change for prepared statement */ 
	 $stmt->close();
   $conn->close();	

function getDB() {
   $dbhost="localhost";
   $dbuser="root";
   $dbpass="seedubuntu";
   $dbname="Users";


   // Create a DB connection
   $conn = new mysqli计算机网络安全 实验三 SQL注入攻击

软件安全实验——pre8(SQL注入预习)

安全实验室中的 SQL 注入 - 语法错误?

如何安全地进行SQL注入测试

20155201 网络攻防技术 实验九 Web安全基础

操作系统ucore lab8实验报告