SST-Lab1:Environment Variable and SetUID
软件安全测试第一次实验
__姓名:secret! 班级:secret! 学号:secret!
一、实验题目
Environment_Variable_and_SetUID.pdf (seedsecuritylabs.org)
二、实验环境
Dell 14pro 灵越5410
VMware17
Ubuntu 22.04
Linux 5.19.0 x86_64
三、实验内容
1. 设置环境变量
Task 1.1
打印环境变量(截图中的输出未展示全部)
显示当前路径
Task 1.2
导入/撤销环境变量
- 子进程如何从父进程获取环境变量
myprinten.c
结论:结合教材所学知识,fork()创建子进程会继承父进程的环境变量,execve()调用会丢失环境变量,需要作为参数显式地传递。
- 环境变量受execve的影响
execve.c
(截图中的输出未展示全部)
原因分析:首先传入NULL环境变量为空,然后修改第三个参数environ显式传入成功打印环境变量
结论:execve若在一个进程中使用,环境变量会丢失,必须要作为参数显式地传递
原有进程的内存被完全替换成新程序,execve不会创建新的进程,PID没有变化,exec只是用磁盘上的新程序替换了当前进程的正文,数据,堆,栈段。所以环境变量也被替换了。
- 环境变量受system()影响
system.c
(截图中的输出未展示全部)
结论:可见实验中打印出所有环境变量,说明system函数会将环境变量传递给新程序
system()调用实际上是调用shell执行命令,system()的实现是用execl()系统调用来运行shell然后execve()传递环境变量数组,所以用system系统调用,当前进程的环境变量会传递给新程序。
- 环境变量和setuid程序
编译运行后设置为setuid程序,导入3个环境变量,再分别运行打印
结论:
所有的在父进程shell设置的环境变量都传递到了setUid程序子进程。shell中执行程序,当输入程序名称时,shell会生成一个子进程并在子进程中执行该程序.这个过程通常为先使用fork函数创建一个子进程,再使用execve()函数。
设置LD_LIBRARY_PATH环境变量(动态库的查找路径)不会出现在子进程的环境变量中,而其他两个均可以被包含。这是由于setuid程序的防御机制,当真实用户和有效用户不一致会自动忽略这个环境变量,而自定义的usrname没有危害仍可以打印
- PATH环境变量和setuid程序
编译运行后设置为setuid程序,将/bin/sh复制到另一个路径下并添加到环境变量,查看环境变量检查添加成功:tmp目录最先被搜索,然后运行程序,得到root权限
原因分析:虽然程序中命令为ls,但实际上运行了自己设定的程序/bin/sh,同时由于程序为Set-UID,有效用户为root,运行时以root权限运行,得到root权限的shell.
- LD_PRELOAD环境变量和setuid程序
Step1
进行动态链接和环境变量设置,编译源代码
Step2
情形一
常规程序,普通用户运行,执行自定义函数
情形二
设置setuid再运行,正常sleep1s后退出
情形三
在setuid基础上加入环境变量后再运行,正常sleep1s后退出
情形四:
将拥有者设置为第三方用户,成功运行重载sleep函数
设置为setuid程序再运行,不会运行重载函数:
结果分析:
动态链接器对Setuid程序有防御机制,当拥有者存在不一致时会忽略设置的环境变量;设置了Set-UID的程序,运行时不会出现函数重载
- 使用system()与excve()调用外部程序
Step1
编译并setuid系列操作,查看权限:
在当前目录下创建不为空文件test8。将所有者改为root并用root身份设置只读权限,(这样操作使得普通用户无法操作test8)。运行程序,查看test8内容并执行输入行指令rm test8,成功删除test8文件
Step2
注释system系统调用代码,改用execve
不会得到root权限
结果分析:
System系统调用函数实际执行三个操作:首先fork创建一个子进程;然后子进程中调用exec函数去执行command;在父进程中调用wait等待子进程结束。
其中在第二步引入了外部程序shell,(希望这个setuid执行shell:shell先被执行,command作为输入然后解析执行,是通过调用/bin/bash -c command;shell程序允许一行输入两个命令,用分号隔开),而在shell中,是可以执行任何指令的,所以实验中可以执行多条指令,在设置setuid程序后,可以获取root权限。system()函数违背了最小权限原则,调用了shell。
execve函数为执行一个系统调用函数,包含3个函数参数,其中第2个参数若包含额外指令也会被视作一个参数。
- 权限泄露
命令行解释:
首先以普通用户运行发现无法打开因为没有/etc/zzz的root权限
然后ls -l查看可执行文件的权限
以root身份向/etc/zzz写入,然后退出登录降低身份为普通用户
以普通用户身份写入/etc/zzz仍然没有权限
但是运行setuid程序之后,输出文件描述符并能够成功写入
结果分析:
Cap_leak.c中有打开文件open代码但是没有对应关闭文件close代码,并且setuid()撤销权限时泄露把握文件描述符的能力,也就是取消权限前并没有关闭文件,导致普通用户通过打印文件描述符fd拥有对文件操作的特权
避免方法:
法一:退出root之前,取消可执行文件cap_leak的set位权限即setuid,将/etc/zzz的权限位除拥有者root外其他用户其他组都没有任何读写执行的权限;
法二:在cap_leak.c中注释撤销权限代码setuid(),以防普通用户从中获得特殊权限。
修改后运行结果如下:
成功阻止普通用户写入