软件安全seed实验第一章、第二章


Task3:EnvironmentVariablesand execve()

问题描述:
在该任务中,我们研究当通过execve()执行新程序时环境变量如何受到影响。函数execve()调用系统调用来加载新的命令并执行它。该函数不会返回,也没有新的进程被创建。相反,调用进程的文本,数据,bss和堆栈被加载的程序覆盖。基本上,execve()函数在调用进程内运行新的程序。我们对环境变量会发生什么很感兴趣,他们是否自动继承了新程序?

实验过程:
Step1:
编译并运行以下程序。描述观察到的实验结果。该程序简单地调用了/usr/bin/env,该系统调用能够打印出当前进程的环境变量。

在这里插入图片描述
我们对程序进行编译后运行,发现并没有任何输出,这是因为我们通过execve()系统调用了一个新的程序,该新程序不是在原进程的子进程中运行,进程的内存将被新程序的数据所覆盖,新程序不会继承原进程的数据,所以调用execve()时需要我们显示地传递环境变量,step1中我们并没有在execve()中传递任何环境变量,所以调用程序/usr/bin/env不会打印任何东西。

Step2:
改变execve()函数的参数,描述你观察到的结果。
在这里插入图片描述
在step2我们给execve显示地传递了environ这一个全局变量,该变量指向环境变量数组,这时新的程序就得到了当前进程的所有环境变量。这次执行程序/usr/bin/env就会打印出原进程的环境变量。

Step3:
请得出关于新程序如何获取其环境变量的结论。
结论:通过调用execve()函数来进行系统调用运行一个新程序时,由于新进程不会继承原进程的数据,需要我们显示地传递环境变量。

Task4:Environment Variables and system()

问题描述:
在该任务中,我们研究当通过system()执行新程序时环境变量将如何受到影响。system()函数被用于执行命令,但是不像execve()那样直接执行一个命令。system()函数实际上执行的是如下命令:“/bin/sh -c command”,例如执行“/bin/sh”,即请求一个shell来执行该命令。如果你看system()函数的实现,你会看到它使用execl()执行/bin/sh;excel()调用execve(),把环境变量数组传递给它。因此,使用system(),调用进程的环境变量被传递给新的程序/bin/sh。请编译并运行以下程序来验证这一点。
实验过程:
在这里插入图片描述
通过man system,我们可以看到在执行system(“/usr/bin/env”)时,实际上是通过调用“/bin/sh –c command”命令来执行command的。

在这里插入图片描述
我们执行/bin/sh –c /usr/bin/env可以发现得到了同样的结果,因此,我们得出结论使用system(),调用进程的环境变量被传递给新的程序/bin/sh。
在这里插入图片描述

Task5:EnvironmentVariableand Set-UID Programs

问题描述:
Set-UID在unix操作系统中是一种重要的安全机制。当一个Set-UID程序运行时,它会获得程序所有者的特权。例如,如果程序所有者是root用户,那么任何人运行该程序时,该程序都会获得root用户的特权。Set-UID允许我们做许多有趣的事情,但是当它运行时,会升级用户的权限,会带来很大的风险。尽管Set-UID程序的行为是由代码逻辑决定的,而不是用户,用户能够通过环境变量来影响行为。为了理解Set-UID程序是如何被影响的,让我们首先指出哪些环境变量是否由用户进程的Set-UID程序的进程继承。

实验过程:
Step1: 写一个能够输出当前进程的所有环境变量的程序。
编译后运行如下图,能打印出当前进程的所有环境变量。
在这里插入图片描述
Step2: 编译以上程序,将其权限改为root权限,使其成为一个Set-UID程序。在这里插入图片描述

Step3: 使用一般用户登录终端,使用export命令设置如下环境变量:PATH、 LD_LIBRARY_PATH 、ANY_NAME。这些环境变量被设置在终端进程中。运行该Set-UID程序。在终端输入程序的名字后,终端会建立一个子进程,并用该子进程去运行程序。请检查上一步骤中设置的环境变量是否在子进程的shell中。描述观察到的结果。
导入环境变量:
在这里插入图片描述
运行程序task5,发现导入的新环境变量PATH:/usr/local和QJL=/usr/local都在子进程的shell中,但是LD_LIBRARY_PATH却没有子进程的环境变量列表中。
在这里插入图片描述
在这里插入图片描述
通过查阅我们得知我们调用printf时,动态链接器实现了一些防御策略,当进程的真实用户ID和有效用户ID不一样,或者真实组ID和有效组ID不一样时,进程将忽略LD_PRELOAAD和LD_LIBRARY_PATH环境变量。所以当task5是SET-UID程序时,我们导入的LD_LIBRARY_PATH会被忽略,不会被打印出来。而当task5是普通用户程序时则会打印,如下图task52。这时我们导入LD_LIBRARY_PATH就打印出来了。

在这里插入图片描述

Task6: The PATH Environment Variable and Set-UID Programs

问题描述:
由于调用了shell程序,所以在set-UID程序中调用system()是非常危险的。这是因为shell程序的实际行为可能受到环境变量的影响,如PATH这些环境变量由恶意用户提供。通过改变这些变量,恶意用户可以控制Set-UID程序的行为。在Bash中,您可以通过以下方式更改PATH环境变量(此示例将目录/home/seed添加到PATH环境变量的开头):

$ export PATH=/home/seed:$PATH

以下Set-UID程序应该执行/bin/ls命令。但是,程序员只能使用ls命令的相对路径,而不是绝对路径:

 int main()
   {
          system("ls");
          return 0;
   }

编译上述程序,并将其所有者改为root,将其设置为Set-UID程序。你可以让这个Set-UID程序运行你的代码而不是/bin/ls吗?如果可以,您的代码是否使用root权限运行?描述和解释你的观察。

实验过程:
编译上述程序,并将其所有者改为root,将其设置为Set-UID程序。

在这里插入图片描述
为了防止运行Set-UID程序时自动放弃特权,先去掉Ubuntu16.04的保护机制,然后我们将/bin/sh复制到我们当前文件夹中,然后设置环境变量PATH=.$PATH,运行task6时我们发现该程序没有按照system(“ls”)执行/bin/ls,而是执行了我们的设置的代码/bin/sh,并且可以看到我们可以通过设置的代码执行任何需要root权限的操作。原因是system()函数通过调用shell程序来执行ls,当一个shell程序运行命令时,如果没有提供命令的具体位置,shell将使用PATH环境变量来搜索命令,由于我们导入的PATH=.:$PATH,点号加在PATH列表的最前面,所以会优先搜索当前目录,这使得找到并运行我们当前目录下的ls,而不是/bin/ls。
在这里插入图片描述

Task7: The LD_PRELOAD Environment Variable and Set-UID Programs

问题描述:
在这个任务中,我们研究Set-UID程序如何处理环境变量。一些影响动态链接器的环境变量,包括LD_PRELOAD、LD_LIBRARY_PATH和其他LD_*。一个动态装载器/链接器是操作系统的一部分,用于下载并链接可执行文件运行过程中需要的公共库。Linux中,ld.so或者ld-linux.so是动态加载器/链接器。在那些影响他们行为的环境变量中,LD_LIBRARY_PATH和LD_PRELOAD是该实验涉及到的两个环境变量。在linux中,LD_LIBRARY_PATH是一个冒号分隔的目录集,首先需要在标准的目录集之间搜索库。LD_PRELOAD指定要在所有其他库之前加载的其他用户指定的共享库列表。在这个任务中,我们只研究LD_PRELOAD。

实验过程:
Step1:
首先,我们通过一个正常的程序理解环境变量如何影响动态加载器/链接器的行为。请按照以下步骤执行:

  • 我们新建一个动态链接库。命名下面的代码为mylib.c,该程序基本上覆盖了libc中的sleep函数:
    在这里插入图片描述

  • 用下列命令编译mylib.c: gcc -fPIC -g -c mylib.c gcc -shared -o
    libmylib.so.1.0.1 mylib.o –lc

  • 设置LD_PRELOAD环境变量:
    在这里插入图片描述

  • 编译myprog程序,在链接库libmylib.so.1.0.1的相同目录下:

在这里插入图片描述
Step2:在以下情况中运行myprog程序,观察发生了什么。

  1. 以普通用户的身份运行myprog程序。
    输出 I am not sleeping!
    在这里插入图片描述
  2. 以普通用户运行拥有root权限的myprog程序。
    没有输出。
    在这里插入图片描述
  3. 使myprog成为一个Set-UID 程序,再次设置LD_PRELOAD环境变量,在root账户中运行myprog程序。
    输出I am not sleeping!
    在这里插入图片描述
  4. 使myprog成为一个Set-UID 并属于user1的程序,在user2用户(非root用户)中再次设置LD_PRELOAD环境变量,并运行它。
    没有输出。
    在这里插入图片描述
    Step3:
    观察以上三次程序的执行结果,理解导致他们不同的原因。环境变量起了作用。设计实验证明主要因素,并解释第二步中行为的不同。

结论:
导致他们不同的原因就在于LD_PRELOAD环境变量。LD_PRELOAD环境变量是Unix动态链接库中的一个环境变量,它可以影响程序的运行时的链接,它允许你定义在程序运行前优先加载的动态链接库。这个功能主要是用来有选择性的载入不同动态链接库中的相同函数。在该实验中,mylib.c通过sleep函数,生成了一个libmylib.so.1.0.1链接库。然后将该链接库添加到LD_PRELOAD环境变量上。但是我们调用动态链接器,如果真实用户ID和有效用户ID不一样,或者真实组ID和有效组ID不一样时,进程将忽略LD_PRELOAAD和LD_LIBRARY_PATH环境变量。我们第一次实验中ruid和euid都是seed用户,第三次实验中ruid和euid都是root,所以动态链接器不会忽略我们导入的LD_PRELOAD环境变量,从而能实现我们自己写的sleep函数,打印出“I am not sleeping!”。而第二次或第四次的ruid和euid不同,忽略我们导入的LD_PRELOAD环境变量,执行sleep(1)睡眠操作,因此不会打印出任何东西。

任务八:使用system()和execve()调用外部程序

题目描述:
虽然system()和execve()都可以用于运行新程序,但是如果在特权程序(如Set-UID程序)中使用,则system()非常危险。我们已经看到PATH环境变量是如何影响system()的行为,因为该变量会影响shell的工作原理。execve()没有问题,因为它没有调用shell。调用shell会导致非常危险的后果,而这与环境变量无关。来看下面这种情况:鲍勃为一家审计机构工作,他需要调查一家公司是否有涉嫌欺诈。为了调查目的,鲍勃需要能够读取该公司Unix系统中的所有文件;另一方面,为了保护系统的完整性,鲍勃不能修改任何文件。为了实现这一目标,系统超级用户文斯写了一个特殊的set-root-uid程序(见下文),然后给出了对鲍勃的可执行权限。该程序要求鲍勃在命令行中键入文件名,然后运行/bin/cat显示了指定的文件。由于程序在root权限下运行,它可以显示鲍勃指定的任何文件。然而,由于程序没有写操作的权限,所以文斯非常确定鲍勃不能使用这个特殊的程序来修改任何文件。

步骤一:
编译上面的程序,赋予其root用户权限,并将其变为SET-UID程序。该程序将会使用system()来调用命令。如果你是鲍勃,你能否打破系统的完整性吗?例如,你可以删除不可写文件吗?

步骤一实验过程:
在这里插入图片描述
我们编写完指定的程序并将其命名为task8.c,用gcc –o task8 task8.c编译,查看task8权限,此时只是一个普通的用户程序,经过sudo chown root task8改变程序所属用户之后,再用sudo chmod 4755 task8 设置该程序的Set-UID比特位,此时再次查看task8的权限,我们发现它已经变成一个Set-UID程序了,因此我们可以用该程序查看任何文件,包括那些只有root用户可读的文件。

当我们运行task8 /etc/shadow查看只有root用户才能查看的shadow文件时,系统会告诉我们权限不够,原因是我们在程序中调用system(command)时,实际上是通过/bin/sh –c command来执行command的,所以我们这里调用了shell程序,而又因为我们采用的Ubuntu16.04运用了一个保护机制,该机制会使我们在运行该Set-UID程序时会把euid变成ruid,从而放弃特权,因此会提示我们访问权限不够。当我们用 sudo ln –sf /bin/zsh /bin/sh将/bin/sh链接到一个没有保护机制的zsh的shell上时,我们便成功用task8 /etc/shadow查看到了shadow文件的内容。

到这步,我们能用task8这个Set-UID程序完成指定任务,接下来我们来破坏系统的完整性,比如删除一个文件。这里我们先切换到root用户,并在/root下建立一个只有root用户才能修改的一个文件istest。如下图。
在这里插入图片描述
然后,我们开始攻击,用task8 “aa;/bin/sh”,此时我们发现打开了一个拥有root权限的shell程序,因为在该没有保护机制的shell程序下,一个分号“;”用来分隔这两个命令,所以task8 “aa;/bin/sh”相当于task8 aa 和/bin/sh这两个命令,又由于该Set-UID程序拥有root权限,所以此时我们就得到了一个在root用户下可执行的/bin/sh程序了。这是我们就可以利用该shell程序进入/root目录下去删除只有只有root用户可写的istest文件了。如下图。
在这里插入图片描述
步骤二:
注释掉system(command)语句,并取消注释execve()语句;程序将使用execve()来调用命令。编译程序,并使其成为Set-UID程序。那么在步骤一中的攻击是否仍然有效?
步骤二实验过程:
注释掉system(command)语句,并取消注释execve()语句,重新编译,并赋予其root用户权限。尝试用task8 “aa;/bin/sh”来得到一个shell程序,这时我们发现系统提示我们没有aa;/bin/sh这个文件,这时和我们步骤一中的结果不同了。因为我们用execve()函数代替了system()来执行打开文件的命令,而execve()函数不执行shell程序,而是直接请求操作系统来执行该命令,所以它会把“aa;/bin/sh”当做它传给系统的一个参数而不是指令,所以系统会认为我们要查看的文件时“aa;/bin/sh”而不是文件“aa”,所以会提示没有该文件,因此我们在步骤一中的攻击就失效了。

任务九:权能泄露

题目描述:
遵循最低权限原则,如果不再需要这种特权,Set-UID程序通常会永久放弃其root权限。此外,有时程序需要将其控制权交给用户,在这种情况下,root权限必须被撤销。setuid()系统调用可以用来撤销权限。根据手册,setuid()设置调用进程的有效用户ID。如果调用程序的有效UID是root,真实的UID和保存的set-user-id也被设置。因此,如果一个有有效UID的set-uid程序没有调用setuid(n),则该进程将成为正常进程,其所有的UID都设置为n。当撤销权限的时候,最常见的错误就是权能泄露。这个进程在获得一些特权的时候可能已经获得了一些特权。当特权降级时,如果程序没有清理这个功能,则它们仍然可以由非特权进程访问。换句话说,虽然进程的有效用户ID变为非特权,但是该进程仍具有特权,因为它具有特权能力。

编译以下程序,将其所有者更改为root,并将其设置为Set-UID程序。以普通用户身份运行程序,并描述您所观察到的内容。文件/etc/zzz是否被修改?请解释你的观察过程。

实验过程:
编译了程序,将其所有者更改为root,并将其设置为Set-UID程序。以普通用户身份运行task9程序后,我们用cat /etc/zzz查看文件zzz是否被修改,发现zzz被修改了,文件中加入了Malicious Data。
在这里插入图片描述
我们观察fork()调用部分(源程序该部分代码见下图),fork()创建子进程,子进程返回一个等于0的值,然后执行else语句部分,通过文件描述符向文件zzz中写入了数据Malicious Data,文件zzz就被修改了,然后关闭文件;fork()后父进程得到子进程的进程号,其值大于0,所以执行if语句部分,然后关闭文件。我们虽然不知道子进程和父进程哪一个先执行,但是由于fork后,只要在子进程中的文件zzz还没关闭之前,文件描述符fd依然有效,所以此时依然能将Malicious Data写入文件zzz中。
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值