首页 > 其他分享 >网络攻防技术——环境变量和Set-UID攻击

网络攻防技术——环境变量和Set-UID攻击

时间:2024-01-05 14:56:50浏览次数:34  
标签:Set UID 程序 shell 进程 执行 root 环境变量

实验6:环境变量与set-uid实验

实验内容:

本实验室的学习目标是让学生了解环境变量如何影响程序以及系统行为。环境变量是一组动态命名值,可以影响正在运行的进程将在计算机上运行。大多数操作系统都使用它们,因为它们是1979年引入Unix。尽管环境变量会影响程序行为,但它们是如何实现的这一点很多程序员都不太理解。因此,如果程序使用环境变量程序员不知道它们被使用,程序可能有漏洞。

在本实验室中,学生将了解环境变量是如何工作的,它们是如何从父进程到子进程,以及它们如何影响系统/程序行为。我们特别感兴趣的是如何环境变量影响Set-UID程序的行为,这些程序通常是特权程序。

本实验室涵盖以下主题:

•环境变量

•SET-UID程序

•安全地调用外部程序

•能力泄漏

•动态加载程序/链接器

 

 

  1. Task1

    使用 `printenv` 或 `env` 命令可以打印当前shell中的全部环境变量

    也可以指定打印某个环境变量

    使用`export`命令和`unset`命令可以分别设置和删除环境变量,作用域是当前的shell

  2. Task2

    environ是一个全局变量,通常用于在C语言中获取当前进程的环境变量。它是一个指向字符指针数组的指针,这个字符指针数组中的每个元素都是一个指向以null结尾的字符串的指针,表示一个环境变量的键值对,environ[i]获取到了一个字符指针,指向其中某个环境变量,C语言中通过打印字符串指针可以打印出其指向的字符串。该声明使用了extern关键字,因此environ变量的定义通常是在操作系统或C运行时库中,而不是在用户程序中。

使用fork()函数创建一个子进程,分别在父子进程中打印环境变量

首先在子进程中打印环境变量如下

父进程中打印环境变量如下

使用`diff`命令发现父子进程的环境变量相同,说明子进程会继承父进程的环境变量,还包括父进程的堆栈、内存等,但进程id和fork()函数返回值不同

  1. Task3

    `execve()`函数执行参数指定的命令时不会产生一个新进程(或者一个新的shell),而是将被加载的程序的数据和堆栈等覆盖到调用进程上,我们先不传入环境变量,打印现在环境的环境变量,发现不存在环境变量,说明原有进程的环境变量被覆盖

    传入进程的环境变量,发现传入新环境

    `execve`函数会将传入的环境变量覆盖到原有环境变量上,若没有传入,则环境变量为空

  2. Task4

    system()函数也可以用来执行新的程序,与`execve()`函数不同,system()函数实际上执行"/bin/sh -c命令",即打开了一个新的shell,并要求shell执行该命令

    system()会调用fork()函数产生子进程,然后子进程调用/bin/sh -c string 来执行传入参数的命令。此命令执行完后随即返回父进程,环境变量从父进程传给了子进程中的shell程序,在shell程序输出了全部的环境变量

  3. Task5

    Set-UID是Unix下的一种安全机制,当该机制下的程序运行时,它将拥有该程序所有者的权限。例如,如果程序的所有者是root,那么当任何人运行此程序时,程序在执行过程中都会获得root的权限,而普通用户运行该程序时就会存在越权的漏洞

    编写程序打印环境变量

    将用户设置成root并赋予执行权限

    修改成功

    我们在seed用户下使用export命令设置环境变量,我们设置的Task5=Task5 被成功传入,但 LD_LIBRARY_PATH 传入失败;

    这是一种动态链接器的保护策略,因为这个程序用到了动态链接库。我们是在seed用户的状态下修改的LD_LIBRARY_PATH,在Set-UID的机制下,对动态链接相关环境变量的修改不能被传递到可执行程序中,这是为了抵御修改LD_LIBRARY_PATH来改变set-uid程序的库的寻找位置来实施攻击的攻击方法动态链接器在effective uid(root)和real uid(seed)不一致时,会将LD_LIBRARY_PATH和LD_PRELOAD环境变量忽略掉,所以子进程是看不到的。而环境变量PATH以及ANY_NAME与动态链接器无关,没有被屏蔽,所以子进程能够看到

  4. Task6

    编写一个调用ls的程序,并将程序所有者设置为root。程序在执行时如果不提供命令的具体路径,会在环境变量的路径下依次搜索,直到找到路径下和准备执行的程序同名的程序,然后拿去执行,这里程序会找到 /etc/bin/ls 去执行

由于/bin/sh默认链接到dash,而dash的保护机制会组织有效ID和真实ID不一致的程序的执行,所以需要将/bin/sh链接到zsh

编写一个假的ls程序,并将编译后的执行文件也命名成ls

把假的ls文件所在的目录添加到PATH环境变量的开头,这时再使用system()函数调用ls执行的就是我们写的ls程序

注意路径最后有一个"/"

直接shell中执行ls调用的也是我们的ls程序

eid指的是用户的有效用户ID(effective user ID),也就是用户在执行特定进程时所拥有的权限

将我们编写的ls程序加上调用`id`命令的语句,发现打印的eid是root,证明文件执行时是以root权限执行的

  1. Task7

    `gcc -fPIC -g -c mylib.c`:编译源代码文件mylib.c,生成目标文件mylib.o。选项-fPIC表示生成位置无关的代码(Position Independent Code)以便编译器能够在不同地址空间中执行该代码。选项-g表示生成调试信息,以便能够使用调试器来调试该代码。

    `gcc -shared -o libmylib.so.1.0.1 mylib.o -lc`:将目标文件mylib.o链接成一个动态链接库libmylib.so.1.0.1。选项 -shared表示生成动态链接库,选项 -o指定输出文件名。-lc选项表示动态链接库需要链接C运行时库

    这里只是修改了sleep函数,但实际攻击中可以放入恶意代码,当用户的正常程序链接到我们的恶意动态库时就会执行这些恶意代码

    环境变量LD_PRELOAD用于在程序运行时强制优先加载指定的共享库文件。这个功能可以用来替换已经存在的库中的函数或者添加新的函数,修改这个环境变量到我们的动态链接库

    

  1. seed用户的程序,以seed用户的身份运行

    链接到了我们的库

  2. root用户程序,以seed用户的身份运行

    链接到了系统的库

  3. root用户的程序,以root用户的身份运行

    链接到了我们的库

  4. leo用户的程序,以leo用户的身份运行

    链接到了系统的库

是否能成功链接到我们的库取决于我们设置的LD_PRELOAD 环境变量有没有被动态连接器屏蔽,当执行环境的用户ID和程序的用户ID不一致时,动态链接器出于安全考虑,会将环境变量的改变屏蔽掉,因此我们设置的环境变量不会生效

  1. Task8

    编译给出的catall文件,并将执行文件用户设置为root

    在root权限下创建一个名为task8的文件,发现在seed用户下无法将其删除

    在catall执行后的参数中写入两条shell语句,以";"作为分隔,system()函数拿到字符串后会直接将其作为整个shell命令去执行,发现第二条删除语句成功执行,这是因为catall是一个root权限的文件,执行时具有root权限,这里就发生了普通用户执行root程序的越权行为

    换成execve()函数,发现无法执行,原因是execve()函数并不会启动一个shell,传入的字符串整个作为一个程序去执行,自然会报无法找到这个程序/命令的错误

  2. Task9

    在root用户下新建一个只读的文件/etc/zzz

    编译给出的cap_leak程序,并将用户设置成root

    在程序的执行过程中,遇到下面的语句时,程序会将程序的用户ID设置成当前用户ID,即seed,这样看起来程序之后就不会再以root权限执行了

    这时再启动一个shell

    我们发现在这个shell中还是成功地对zzz文件进行了写操作

    这是因为程序在切换权限之前没有关闭该文件,在新权限下继承了该文件描述符,文件描述符泄露,在关闭文件描述符之前,root操作都能成功执行

     

  3. 结论

    环境变量是一组动态命名值,可以影响正在运行的进程将在计算机上的运行,我们需要了解环境变量是如何工作的,它们是如何从父进程到子进程,以及它们如何影响系统/程序行为,尤其是针对Set-UID程序,这会带来巨大的安全漏洞。`fork()`、`execve()`和`system()`函数对环境变量传递的方式不尽相同,fork()函数会将父进程环境变量复制到子进程中,execve()函数,不会传递环境变量,但可以将环境变量作为参数进行传递,system()函数新建一个进程并开启一个shell,将环境变量传递进去。对于Set-UID来说普通环境变量的传递同上,但涉及到动态链接的环境变量时,动态链接器在 effective uid 和 real uid 不一致时,会将 LD_LIBRARY_PATH 和 LD_PRELOAD 环境变量忽略掉,所以不会传递成功。通过修改环境变量,可以修改其他用户调用程序的执行程序,从而执行我们的恶意程序。最后,Set-UID程序很有可能发生权限溢出的问题,攻击者可能利用被泄露的权限对我们的系统造成攻击(如对文件的修改,甚至拿到该权限下的shell),所以使用时一定要谨慎。

标签:Set,UID,程序,shell,进程,执行,root,环境变量
From: https://www.cnblogs.com/leo1017/p/17947242

相关文章

  • 无涯教程-Redis - Sorted Sets(排序集)
    RedisSortedSets与RedisSets类似,它具有存储在集合中的值的独特功能,不同之处在于,排序集的每个元素都与一个分数相关联,该分数用于从最小到最大分数中获取排序的排序集。SortedSets-示例redis127.0.0.1:6379>ZADDLearnfk1redis(integer)1redis127.0.0.1:6379>ZA......
  • vue3 setup函数之数据
    setup中定义方法:exportdefault{name:'App',setup(){//定义方法functionedit(){}return{//方法与数据,必须要返回出去,不然不起作用。edit}}} setup中ref函数定义基本数据类型与对象数据类型:1.ref函数定义基......
  • javascript的Map和Set概念以及区别和使用场景
    Map和SetJavaScript的默认对象表示方式{}可以视为其他语言中的Map或Dictionary的数据结构,即一组键值对。但是JavaScript的对象有个小问题,就是键必须是字符串。但实际上Number或者其他数据类型作为键也是非常合理的。MapMap是一组键值对的结构,具有极快的查找速度。举个例子,假设要根......
  • 【C++】STL 容器 - set 集合容器 ⑥ ( pair 对组简介 | pair 对组元素访问 | set 集合
    文章目录一、pair对组1、pair对组简介2、pair对组元素访问3、代码示例-pair对组4、set集合容器存储pair对组元素二、set集合容器insert插入结果类型-pair对组1、std::set#insert函数原型分析2、代码示例-std::set#insert函数插入元素结果分析一、pair对组1......
  • 【C++】STL 容器 - set 集合容器 ⑤ ( 仿函数 functor 简介 | 仿函数 functor 调用 |
    文章目录一、仿函数functor1、仿函数functor简介2、仿函数functor调用3、代码示例-仿函数functor调用二、为自定义类元素设置排序规则-仿函数functor1、自定义类排序规则2、仿函数-实现自定义类排序规则3、重载<运算符函数-实现自定义类排序规则一、仿函数fu......
  • 【C++】STL 容器 - set 集合容器 ④ ( 设置 set 集合容器的排序规则 | 默认的 set 集
    文章目录一、设置set集合容器的排序规则1、默认的set集合容器-从小到大排列2、设置set集合容器从大到小排列二、使用仿函数自定义set集合容器排序规则1、仿函数概念2、使用仿函数实现set集合容器排序规则一、设置set集合容器的排序规则1、默认的set集合容器-......
  • 【C++】STL 容器 - set 集合容器 ③ ( set 集合容器常用 api 简介 | 删除元素 | 删除
    文章目录一、删除元素1、删除指定值的元素-erase函数2、删除指定迭代器位置的元素-erase函数3、删除指定迭代器范围的元素-erase函数4、删除集合中的所有元素-clear函数一、删除元素1、删除指定值的元素-erase函数在C++语言的STL标准模板库中,set集合容......
  • 【C++】STL 容器 - set 集合容器 ② ( set 集合容器常用 api 简介 | 使用迭代器进行正
    文章目录一、set集合容器遍历1、使用迭代器进行正向迭代与反向迭代2、代码示例二、set集合容器插入元素1、插入单个元素-insert函数2、插入多个元素-insert函数3、插入指定迭代器范围的元素-insert函数一、set集合容器遍历1、使用迭代器进行正向迭代与反向迭代std:......
  • 【错误记录】set 集合容器仿函数报错 ( 具有类型“const IntCompare”的表达式会丢失
    文章目录一、报错信息二、问题分析三、解决方案一、报错信息使用仿函数为set集合容器定义元素排序规则;仿函数functor是一个在许多编程语言中都存在的概念,它通常指一个对象,该对象能像函数那样被调用;具体来说,仿函数是一个类,该类重载了operator()函数,......
  • druid实现数据库密码加密功能
    数据库密码直接写在配置中,对运维安全来说,是一个很大的挑战。可以使用Druid为此提供一种数据库密码加密的手段ConfigFilter。如果项目已经集成druid,只需按要求配置即可。1、执行命令加密数据库密码找到druid-1.2.20.jar所在的文件夹目录,执行cmd命令,然后执行下面的命令生成数据库......