竞争条件漏洞实验室

更新日期:2020年7月26日

版权所有©2006-2020杜文良,保留所有权利。

免费用于非商业教育目的。禁止将这些材料用于商业用途。种子项目是由美国国家科学基金会的多项资助资助的。

1 概述

本实验室的学习目标是让学生将从课堂上学到的关于种族条件脆弱性的知识付诸行动,从而获得有关种族条件脆弱性的第一手经验。当多个进程同时访问和操作同一数据时,会出现竞争条件,执行的结果取决于访问发生的特定顺序。如果特权程序存在竞争条件漏洞,攻击者可以运行并行进程与特权程序“竞争”,以改变程序的行为。

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

•竞争条件脆弱性

•粘性符号链接保护

•最低特权原则

阅读和录像。竞赛条件攻击的详细范围可在以下内容中找到:

•种子书《计算机与互联网安全:实践方法》第7章,第2版,杜文良著。详情请参阅https://www.handsonsecurity.net.

•Udemy种子讲座第6节,计算机安全:实践方法,杜文良。详情请参阅https://www.handsonsecurity.net/video.html.

相关主题。还有三个与比赛条件相关的实验室。一个是Dirty COW攻击实验室,它利用操作系统内核中的竞争条件漏洞(种子书第8章介绍了这种攻击)。另外两个是熔毁和幽灵攻击实验室(种子书第13章和第14章)。他们利用CPU内部的竞争条件。这四个实验室全面覆盖了计算机系统不同层次的竞争条件问题,从应用程序、内核到硬件。

实验室环境。这个实验室已经在我们预先构建的Ubuntu 16.04虚拟机上进行了测试,可以从SEED网站下载。

2 实验任务

2.1 初始设置

Ubuntu10.10及更高版本附带了一个针对种族条件攻击的内置保护。此方案通过限制谁可以跟随符号链接来工作。根据文档,“如果跟随者和目录所有者与符号链接所有者不匹配,则无法遵循世界可写粘性目录(例如/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

/* 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");
}

上面的程序是root拥有的Set-UID程序;它将用户输入字符串附加到临时文件/tmp/XYZ的末尾。由于代码以root权限运行,即其有效使用ID为零,因此它可以覆盖任何文件。为了防止自己意外覆盖他人的文件,程序首先检查真实用户ID是否具有文件/tmp/XYZ的访问权限;这就是第①行中access()调用的目的。如果真实的用户ID确实拥有权限,程序将在第②行打开文件,并将用户输入附加到文件中。

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

设置设置UID程序。我们首先编译上面的代码,并将其二进制文件转换为root用户拥有的setuid程序。以下命令可实现此目标:

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

2.3 任务1:选择我们目标

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

root:x:0:0:root:/root:/bin/bash

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

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

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

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

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

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

然后删掉

2.4 任务2.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脚本使用passwdinput作为输入,在循环中运行易受攻击的程序(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文件的所有权。如果此文件的所有者成为root用户,请手动删除此文件,然后重试攻击,直到攻击成功。请在实验室报告中记录这一观察结果。在任务2.B中,我们将解释原因并提供一种改进的攻击方法。

创建攻击程序并编译

创建target.sh脚本文件:


攻击成功。验证:

2.5 任务2.B:改进的攻击方法

在任务2.A中,如果您已经正确地完成了所有操作,但仍然无法成功进行攻击,请检查/tmp/XYZ的所有权。您将发现/tmp/XYZ的所有者已成为root(通常,它应该是seed)。如果发生这种情况,您的攻击将永远不会成功,因为您的攻击程序以种子权限运行,无法再删除或取消链接()。这是因为/tmp文件夹上有一个“粘滞”位,这意味着只有文件的所有者才能删除该文件,即使该文件夹是可写的。

在任务2.A中,我们允许您使用root的权限删除/tmp/XYZ,然后重试攻击。不希望出现的情况是随机发生的,因此通过重复攻击(在root的“帮助”下),您最终将成功完成任务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;
}

任务。请使用此新策略修改您的攻击计划,然后重试您的攻击。如果所有操作都正确,您的攻击应该能够成功。

首先,别忘了删掉passwd里面的test

创建attack.c
编译

攻击并验证成功

2.6 任务3:对策:应用最小特权原则

本实验室中易受攻击程序的基本问题是违反了最小特权原则。程序员知道运行该程序的用户可能太强大,因此他/她引入了access()限制用户的权限。但是,这不是正确的方法。更好的方法是应用最小特权原则;即,如果用户不需要某些特权,则需要禁用该特权。

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

首先,删了passwd中的test,在漏洞程序的main()开头增加四行代码(见书p145)

重新编译,提升权限


攻击不成功

2.7 任务4:对策:使用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)此方案的限制是什么?

注释掉四行代码


重新编译,提升权限

开启保护

攻击失败

(1)此保护方案如何工作?

答:漏洞程序是root权限运行的,/tmp目录的所有者也是root,但攻击者程序不是root,所以攻击者不能使用该链接,否则就会崩溃。

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

答:允许用户建立软连接的情况是 软连接所在目录是全局可读写目录或者软连接的uid与跟从者的uid匹配,又或者目录所有者与软连接所有者匹配

Race_Condition_new相关推荐

最新文章

  1. constrctor,prototype,__proto__
  2. 马斯克脑机接口遭质疑:不是新技术,没体现神经解码进展
  3. POJ3666-Making the Grade【线性dp】
  4. JMeter处理Cookie与Session
  5. 使用IDA Pro动态调试SO文件
  6. linux-2.6内核模块引用计数的实现(try_module_get和module_put)
  7. Compilation Error 解决方案汇集
  8. 前端-requests-flask对应关系 json
  9. ios上传图片 java_iOS ASIFormDataRequest上传图片 前后台代码
  10. 启动Tomcat时报错,一大堆的
  11. 儿童机器人编程入门优先学习什么?
  12. win10下装黑苹果双系统_高手拆换GPD P2 Max无线模块,实现完美黑苹果
  13. 2016年360校招笔试题
  14. python hook微信_GitHub - 15993248973/wechathook: 借助微信hook,拦截修改某些call,填充进我们的Python代码,进行微信公众号文章的爬取...
  15. 如何礼貌的拒绝offer
  16. sonar问题:Font declarations should contain at least one generic font family
  17. 【ffmpeg】创建AAC编码器
  18. c语言汇编输出字符串长度,汇编语言输入一些字符计算其长度
  19. 抖音2018年度数据报告
  20. 颜色的Pantone编码

热门文章

  1. 二叉树后序遍历的非递归算法
  2. [附源码]计算机毕业设计Python基于web的羽毛球管理系统(程序+源码+LW文档)
  3. 简单聊聊煤炭行业的数字化和可持续发展
  4. 关于NETFPGA-10G敬告国内朋友
  5. 经验分享|个人建站服务器选择
  6. linux设置TCP接收缓存,TCP缓存设置及自调节
  7. 施乐服务器显示25,施乐服务器设置
  8. MySQL中的悲观锁和乐观锁
  9. 1988-2019年中国和世界各国对世界农产品贸易数据
  10. 吸引力法则练习的一些小结