- Environment Variable and Set-UID Program Lab
- Task 1: Manipulating Environment Variables
- Task 2: Passing Environment Variables from Parent Process to Child Process
- Task 3: Environment Variables and execve()
- Task 4: Environment Variables and system()
- Task 5: Environment Variable and Set-UID Programs
- Task 6: The PATH Environment Variable and Set-UID Programs
- Task 7: The LD PRELOAD Environment Variable and Set-UID Programs
- Task 8: Invoking External Programs Using system() versus execve()
- Task 9: Capability Leaking
- 实验总结
Environment Variable and Set-UID Program Lab
Task 1: Manipulating Environment Variables
1.打印当前的环境变量,并查找PWD。

2.设置一个环境变量rwb=111,查看输出。取消这个环境变量。

Task 2: Passing Environment Variables from Parent Process to Child Process
task2的代码如下。首先输出子进程的环境。


修改代码。输出父进程的环境。


diff两个输出文件,发现没有区别。fork()是创建进程函数。程序一旦开始运行,就会产生一个进程,当执行到 fork()时,就会创建一个子进程。此时父进程和子进程是共存的,它们会一起向下执行程序的代码。通过修改 printenv 的执行位置,构造出 child1(子进程的环境变量)和 child2(父进程的环境变量) 。可以明确的是,子进程会继承父进程的环境变量。

Task 3: Environment Variables and execve()
程序如图所示,发现没有打印环境。


更改代码之后结果如下。


execve()函数接收三个参数:1.执行程序的路径;2.调用程序执行的参数序列;3.传入新程序的环境变量。若成功不返回,失败返回-1。前者第三个参数传递的是NULL,所以打印为空。后者传递了外部环境变量数据给新程序,因此打印出了当前环境变量。
Task 4: Environment Variables and system()
task4的代码如下。


system() 函数的实现,可以发现它使用 execl() 来执行 /bin/sh,execl() 会调用 execve() ,同时将环境变量传递给这个系统调用。因此,使用 system() 时,调用进程的环境变量会传递给新程序 /bin/sh。
Task 5: Environment Variable and Set-UID Programs
task5的代码如下。

将程序编程setuid程序。并修改环境变量。




运行程序后可以看到 PATH 和rwb下有export 进去的./program ,但是LD_LIBRARY_PATH并没有显示出来。
Task 6: The PATH Environment Variable and Set-UID Programs
task6的代码如下。
首先不改变环境变量,显示正常,调用了/bin/ls。


写一个ls程序,同时修改环境变量。发现执行的程序是我们自己的ls程序。ls.c的程序如下。


Task 7: The LD PRELOAD Environment Variable and Set-UID Programs
这个实验在macos上没有做出来,又换了ubuntu虚拟机做了。
mylic.c和myprog.c代码如下。


编译程序,并设置LD_PRELOAD环境变量。

1.myproc为普通程序,并以普通用户运行

会执行我们库中的sleep。
2.myprog 设置为 Set-UID root 程序,并以普通用户运行

sleep 1秒。
3.myprog 设置为 Set-UID root 程序,在 root 账户中设置 LD_PRELOAD 环境变量后运行该程序。

会执行我们库中的sleep。
4.myprog 设置为 Set-UID user1 程序,在 user2中设置 LD_PRELOAD 环境变量后运行该程序。
添加一个user1,然后用rwb用户去实执行这个程序。


sleep 1秒。
主要原因:动态链接器的保护机制。
当运行进程的真实用户ID与程序的拥有者的用户ID不一致时,进程会忽略掉LD_PRELOAD环境变量。只有用户自己创建的程序自己去运行,才会使用LD_PRELOAD环境变量,重载sleep函数,否则的话会忽略LD_PRELOAD环境变量,不会重载sleep函数。
- 普通用户(seed)下执行myprog程序,此时myprog程序的拥有者是seed,而且在seed用户下设置了环境变量,所以真实用户ID与拥有者用户ID一致,子进程会继承seed用户下的LD*环境变量,并加入共享库,执行设置的sleep函数。
- myprog为Set-UID 根程序,在seed用户下执行,ID不一致,所以动态链接器会忽略LD_PRELOAD环境变量,子进程不能继承seed用户下的LD*环境变量,所以正常执行sleep函数。
- myprog为Set-UID 根程序,并在Root用户下设置了环境变量,所以在root用户下运行myprog,ID一致,所以子进程会继承root用户下的LD_PRELOAD环境变量,并加入共享库,执行设置的sleep函数。
- myprog为Set-UID user1程序,在seed用户下执行也会遇到ID不一致,所以忽略环境变量,正常执行sleep函数。
Task 8: Invoking External Programs Using system() versus execve()
catall.c如图所示。


/bin/bash 有某种内在的保护机制可以阻止 Set-UID 机制的滥用,为了能够体验这种内在的保护级制出现之前的情形,我们打算使用另外一种 shell 程序——/bin/zsh。在一些linux 的发行版中(Ubuntu), /bin/sh 实际上是/bin/bash 的符号链接。为了使用zsh,我们需要把/bin/sh链接到/bin/zsh。

在root下面创建了一个a.txt,然后在rwb下删除这个文件,发现并没有权限给删除掉。


利用system删除掉a.txt。

注释掉system,取消注释execve。然后编译将其设置为setuid程序。

使用system()可以成功删除不可写文件,是因为system会创建一个子进程,然后子进程会调用一个新的shell程序,而且因为task8是一个Set-UID根程序,所以在执行时会以root权限执行删除文件的命令,可以成功删除。
使用execve()不可以成功删除不可写文件,因为execve会执行一个新程序,而不会调用新的shell程序,所以将我们输入的参数仅仅当成一个字符串,不会执行命令,所以不能删除不可写文件

Task 9: Capability Leaking
cap_leak.c如图所示。

创建root用户所有权限为0644的zzz文件。

修改文件位置


发现已经写入。

运行Set-UID程序时,进程暂时获得root权限,打开zzz文件时,获得了root权限下的读写文件、向文件中添加内容的权限,当使用setuid()释放root权限时,没有释放进程已经获得的特权功能—读写文件、向文件中添加内容;导致仅仅是将程序的拥有者降为非root用户,然后进程还拥有root权限下的读写文件、向文件中添加内容的功能,所以造成了权限泄露的问题,当执行fork创建子进程后在子进程中返回0,父进程中返回子进程的PID,父进程打开文件etc/zzz后,子进程不是直接修改文件/etc/zzz,而是修改了缓冲区中的文字,即在setuid之前,zzz文件就已经被打开了,只要将语句setuid(getuid())移至调用open函数之前,就可以避免。
实验总结
本次的实验集中在setuid程序和环境变量两个部分。通过实验,我更加深入理解了setuid程序是怎么样去执行了,同时理解了环境变量在程序执行的时候扮演着怎么样的角色。整体实验不算难,也相当于是抛砖引玉,未来可以深入学习类似的一些程序。