软件安全课程实验1 Environment Variable and Set-UID Program

Environment Variable and Set-UID Program Lab

个人博客地址

Task 1

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lQgVbauV-1643259278308)(环境变量和set-UID程序/image-20211021190558511.png)]

  • 使用printenv打印环境变量

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-v8SVDa1i-1643259278309)(环境变量和set-UID程序/image-20211021190734705.png)]

  • 使用export设置环境变量的值,用echo查看设置结果

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1ExKZbij-1643259278309)(环境变量和set-UID程序/image-20211021192046493.png)]

  • 使用unset删除环境变量,用echo查看设置结果

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5FROSkNP-1643259278310)(环境变量和set-UID程序/image-20211021192112022.png)]

Task 2

Step 1.

首先编译myprintenv.c文件

然后结果保存在file

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Dzhrq6Ct-1643259278310)(环境变量和set-UID程序/image-20211021192345242.png)]

观察file文件,我们可以发现是各个环境变量的值

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XF4S7Bkt-1643259278311)(环境变量和set-UID程序/image-20211021192715013.png)]

Step 2

按照要求注释掉子进程的printenv语句,将父进程的printenv语句取消注释

重新编译,将结果保存在file2文件中,发现同样是环境变量的值

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kfp4pe0G-1643259278311)(环境变量和set-UID程序/image-20211021194639135.png)]

Step 3

用diff命令比较file和file2的差异,发现两者是相同的,说明子进程完全继承了父进程的环境变量。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-32j0dC2b-1643259278312)(环境变量和set-UID程序/image-20211021194823695.png)]

进一步查阅资料可知

子进程完全复制了父进程的资源,包括进程上下文、代码区、数据区、堆区、栈区、内存信息、打开文件的文件描述符、信号处理函数、进程优先级、进程组号、当前工作目录、根目录、资源限制和控制终端等信息,而子进程与父进程的区别有进程号、资源使用情况和计时器等。

Task 3

Step 1

编译运行myenv.c,发现结果为空

#include <unistd.h>

extern char **environ;

int main()
{
  char *argv[2];

  argv[0] = "/usr/bin/env";
  argv[1] = NULL;

  execve("/usr/bin/env", argv, NULL);  

  return 0 ;
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YMMlIoBM-1643259278313)(环境变量和set-UID程序/image-20211021195206499.png)]

Step 2

将代码修改为如下方所示的代码后,再次编译运行

#include <unistd.h>

extern char **environ;

int main()
{
  char *argv[2];

  argv[0] = "/usr/bin/env";
  argv[1] = NULL;

  //execve("/usr/bin/env", argv, NULL);  
  execve("/usr/bin/env", argv, environ);

  return 0 ;
}

发现程序打印出了环境变量的值。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Kqmfyb7t-1643259278313)(环境变量和set-UID程序/image-20211021195323867.png)]函数execve()的作用,其调用格式如下:
int execve(const char * filename,char * const argv[],char * const envp[])
其有三个参数,第一个参数filename指向要运行的新程序的路径,第二个参数argv数组包含新程序的所有参数,第三个参数envp数组包含新程序的环境变量。

如果将envrion传给execve()函数,则新进程想将他自己的环境变量传给新程序。如果设为NULL,则不传递任何环境变量

Step 3

进程在被初始化时通过以下两种方式获取环境变量。

第一种方式,如果是一个新创建的进程,即使用fork()系统调用(在UNLX中)生成的进程,在这种情况下,子进程的内存是父进程内存的副本,也就是说,子进程将继承父进程所有的环境变量。

第二种方式,如果进程自身通过 execve()系统调用运行一个新的程序(而不是在子进程中运行),进程的内存将会被新程序的数据覆盖,因此进程中存储的所有环境变量将会丢失。如果一个进程希望将环境变量传递给新运行的程序,它需要在调用 execve()函数时显式地传递环境变量。

Task 4

保存编译运行下方的代码,可以发现结果是打印出了环境变量

#include <stdio.h>
#include <stdlib.h>
int main()
{
	system("/usr/bin/env");
	return0 ;
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AQ6UvdJr-1643259278314)(环境变量和set-UID程序/image-20211021202226212.png)]

system()的函数说明

system()会调用fork()产生子进程,由子进程来调用/bin/sh-c string来执行参数string字符串所代表的命令,此命令执行完后随即返回原调用的进程。在调用system()期间SIGCHLD 信号会被暂时搁置,SIGINT和SIGQUIT 信号则会被忽略。

返回值

如果fork()失败 返回-1:出现错误

如果exec()失败,表示不能执行Shell,返回值相当于Shell执行了exit(127)

如果执行成功则返回子Shell的终止状态

如果system()在调用/bin/sh时失败则返回127,其他失败原因返回-1。若参数string为空指针(NULL),仅当命令处理程序可用时,返回非零值,可以通过这一特征判断在一个给定的操作系统上是否支持system函数(当system函数返回值为0时,表明system函数无效,在UNIX系统中,system函数总是可用的);。如果system()调用成功则最后会返回执行shell命令后的返回值,但是此返回值也有可能为 system()调用/bin/sh失败所返回的127,因此最好能再检查errno 来确认执行成功。

Task 5

Step 1

#include <stdio.h>
#include <stdlib.h>
extern char**environ;
int main()
{
int i = 0;
    while (environ[i] != NULL) {
        printf("%s\n", environ[i]);
        i++;
	}
}

将代码保存在foo.c中

编译运行,打印出环境变量

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-njxDi9LV-1643259278315)(环境变量和set-UID程序/image-20211021203257312.png)]

Step 2

将程序的拥有者改为root, 同时将其变成set-UID程序

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EPAbkmQu-1643259278315)(环境变量和set-UID程序/image-20211021204849038.png)]

Step 3

按照题目要求设置三个环境变量,然后运行已经是特权程序的foo。

我们可以看到wza和PATH环境变量都成功出现,但是LD_LIBRARY_PATH始终没有出现在环境变量中

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-s1K2PejP-1643259278316)(环境变量和set-UID程序/image-20211022110105522.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-W07nu1h8-1643259278316)(环境变量和set-UID程序/image-20211022110154632.png)]

运行foo单独抓去LD变量,可以发现输出为空白,没有找到

我们将foo的拥有者改回seed ,发现LD_LIBRARY_PATH这个出现了。

这说明这个环境变量会对动态链接器的行为产生影响,所以它在动态链接器的进程中被屏蔽掉了,从而不会再对特权程序的动态链接产生影响。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zkkx0Fin-1643259278317)(环境变量和set-UID程序/image-20211022110351472.png)]

Task 6

运行代码

task6.c

#include <stdio.h>
#include <stdlib.h>
int main()
{
	system("ls");
	return 0;
}

恶意代码

ls.c

#include <stdlib.h>
int main(){
	system("/bin/bash -p");

将运行代码编译后保存为task6可执行文件

将恶意代码编译保存为ls,保存在当前文件夹中

首先我们执行task6,此时正常调用/bin/ls指令

然后我们改变task6的所有者为root,同时设置其为特权程序,然后将当前文件夹包含在环境变量PATH中,再次执行我们发现程序得到的只是一个普通的shell

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-S2uu4a4r-1643259278317)(环境变量和set-UID程序/image-20211024152521937.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4kpbq1hY-1643259278318)(环境变量和set-UID程序/image-20211022115545732.png)]

阅读资料后我们可以知道,这是由于Ubuntu20.04的保护机制导致的,我们采用sudo ln -sf /bin/zsh /bin/sh将/bin/sh指向一个特别的shell的程序即可

再次执行,我们发现程序成功创建了一个具有root权限的shell程序。

实验原理是system会调用/bin/sh来执行命令,而shell会在PATH中搜索要使用的命令,所以当我们把我们的恶意程序放在当前目录,并且把当前目录加到环境变量PATH中时,当执行task6就会调用我们的恶意代码,从而成功创建一个shell进程,又因为task6是一个具有root权限的特权程序,所以我们创建的是一个具有root权限的shell进程。

Task 7

Step 1

mylib.c

#include <stdio.h>
void sleep (int s)
{
	/*If this is invoked by a privileged program,
	you can do damages here!*/
	printf("I am not sleeping!\n");
}

按照题目要求编译但不链接mylib.c

创建一个新的共享库,并将该共享库加入LD_PRELOAD环境变量。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1szK4KpL-1643259278318)(环境变量和set-UID程序/image-20211022170152068.png)]

myprog.c

编译该文件,保存可执行文件为myprog

/*myprog.c*/
#include <unistd.h>
int main()
{
	sleep(1);
	return 0;
}

Step 2

  • 正常运行myprog

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FJuaRwKL-1643259278319)(环境变量和set-UID程序/image-20211022172148790.png)]

    程序行为被改变,调用了mylib.c 的sleep程序

  • 将myprog设置为具有root权限的set-UID程序后再次执行

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9ZrKp1ao-1643259278320)(环境变量和set-UID程序/image-20211022172502079.png)]

    程序行为正常,正常调用了lib中的sleep语句 ,睡眠了1s后返回。

  • 在root权限下再次export LD_LIBRARY变量,然后再次执行

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OYOXdmAC-1643259278320)(环境变量和set-UID程序/image-20211022182847388.png)]

    发现程序行为再次被改变,调用了mylib.c 的sleep程序

  • 要求在另一个用户下运行seed用户的set-UID程序,并重新export LD_LIBRARY变量

    首先利用sudo su 进入root用户

    然后用sudo adduser seed2 创建一个新的用户seed2

    然后su seed2切换到 seed2

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cady3DUh-1643259278320)(环境变量和set-UID程序/image-20211022185658486.png)]

    运行root权限的set-UID程序,程序正常执行lib库的sleep函数,睡眠1s

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-L34XHaFL-1643259278321)(环境变量和set-UID程序/image-20211022190037146.png)]

    返回seed用户,将程序改为seed用户的特权程序,然后重新重新export LD_LIBRARY变量

    运行,程序依旧正常执行lib库的sleep函数,睡眠1s

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-l1gEauPs-1643259278322)(环境变量和set-UID程序/image-20211022190532236.png)]

Step 3

Linux动态链接器会在一些默认目录中寻找程序所使用的库,用户可以在LD_PRELOAD和LD_LIBRARY_PATH环境变量来增加新的搜索目录和库文件

我们设置了LD_PRELOAD后可以让链接器将sleep()函数和我们的代码mylib.c连接起来,而不是和标准库libc库中的sleep()函数链接起来。

所以这导致第一种情况下正常运行myprog程序行为的改变

而在第二种情况下,我们将程序改为root权限的特权程序后正常执行,这是因为动态链接库的一些防御措施,当进程的真实用户ID和有效用户ID不一样,进程将忽略LD_PRELOAD环境变量。

在第三种情况下,我们将用户改为root, 然后在运行具有root权限的特权程序后,程序再次改变了行为,调用了mylib.c代码,这是因为真实用户ID和有效用户ID一样,导致防御措施失效了

在第四种情况下,我们将用户改为seed2,挺好运行然后在运行具有seed用户的特权程序,程序依旧正常执行,防御措施有效。

实验验证

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7bp8nPDc-1643259278322)(环境变量和set-UID程序/image-20211022192624573.png)]

我们复制eve到 当前目录为myenv,然后将其设置为root权限的特权程序,之后设置三个LD变量,用env打印,可以发现三个变量都出现了,用myenv打印发现只出现了一个变量LD_MYOWN

这就是因为那两个变量对动态链接器的行为会产生影响,所以他们在Set-UID程序中被屏蔽了,从而不会再对特权程序的动态链接产生任何影响,而LD_MYOWN是自己定义的,不会被动态链接器使用,所以没有安全威胁,因此没有被屏蔽。

Task 8

Step 1

#include <string.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc,char *argv[])
{
    char * v[3];
    char * command;
    if(argc < 2) {
        printf("Please type a filename.\n");
        return 1;
    }
    v[0] ="/bin/cat"; v[1] = argv[1]; v[2] = NULL;
    command = malloc(strlen(v[0]) + strlen(v[1]) + 2);
    sprintf(command,"%s%s", v[0], v[1]);
    //Useonlyoneofthefollowings.
    system(command);
    //execve(v[0],v,NULL);
    return 0 ;
}

编译catall文件,然后将其设置为root权限的特权程序

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GOEkedFa-1643259278322)(环境变量和set-UID程序/image-20211022195314688.png)]

执行如下图所示的指令,可以发现程序创建了一个shell进程,但最开始我们只得到了一个普通的shell程序,这是由于Ubuntu20.04的一个保护机制导致的,我们采用

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

来使得/bin/sh指向/bin/zsh

再次调用catall “aa;/bin/sh",我们发现这次创建了一个具有root权限的shell进程,用cat /etc/shadow 指令验证,发现成功输出密码,说明我们权限窃取成功。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-57fcPYh3-1643259278323)(环境变量和set-UID程序/image-20211022195229974.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-g0LESf3N-1643259278323)(环境变量和set-UID程序/image-20211022200809708.png)]

Step 2

注释掉system(),取消注释execve()

#include <string.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc,char *argv[])
{
    char * v[3];
    char * command;
    if(argc < 2) {
        printf("Please type a filename.\n");
        return 1;
    }
    v[0] ="/bin/cat"; v[1] = argv[1]; v[2] = NULL;
    command = malloc(strlen(v[0]) + strlen(v[1]) + 2);
    sprintf(command,"%s%s", v[0], v[1]);
    //Useonlyoneofthefollowings.
    //system(command);
    execve(v[0],v,NULL);
    return 0 ;
}

编译修改后的代码保存为safecatall

依旧将其设置为root权限的特权程序,再次运行safecatall “aa;/bin/sh"我们发现程序没有出现错误

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tD1arnMz-1643259278323)(环境变量和set-UID程序/image-20211022201948533.png)]

第一步中我们得到root权限的原因是因为system(command)函数是通过调用"/bin/sh -c command"完成command指令的,换句话说,外部指令不是上述程序直接执行的,而是shell程序首先被执行,然后shell将command作为输入并解析。但是因为shell过于强大,它可以解析用分号隔开的两条命令,所以当我们输入 “aa;/bin/sh"是,分号后面的指令也被解析并执行了,由此我们得到了一个root权限的shell。

而使用execve()函数则不会如此,因为它会把那整个字符串作为参数,所以不会发生权限泄露的情况。

Task 9

cap_leak.c

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>

void main()
{
  int fd;
  char *v[2];

  /* Assume that /etc/zzz is an important system file,
   * and it is owned by root with permission 0644.
   * Before running this program, you should create
   * the file /etc/zzz first. */
  fd = open("/etc/zzz", O_RDWR | O_APPEND);        
  if (fd == -1) {
     printf("Cannot open /etc/zzz\n");
     exit(0);
  }

  // Print out the file descriptor value
  printf("fd is %d\n", fd);

  // Permanently disable the privilege by making the
  // effective uid the same as the real uid
  setuid(getuid());                                

  // Execute /bin/sh
  v[0] = "/bin/sh"; v[1] = 0;
  execve(v[0], v, 0);                             
}

上述代码展示了一个root用户的Set-UID程序。程序运行分为三步。第一步,它打开了一个只有root用户可以修改的文件/etc/zz。在文件被打开后,程序定义了一个文件描述符,通过该文件描述符完成后续对文件的操作。文件描述符是权限的一种形式,任何拥有它的人都可以访问对应的文件。第二步,通过将有效用户ID(roo)变得跟真实用户ID一样,程序降低了自身的权限,实际上相当于放弃了进程的root特权。第三步,程序调用了一个 shell 程序

然而上述程序忘记了关闭文件,文件描述符仍然有效,因此这个非特权进程仍然可以修改/etc/zzz文件。从程序的执行结果来看,可以发现文件描述符的值是3.通过echo>&3”命令可以修改/etc/zz文件。这里“&3”表示文件描述符3. 在运行这个 SET-UID程序之前,无法修改受保护的/ete/zz文件。但是通过 SET-UID程序获得文件描述符后,可以成功地修改该文件。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MeRrGwX2-1643259278324)(环境变量和set-UID程序/image-20211022204515258.png)]

为了修复该程序中的权限泄露问题,应该在降低特权之前先销毁权限,用close(fd)关闭文件描述符即可。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值