环境变量攻击

环境变量

  • 一组动态的定义值
  • OS运行环境的一部分
  • 影响正在运行进程的行为方式(加载哪些外部DLL)
  • 当执行一个程序时,如果没有提供完整的路径,shell进程将使用环境变量来找到程序的位置

使用Windows系统,我们安装编程相关软件时有时会问是否加入到环境变量,比如python(最好要加),这样我们在任何目录下cmd都可以运行python(现在本目录下找,没有就去环境变量里找)

如何访问环境变量

计算机右键属性>高级系统设置>环境变量

image-20221112224205107.png

代码访问环境变量

image-20221112224409693.png

编译运行后如下

image-20221112224614565.png

更简单的方法是printenv或env查看环境变量

image-20221112225034470.png

查看特定变量的方法:env | grep variable,或直接printenv variable

image-20221112225117641.png

image-20221112225123304.png

设置自己的环境变量:export variable=value

image-20221112225315241.png

image-20221112225325512.png

清除自定义的环境变量:unset variable

image-20221112225356032.png

进程如何获取环境变量

  • fork()创建一个新进程,则子进程将继承其父进程的环境变量

    #include <unistd.h>
    #include <stdio.h>
    #include <stdlib.h>
    
    extern char **environ;
    
    void printenv()
    {
      int i = 0;
      while (environ[i] != NULL) {
         printf("%s\n", environ[i]);
         i++;
      }
    }
    
    void main()
    {
      pid_t childPid;
      switch(childPid = fork()) {
        case 0:  /* child process */
          printenv();    
          exit(0);
        default:  /* parent process */
          // printenv();   
          exit(0);
      }
    }

代码解释:父进程fork后返回子进程id给pid,由于不为0将执行default中内容,在子进程中,fork返回0,执行case 0里的内容

https://blog.csdn.net/hzrandd/article/details/50724697

http://www.csl.mtu.edu/cs4411.ck/www/NOTES/process/fork/create.html

分别注释两个进程,编译运行后打印环境变量,可以看到fork的父子进程环境变量一模一样

image-20221112225727434.png

  • execve()启动一个新程序,在此场景中内存空间被覆盖,旧环境变量将丢失

    #include <unistd.h>
    
    extern char **environ;
    
    int main()
    {
      char *argv[2];
      argv[0] = "env";
      argv[1] = NULL;
      execve("/usr/bin/env", argv, NULL);  
      return 0 ;
    }

运行后打印环境变量为空

int execve(const char filename, char const argv[ ], char *const envp[ ]);

  • filename:执行命令的文件路径
  • argv[ ]:使用数组指针来传递给执行文件,必须以NULL结尾
  • envp[ ]:传递给执行文件的新环境变量数组

execve传递给新进程旧环境变量的方法:NULL改为外部变量environ,也可以传递自定义的环境变量

/proc文件系统

/proc是linux下的一个虚拟文件系统,包含每个进程的一个目录,使用进程ID作为目录名称

  • 每个进程目录都有一个名为environ的虚拟文件,其中包含进程的环境
  • 查看当前进程的环境变量:strings /proc/$$/environ (shell自动将\$$替换为进程ID)

shell命名变量

当shell程序启动时,会将环境变量复制到自己的shell变量中,对shell变量的改动不会改动环境变量

image-20221113095809405.png

如下图:显示了shell变量如何影响子进程的环境变量;同时显示了父shell的环境变量如何成为子进程的环境变量

image-20221113095852333.png

从图中可以看到:只有原本的环境变量export设置的环境变量才可以进入进程新建的子进程(反映在下图就是LOGNAME2没有出现在子进程的环境变量中【env执行,shell将创建一个子进程】)

image-20221113100137740.png

环境变量的攻击

通过Set-UID攻击

Set-UID

  • 允许用户以程序所有者的权限运行程序
  • 允许用户以临时提升的权限运行程序

在UNIX中,每个进程都有两个用户ID

  • real UID(RUID):确定进程的真正所有者
  • effective UID(EUID):标识进程的权限(访问控制)

当执行正常程序时,RUID=EUID=运行程序的用户的ID;当执行Set-UID时,RUID≠EUID,RUID还是运行程序的用户的ID,EUID是程序所有者的ID

即,若程序归root所有,执行set-UID将以root权限执行

攻击

由于用户可以设置环境变量,因此可以将其作为set-UID程序攻击的一部分

  1. 添加/home/seed 目录到 PATH 环境变量中,使用export,冒号是分割的作用

image-20221113103014341.png

  1. 编写使用 system 调用 ls 的程序

image-20221113103111796.png

  1. 编译后修改所有者权限,使之称为set-UID程序

image-20221113103152122.png

补充:4755(4 表示其他用户执行文件时具有与所有者相当的权限;7 表示文件所有者的权限为全部;5 表示与文件所有者同属一个用户组的其他用户的权限为可读可执行;第二个 5 表示其他用户组的权限为可读可执行)

  1. 执行后发现输出 ls 打印的内容

image-20221113104054541.png

  1. 我们可以利用之前添加的路径/home/seed,将/bin/sh 拷贝到那里并修改为 ls,这时再去执行 ls 其实会直接执行 sh,获取 shell,但是发现没有获取的 root 权限,uid 没有发生变化

image-20221113104219295.png

  1. 主要原因是/bin/sh 链接到了/bin/dash,具有一定的防御措施,检测到 uid 攻击后会将 euid 设置回进程真实的 uid,因此我们做一定的修改,将/bin/sh 链接到/bin/zsh,此时获取 root 权限(多了个euid=0,Set-UID 程序和其他 UNIX 程序的唯一区别是具有 Set-UID 比特位

image-20221113110338238.png

通过动态链接器攻击

动态链接

链接再运行时完成,在运行使用动态链接编译的程序前,首先将其可执行文件加载到内存(静态链接生成的程序比动态链接生成的大得多)

  • LD_PRELOAD包含一个共享库的列表,链接器将首先搜索它
  • 如果没有找到所有函数,链接器将在几个文件夹中搜索,包括LD_LIBRARY_PATH指定的文件夹
  • 这两个变量可以被用户设置,使得有机会控制链接过程

攻击

  1. 编写 sleep 函数的 lib 文件

image-20221113114130706.png

  1. 编译文件(-shared 生成共享目标文件,通常用在建立共享库时)

image-20221113114153713.png

  1. 设置 LD_PPRELOAD 环境变量为之前生成的共享目标文件

image-20221113114207723.png

  1. 编写 myprog.c 并编译运行,可以看到并没有调用本来的 sleep,而是调用了我们编写的 带有 printf 的 sleep

image-20221113114343654.png

image-20221113114348171.png

  1. 但是如果程序为set-UID程序,程序没有调用我们自定义的链接库

image-20221113114606835.png

原因:动态链接器实现了一个对策,当EUID和RUID不同时,它会忽略LD_PRELOAD和LD_LIBRARY_PATH环境变量

示例:复制env程序,并将其设置为Set-UID程序;导出上面两个环境变量

image-20221113114820337.png

image-20221113114824895.png

通过外部程序攻击

应用程序可以调用外部程序,本身可能不使用环境变量,但是被调用的外部程序可能会使用

方法

  • 执行exec()家族
  • system(),调用execl,execl调用execve运行/bin/sh,然后shell运行程序

与system()相比,execve()攻击面更小,不受环境变量影响;当在特权程序中调用外部程序时,应该使用execve()

其他方法:通过外部库、应用程序代码攻击

最后修改:2022 年 11 月 13 日
如果觉得我的文章对你有用,请随意赞赏