Set UID¶
约 1353 个字 48 行代码 29 张图片 预计阅读时间 7 分钟
Warning
本实验需要在 Ubuntu 20.04 或更低版本进行,Ubuntu 24.04 会有意想不到的效果
任务 1:操作环境变量¶
我们使用 printenv PWD
命令来打印当前工作目录的环境变量:
用 env | grep PWD
也同理:
- 图中的 OLDPWD 环境变量代表上一个目录
我们还可以用 export
来设置一个新的环境变量,并用 printenv 验证:
用 unset
即可删除环境变量:
任务 2:从父进程向子进程传递环境变量¶
步骤 1¶
编译 myprintenv.c
文件,运行获得输出:
步骤 2¶
注释掉子进程中的 printenv()
语句,取消注释父进程中的 printenv()
语句,重新编译并运行:
步骤 3¶
使用 diff
命令比较两个文件的差异,得到:
两个文件没有任何差异,说明子进程成功从父进程复制到了所有的环境变量
任务 3:环境变量与 execve()¶
步骤 1¶
编译运行 myenv.c
,得到结果:
可以看到没有任何输出,当前进程没有任何环境变量
步骤 2¶
修改 myenv.c
的 execve()
调用为 execve("usr/bin/env", argv, environ)
,重新编译运行:
可以看到输出了完整的环境变量列表
步骤 3¶
execve()
函数是否将环境变量传递给新程序,完全取决于其第三个参数
- 如果第三个参数为
NULL
,新程序不会继承任何环境变量 - 如果第三个参数指向一个环境变量数组(如全局变量
environ
),新程序则会获得这些环境变量
任务 4:环境变量与 system()¶
编译并运行以下代码:
得到结果:
可以看到,程序输出了当前 shell 的完整环境变量列表,说明 system()
函数会将被调用进程的环境变量传递给它所创建的 shell 进程
任务 5:环境变量与 Set-UID 程序¶
步骤 1¶
编译并运行以下代码,以打印当前进程中的所有环境变量:
步骤 2¶
编译上述程序,将其所有者改为 root,设置为 Set-UID 程序:
步骤 3¶
使用 export 命令设置几个环境变量:
运行步骤 2 的 Set-UID 程序:
可以看到,PATH 和自定义的环境变量都被继承下来了,但是 LD_LIBRARY_PATH 却消失了,这可能是因为现代 Linux 系统对 Set-UID 程序的安全性进行了增强,禁止了某些敏感环境变量的传递,以防止攻击者利用这些环境变量进行攻击
任务 6:PATH 环境变量与 Set-UID 程序¶
首先我们需要将 /bin/sh 链接到 /bin/zsh:
我们首先在 /tmp 当中编写一个恶意 ls 程序:
编译程序:
构建受害程序:
编译并设置为 Set-UID 程序:
将包含恶意 ls 的目录添加到 PATH 环境变量中并运行受害程序:
可以看到,恶意 ls 程序成功运行并创建了 /root/hacked 文件,说明我们成功利用了 Set-UID 程序的 PATH 环境变量漏洞来执行恶意代码
在 Ubuntu 24.04 的效果
在 Ubuntu 24.04,恶意 ls 程序成功运行但是没有输出,且没有创建 /root/hacked
,但是同样使得进程的 ls
命令改变
任务 7:LD_PRELOAD 环境变量与 Set-UID 程序¶
步骤 1¶
构建动态链接库,编写 sleep()
函数覆盖 libc 中的 sleep()
函数:
设置 LD_PRELOAD 环境变量并编译运行以下程序:
步骤 2¶
不同条件下运行上述程序:
-
myprog
为普通程序,以普通用户运行:- 输出了覆盖的
sleep()
函数,原来的sleep()
函数并没有被调用 myprog
为 Set-UID root 程序,以普通用户运行:
- 程序暂停了 1 秒,并没有输出覆盖的
sleep()
函数 myprog
为 Set-UID root 程序,以 root 用户运行:
- 输出了覆盖的
sleep()
函数,原来的sleep()
函数并没有被调用 myprog
为 Set-UID user1 程序,在 user2 账户中运行:
- 程序暂停了 1 秒,并没有输出覆盖的
sleep()
函数
步骤 3¶
- 输出了覆盖的
LD_PRELOAD
环境变量被操作系统认为是危险的,因此在执行 Set-UID 程序时,除非程序的真实用户和有效用户是同一个人(通常是 root 自己运行自己的程序),否则这些变量会被动态加载器清除或忽略,以防止权限提升攻击
任务 8:使用 system() 和 execve() 调用外部程序¶
步骤 1¶
在这之前,我们有生成一个 /root/hacked
文件,但是没有权限去删除:
对于给定的程序,因为使用的是 system()
函数,我们通过注入一条命令,去删除之前我们在 /root
下生成的 hacked
文件:
可以看到,我们成功删掉了 /root/hacked
文件,实现了破坏系统的完整性
步骤 2¶
我们在 catall.c
中注释掉 system()
函数,改为使用 execve()
函数来调用外部程序,还原 /root/hacked
文件,再次编译并运行:
可以看到,程序将整个字符串当做文件,并没有执行命令,因此没有删除 /root/hacked
文件
任务 9:权限泄漏¶
首先创建一个归 root 所有的重要文件 /etc/zzz
:
我们通过 cap_leak.c
泄漏的文件描述符,即可获取到不属于 seed 账户权限的文件的写入权:
在正常情况下,我们是无法写入的: