SEED 2.0 Softwarelab1:Environment Variable and SetUID

Environment Variable and SetUID

Overview

在本实验室中,学生将了解环境变量如何工作,如何从父进程传递到子进程,以及它们如何影响系统/程序行为。

我们特别感兴趣的是环境变量如何影响Set-UID程序(通常是特权程序)的行为。

本实验室涵盖以下主题:

  • Environment variables,环境变量
  • Set-UID programs,Set-UID程序
  • Securely invoke external programs,安全地调用外部程序
  • Capability leaking,权限泄漏
  • Dynamic loader/linker,动态加载器/链接器

Lab Tasks

Taks 1:Manipulating Environment Variables,操纵环境变量

在这个任务中,我们研究被用来创建和删除环境变量的命令。

请做以下的任务:

  • 使用printenv或者env命令去打印出环境变量。
  • 使用exportunset命令去创建或者删除环境变量。这两个命令不是程序,是bash自带的内部命令。

过程

  1. 先用env命令查看环境变量
    image-20211020232822008
  2. 然后export sung_lab=test创建一个环境变量,用env | grep sung_lab或者printenv sung_lab命令查看
  3. 最后unset sung_lab删除环境变量sung_lab,同样查看一下
    image-20211020233319753

Task 2:Passing Environment Variables from Parent Process to Child Process,传递父进程的环境变量给子进程

在这个任务中,我们研究一个子进程如何从它的父进程得到环境变量。

我们想知道是否父进程的环境变量会被子进程继承。

Step 1. 请运行myprintenv.c程序,并描述你看到的现象。

  1. 使用gcc myprintenv.c命令编译它,生成a.out二进制程序。

  2. 使用a.out > file运行这个二进制程序并将结果保存到file文件。

    image-20211021082901997
    image-20211021084800668
    image-20211021084917107
    image-20211021085010998

  3. 现象:我们看到a.outenv命令一样输出了环境变量,我们查看源码知道,这里输出的是fork()创建出的子进程的环境变量。

Step 2. 注释子进程的printenv()函数,取消注释父进程的printenv()函数并重新编译运行保存结果。

  1. 修改文件
    image-20211021084533013
  2. 重新编译运行,保存结果到file2
    image-20211021085304578
    image-20211021085335272
    image-20211021085425404
    image-20211021085456050
  3. 现象:我们看到a.out输出了环境变量,我们之前修改源码,switch中将子进程的printenv()注释的同时将父进程的printenv()取消注释,所以这里输出的是父进程的环境变量。

Step 3. 使用diff命令比较两个文件内容的不同。得出你的结论。

image-20211021090547419

结论:我们看到父进程和子进程打印的环境变量没有不同,所以得出结论,子进程会完整地继承父进程的环境变量。

Task 3:Environment Variables and execve(),环境变量和execve()函数

在这个任务中,我们研究当一个新程序被通过execve()函数执行的时候,环境变量是如何被影响的。

基本上,execve()函数在调用进程中运行新程序。

我们对环境变量发生了什么感兴趣,他们是否会被新程序自动继承?

Step 1. 编译并运行myenv.c程序,描述你看到的现象。

程序简单地执行了/usr/bin/env程序,这个程序会打印当前进程的环境变量。

image-20211021092417939

很奇怪,没有环境变量输出。

Step 2. 修改execve()函数调用的参数,描述你看到的现象。

  1. 修改第三个参数NULLenviron,如图所示:
    image-20211021093119656
  2. 重新编译运行,查看结果
    image-20211021093436697
    image-20211021093519126
  3. 我们可以看到,这次正确输出了环境变量

Step 3. 关于新程序如何得到它的环境变量,请给出你的结论

通过execve()等函数运行新程序,可以在参数里指定环境变量指针来对新程序设置环境变量。

这也正是step 1和 step 2结果有差异的原因

step 1设置新程序环境变量指针为空,所以新程序没有得到环境变量,也就没有输出;

step 2中设置了环境变量,所以新程序得到环境变量,所以有输出。

Task 4:Environment Variables and system(),环境变量和system()函数

在这个任务中,我们研究当一个新程序被通过system()函数执行的时候,环境变量如何被影响。

system()实际上执行/bin/sh,并请求shell去执行命令。

system()先使用execl()函数去执行/bin/shexecl()调用execve(),传递给它环境变量数组。

因此,使用system()函数,调用进程的环境变量被传递给新程序/bin/sh

请编译并运行如下程序去验证这个结论。

过程

  1. 编辑一个verify.c程序,文件内容如下:
    image-20211021101703340
  2. 编译并运行,查看结果
    image-20211021101929315
    image-20211021102001671
  3. 可以看到输出了环境变量。说明环境变量从调用进程传给了/bin/sh,再从/bin/sh传给了子进程env

Task 5:Environment Variable and Set-UID Programs,环境变量和Set-UID程序

Set-UID是Unix操作系统一种重要的安全机制。当一个Set-UID程序运行时,它假使程序所属用户的权限。

例如,如果一个程序的所属用户是root,那么当任何人去运行它,在调用过程中这个程序都会获得root权限。

Set-UID允许我们去做很多有趣的事情,但由于它升级了当前用户的特权,它是相当危险的。

尽管Set-UID程序的行为被程序逻辑所决定,而不是用户,但是用户确实能通过改变环境变量去影响它的行为。

为了理解Set-UID如何被影响,让我们搞清楚Set-UID程序的进程是否会继承当前用户进程的环境变量。

Step 1. 编写以下程序,它能打印出当前进程的环境变量。

image-20211021105132343

Step 2. 编译以上程序,改变所属用户,并使它成为一个Set-UID程序。

image-20211021105539178

Step 3. 你需要用非root用户去设置以下环境变量(它可能已经存在)

  • PATH
  • LD_LIBRARY_PATH
  • ANY_NAME (这是由你定义的环境变量,因此选择你想要的名字)

这些环境变量设置在当前用户的shell进程。

现在,运行Step 2创建的Set-UID程序。请检查你在shell进程(父进程)所设置的所有的环境变量是否都进入了Set-UID子进程。

描述你的观察。如果遇到令人惊讶的现象,描述他们。

  1. 查看这三个环境变量的值,发现只有PATH存在。
    image-20211021111121776
  2. 设置这两个不存在的环境变量和已经存在的变量PATH
    image-20211021111652449
  3. 我们运行Step 2生成的foo程序,查看我们设置的三个环境变量
    image-20211021112224792
  4. 我们发现LD_LIBRARY_PATH并没有进入Set-UID子进程,进一步查看差异
    image-20211021112921464
  5. 我们发现除了LD_LIBRARY_PATH环境变量外,只有_变量不同。

可以看到输出的环境变量中包含PATH 和 ANY_NAME,且环境变量PATH为我们修改后的值。但其中没有我们用 export 命令设置的环境变量LD_LIBRARY_PATH。这个出乎意外,按道理说用 export 设置的都会进入子程序的环境变量。

其实这是一种动态链接器的保护策略,因为这个程序用到了动态链接库。

因为我们是在普通用户的状态下修改 LD_LIBRARY_PATH,如果这个修改能传递到 set-uid 程序中,那么我们就能通过修改 LD_LIBRARY_PATH 来改变 set-uid程序的库的寻找位置来实施攻击。

因此动态链接器在 effective uid 和 real uid不一致 或者 effective groupid 和 real groupid不一致时,会将 LD_LIBRARY_PATH 和 LD_PRELOAD 环境变量忽略掉,所以子进程是看不到的。

而其他的环境变量动态链接器没有使用到,因此也不会去干涉,所以子进程能够得到。

Task 6:The PATH Environment Variable and Set-UID Programs,PATH环境变量和Set-UID程序

因为shell程序被调用,在Set-UID程序里调用system()函数是相当危险的。

这是因为shell程序的真实行为会被环境变量影响,像是$PATH变量,这些环境变量是由用户提供的,而用户可能是恶意的。

通过改变这些环境变量,恶意用户可以控制Set-UID程序的行为。

1
2
3
4
5
int main()
{
system("ls");
return 0;
}

请编译以上程序,改变程序所属用户为root,并且让它成为一个Set-UID程序。

使用Set-UID程序去执行你的恶意代码,描述并解释你观察的现象。

过程

  1. 先准备一个Set-UID程序,我们现在得到一个Set-UID程序myls
    image-20211021144813929

  2. 修改环境变量PATH,增加/home/seed/Desktop/Labsetup目录,我们将在这个目录下面存放恶意程序
    image-20211021152009796

  3. 以id命令为例,将id文件更名为ls放在/home/seed/Desktop/Labsetup目录下面,当我们执行myls文件,不再调用ls而是名为ls的id命令。
    image-20211021152108820

程序 id 是Set-UID程序运行的,按理说应该也有 root 权限,但从程序 id 的输出来看,其并没有获得 root 权限。

这是因为Ubuntu 20.04 VM,/bin/bash实际上是指向/bin/dash的符号链接。

/bin/dash 中有一个对策,当它发现自己自己在一个Set-UID程序中执行时,它会把有效用户ID(root)改成当前进程的真实用户ID(seed), 也就是放弃了特权,所以攻击失败。

SEED_Ubuntu有一个叫做zsh的shell程序,我们可以使用一下命令链接/bin/sh到/bin/zsh:

sudo ln -sf /bin/zsh /bin/sh

再次执行,结果如下所示:

image-20211021153534901

Task 7:The LD_PRELOAD Environment Variable and Set-UID Programs,LD_PRELOAD环境变量和Set-UID程序

在本任务中,我们研究Set-UID程序如何处理一些环境变量。

一些环境变量,包括LD_PRELOAD、LD_LIBRARY_PATH和其他会影响动态加载器/连接器行为的LD_*环境变量。

动态加载器/链接器是操作系统(OS)的一部分,它在运行时加载(从持久存储到RAM)和链接可执行文件所需的共享库。

在Linux中,ld.so或ld-Linux.so是动态加载器/链接器(每个都用于不同类型的二进制文件)。在影响其行为的环境变量中,我们在本次试验中关注LD_LIBRARY_PATH和LD_PRELOAD。

在Linux中,LD_LIBRARY_PATH是一组以冒号分隔的目录,在其中首先搜索库,然后搜索标准目录集。

LD_PRELOAD指定要在所有其他库之前加载的其他用户指定共享库的列表。

在本任务中,我们将只研究LD_PRELOAD。

Step 1. 首先,我们要理解当运行一个普通程序时,环境变量是如何影响这些动态加载器/链接器。

  1. 我们先建立一个动态链接库,创建mylib.c,它覆盖了libc的sleep()函数
    image-20211021155829901
  2. 我们可以使用以下命令编译上述程序
    image-20211021161341297
  3. 现在,设置LD_PRELOAD环境变量:
    image-20211021161442505
  4. 最后,编译下面的程序myprog,并在与上面动态链接库libmylib.so.1.0.1相同的目录下
    image-20211021161714670

Step 2. 完成上述操作后,请在以下条件下运行myprog,并观察发生的情况

  • 使myprog成为一个普通程序,运行它作为一个普通用户
    image-20211021162603277
    现象:执行我们覆盖的sleep()函数,输出 i am not sleeping!
  • 使myprog成为一个Set-UID root程序,运行它作为一个普通用户
    image-20211021163126750
    现象:等待一秒后结束,无输出
  • 使myprog成为一个Set-UID root程序,作为root用户再一次改变LD_PRELOAD环境变量的值,运行它。
    image-20211021163339271
    现象:执行我们覆盖的sleep()函数,输出 i am not sleeping!
  • 使myprog成为一个Set_UID user1程序,在另一个用户帐户(非root用户)中再次改变LD_PRELOAD环境变量并运行它
    image-20211021163940025
    现象:等待一秒后结束,无输出

Step 3. 分析上述结果不同的原因

造成不同结果的关键在于 LD_PRELOAD 环境变量有没有被动态连接器屏蔽,LD_PRELOAD 和 LD_LIBRARY_PATH 类似,具体机制可以查看上面 Task 5 及其分析。

对于第一种情况,euid等于uid,均为seed,LD_PRELOAD 环境变量没有被屏蔽,所以链接的是 libmylib.so.1.0.1,输出一句话

对于第二种情况,euid为root,uid为seed,不相同,LD_PRELOAD 环境变量被屏蔽,从标准路径查找链接库,所以链接的是标准库。所以结果为等待一秒后结束,无输出。

对于第三种情况,euid 等于uid,均为 root,LD_PRELOAD 环境变量没有被屏蔽,所以链接的是 libmylib.so.1.0.1,输出一句话

对于第四种情况,euid为uer1,uid为seed,不相同,LD_PRELOAD 环境变量被屏蔽,从标准路径查找链接库,所以链接的是标准库。所以结果为等待一秒后结束,无输出。

Task 8:Invoking External Programs Using system() versus execve(),使用system()和execve()函数调用外部程序

尽管system()execve()函数都被用来去运行一个新程序,但是因为环境变量可以影响shell的工作,所以system()函数要危险得多,例如Set-UID程序中使用。execve()没有这个问题,因为它不调用shell。

调用shell还有另一个危险的后果,这次与环境变量无关。

Step 1. 编译catall程序并让它成为一个Set-root-UID程序,程序将使用system()函数调用命令。

如果你是Bob,你能否删除一个不可写的文件?

  1. 编译程序,并设置为Set-root-UID程序
    image-20211021170716399

  2. 实施一个删除文件攻击, 用 root 账户在 /root 目录下新建一个文件example, 然后切换到 seed 账户,可以看到无法访问也无法删除该文件。
    image-20211021171009166

  3. 在第一条命令执行后,继续执行恶意命令,成功删除example文件
    image-20211021171522720
    image-20211021171442550

Step 2. 将step 1中的调用system函数改成调用execve,重复上面的流程。

image-20211021171828008

image-20211021172024896

可以发现,运行报错并且未删除该文件

分析:在使用system函数时,最终是shell去执行命令,而且未仔细检查用户输入,使得其执行了两条命令。其中第二条命令为恶意构造的命令,删除了文件。改成使用execve之后,这种攻击方式是不会成功的,因为它是通过系统调用的方式去执行,只能执行一个进程,且进程名已指定,不会产生这种漏洞。

Task 9:Capability Leaking,权限泄漏

image-20211021172835747

先用 root 权限创建 /etc/zzz 文件。然后编译上面代码,并设置为Set-root-UID程序。

使用普通账户运行该程序发现等了一秒,无输出。查看 /etc/zzz 可以发现文件内容已经被修改。

image-20211021201721022
image-20211021201807244

原因分析:这个特权程序打开了一个重要的的系统文件,并且在放弃特权时没有关闭该文件,而后调用Fork,子进程会继承这个文件描述符,造成特权泄露。子进程可以通过泄露的文件描述符向文件写入恶意内容

  • Copyright: Copyright is owned by the author. For commercial reprints, please contact the author for authorization. For non-commercial reprints, please indicate the source.
  • Copyrights © 2021 Sung
  • Visitors: | Views:

请我喝杯咖啡吧~

支付宝
微信