2019-2020-12 20199317 "Linux kernel principle and Analysis" in the twelfth week job

SET-UID program vulnerabilities experiment

Experiment 1 Introduction

        Set-UID is an important safety mechanism for Unix systems. When a Set-UID program is running, it is assumed to have the owner's permission. For example, if the owner of the program is the root, so when anyone running this program will receive the owner's permission procedures. Set-UID allows us to do a lot of very interesting things, but unfortunately, it's also a lot of bad things culprit.

       Therefore, this experiment has two goals:

       1. appreciate the bright side, to understand why the Set-UID is needed, and how it is executed.

       2. Note the bad side, understand its potential security issues.

2 Experimental content

2.1 Set-UID mechanism is not the case

      Guess why "passwd", "chsh", "su", and "sudo" command requires a Set-UID mechanism, these mechanisms if they do not, then what will happen. If you are not familiar with these programs, they can be familiar by reading the manual. If you copy these commands to its own directory, these programs would not be Set-UID program.

         

       As can be seen from the above screenshot: Copy the passwd to / tmp / under the authority change (suid bit is set in the original directory), permission to modify the copy without the password. For "chsh", "su", and "sudo" command, these programs are copied to the user directory, no longer has the same root.

       从这个方面看Set-UID是需要的:passwd命令有一个特殊的权限标记s ,存在于文件所有者的权限位上。这是一类特殊的权限SetUID ,可以这样来理解它:当一个具有执行权限的文件设置SetUID权限后,用户执行这个文件时将以文件所有者的身份执行。passwd命令具有SetUID权限,所有者为root(Linux中的命令默认所有者都是root),也就是说当普通用户使用passwd更改自己密码的时候,那一瞬间突然灵魂附体了,实际在以passwd命令所有者root的身份在执行,root当然可以将密码写入/etc/shadow文件(不要忘记root这个家伙是superuser什么事都可以干),命令执行完成后该身份也随之消失。原文链接:https://blog.csdn.net/Oo__YAN/article/details/7076889     

        setuid通过使passwd在执行阶段具有文件所有者(也就是root)的权限,让用户临时有了修改shadow文件的能力(当然这种能力是受到限制的)。因此,passwd就是一个经典的setuid程序,其UID是当前执行这个命令的用户ID,而EUID则是root用户的ID(也就是0)。

       但是Set-UID存在着安全性问题:使用SetUID可以灵活的调整所有文件所有者权限,但是也为系统的安全性带来了隐患。如果Root用户为指定的程序文件配置过大的SetUID权限,那么就会为黑客或者非法用户打开了侵入系统的大门。例如在Linux中可以使用“vi”命令来编辑文件,但是,对于普通用户而言,当其试图使用命令“vi /etc/shadow”来修改密码文件时,系统就会弹出“/etc/shadow : Permission Denied”的警告提示,从而禁止其对密码文件的非法修改。但是,如果在Root用户环境中执行“which vi”命令,就可以看到“vi”命令实际上是“vim”命令的别名,其真实路径为“/usr/bin/vim”,这样执行命令"chmod 6755 /usr/bin/vim",就可以将“vi”命令的所有者更改为Root,这样在普通的用户环境中,就可以使用“vi”命令来编辑任何文件(例如“/etc/shadow”),这样,即使是普通用户也可以将密码文件清空,从而实现无密码登陆Linux,给系统的安全带来的威胁不言而喻。因此,对可能给系统安全带来危害的程序来说,应该尽量不要随意为其配置SetUID权限。  原文链接:https://blog.csdn.net/frank_jb/article/details/40192003

2.2  运行 Set-UID 程序
         在linux环境下运行Set-UID 程序,同时描述并且解释你的观察结果。
       ●  以 root 方式登录,拷贝 /usr/bin/zsh 到 /tmp, 同时设置拷贝到tmp目录下的zsh为set-uid root权限,然后以普通用户登录,运行 /tmp/zsh。你会得到root权限吗?请描述你的结果。

         

       ●  拷贝 /bin/bash 到 /tmp 目录,同时设置 /tmp 目录下的bash为Set-UID root权限,然后以普通用户登录,运行 /tmp/bash。你会得到root权限吗?请描述你的结果。

        

       可见,同样的操作,运行复制的zsh可以获得root权限,而bash不能。

2.3  bash 内在保护机制

       从上面步骤可以看出,/bin/bash 有某种内在的保护机制可以阻止Set-UID机制的滥用。为了能够体验这种内在的保护机制出现之前的情形,我们打算使用另外一种shell程序—— /bin/zsh 。在一些linux的发行版中(比如Fedora和Ubuntu),/bin/sh 实际上是/bin/bash 的符号链接。为了使用zsh,我们需要把 /bin/sh 链接到 /bin/zsh。

       sh和bash是两个不同的shell,linux下有很多种shell。sh比较小,但是功能没有bash多,bash用得比较普遍 。/bin/sh是/bin/bash的软连接,在一般的linux系统当中,使用sh调用执行脚本相当于打开了bash的POSIX标准模式,也就是说 /bin/sh 相当于 /bin/bash --posix。/bin/sh执行过程中,若出现命令执行失败,则会停止执行;/bin/bash执行过程中,若命令执行失败,仍然会继续执行。

       在这里简单说一下软连接:软链接可以理解成快捷方式。它和windows下的快捷方式的作用是一样的,方便我们打开源文件,删除源文件,快捷方式也用不了。

       原文链接:https://blog.csdn.net/gao_zhennan/article/details/79127232

       下面的指令将会把默认的shell指向zsh:

       

2.4  PATH环境变量的设置
        system(const char * cmd)系统调用函数被内嵌到一个程序中执行一个命令,system()调用 /bin/sh 来执行shell程序,然后shell程序去执行cmd命令。但是在一个Set-UID程序中system()函数调用shell是非常危险的,这是因为shell程序的行为可以被环境变量影响,比如PATH;而这些环境变量可以在用户的控制当中。通过控制这些变量,用心险恶的用户就可以控制Set-UID程序的行为。
       下面的Set-UID程序被用来执行 /bin/ls 命令;然后程序员可以为ls命令使用相对路径,而不是绝对路径。在 /tmp 目录下新建 test.c 文件:

         

         ●  你能够设置这个Set-UID程序运行你自己的代码而不是 /bin/ls 吗?如果你能的话,你的代码具有root权限吗?描述并解释你的观察。

       可以具有root权限,把 /bin/sh 拷贝到 /tmp 目录下面重命名为 ls(先要确保 /bin/ 目录下的sh 符号链接到zsh,而不是bash),将环境变量PATH设置为当前目录 /tmp,运行编译的程序test。就可以获得root权限:

         

         我对上述过程的理解为:当输入./test 时,shell 进程通过执行代码中的system(), fork() 创建一个新的子进程,fork复制父进程的环境变量等,此时子进程的环境变量为 PATH=/tmp,子进程在这个环境变量下执行 system("ls"),而在这个环境变量下的 ls 代表着 /bin/sh ,链接至zsh,于是就获得了root权限。

        ●  先恢复环境变量 PATH ,然后修改 /bin/sh 使得其返回到 /bin/bash ,重复上面的攻击,你仍然可以获得root权限吗?描述并解释你的观察。

         

         可见修改sh连接回bash,运行test程序不能使普通用户获得 root 权限。

2.5  system()和execve()的不同

        首先确保 /bin/sh 指向zsh:

         

        然后使用如下命令恢复 PATH:

        

        背景:Bob 在一家审计代理处工作,他正在调查一家公司是否存在诈骗行为。为了这个目的,他需要阅读这家公司在 Unix 系统中的所有文件。为了保护系统的可靠性,他不能修改任何一个文件。为了达到这个目的,Vince——系统的超级用户为他写了一个SET-ROOT-UID程序,并且给了Bob可以执行它的权限。这个程序需要Bob在命令行中打出一个文件名,然后运行 /bin/cat 命令显示这个文件。既然这个程序是以root权限运行的,它就可以显示Bob想看的任何一个文件。然而,既然这个程序没有写操作,Vince很确信Bob不能用这个程序修改任何文件。首先在 /tmp 目录下新建 SRU.c 文件,内容如下:

         

         ●  程序中有 q=0。程序会使用system()调用命令行。这个命令安全吗?如果你是Bob,你能对系统的完整性妥协吗?你能重新移动一个对你没有写权限的文件吗?

        这个命令不安全,Bob可能会出于好奇或者个人利益驱使阅读或者修改只有root用户才可以运行的一些文件。

        比如截图中:file文件只有root用户有读写权限,但普通用户通过运行该程序,阅读并重命名了file文件:

         

          ●  如果令q=1;刚才的攻击还会有效吗?请描述并解释你的观察。

        修改为q=1后,不会有效。前面步骤之所以有效,是因为system()函数调用 /bin/sh,链接至zsh,具有root权限执行了 cat file 文件后,接着执行 mv file file_new 命令。

        而当令q=1, execve()函数会把 file; mv file file_new 看成是一个文件名,系统会提示不存在这个文件:

           

         system() 和exceve() 区别为:执行exec()后,老的进程上下文将被exec出来的新的进程上下文覆盖,新进程代替原进程执行。执行system()后则相当于fork()出一个子进程,并等待此子进程执行完毕。

2.6  LD_PRELOAD环境变量

        为了保证Set-UID程序在LD_PRELOAD环境的操纵下是安全的,动态链接器会忽略环境变量,但是在某些条件下是例外的,在下面的任务中,我们猜测这些特殊的条件到底是什么。

        1、让我们建立一个动态链接库。把下面的程序命名为 mylib.c ,放在 /tmp 目录下。在函数库 libc 中重载了sleep函数:

         

         2、我们用下面的命令编译上面的程序(注意区别l和1):

         

         3、把下面的程序命名为 myprog.c ,放在 /tmp 目录下:

         

        请在下面的条件下运行这些程序,并观察结果。基于这些观察告诉我们链接器什么时候会忽略LD_PRELOAD环境变量,解释原因。

        ●  把 myprog 编译成一个普通用户下的程序在普通用户下运行

       可见,它会使用LD_PRELOAD环境变量,重载sleep函数:

        

        ●  把 myprog 编译成一个Set-UID root的程序在普通用户下运行

      在这种情况下,忽略LD_PRELOAD环境变量,不重载sleep函数,使用系统自带的sleep函数:

        

         ●  把myprog编译成一个Set-UID root的程序在root下运行

       在这种情况下,使用LD_PRELOAD环境变量,使用重载的sleep函数:

         

         ●  在一个普通用户下把myprog编译成一个Set-UID 普通用户的程序在另一个普通用户下运行

       注意:需要先使用命令 rm myprog 把之前编译生成的 myprog 文件删掉

       在这种情况下,不会重载sleep函数:

       

        由以上四种情况可见:只有用户自己创建的程序自己去运行,才会使用LD_PRELOAD环境变量,重载sleep函数,否则的话忽略LD_PRELOAD环境变量,不会重载sleep函数。

2.7  消除和清理特权

       为了更加安全,Set-UID程序通常会调用setuid()系统调用函数永久的清除它们的root权限。然而有些时候,这样做是远远不够的。在root用户下,在 /tmp 目录新建一个空文件 zzz。在root用户下将下面代码命名为 test2.c ,放在 /tmp 目录下,编译这个程序,给这个程序设置root权限。在一个普通的用户下,运行这个程序。描述你所观察到的情况,/tmp/zzz 这个文件会被修改吗?解释你的观察。

       代码:

        

        结果如图:

        

        如图所示文件被修改了,原因在于设置 uid 前,zzz文件就已经被打开了。只要将语句 setuid(getuid()) 移至调用open函数之前,就能避免这个问题。

       setuid()用来重新设置执行目前进程的用户识别码。不过,要让此函数有作用,其有效的用户识别码必须为0(root)。在Linux下,当root 使用setuid()来变换成其他用户识别码时,root权限会被抛弃,完全转换成该用户身份,也就是说,该进程往后将不再具有可setuid()的权 利,如果只是向暂时抛弃root 权限,稍后想重新取回权限,则必须使用seteuid()。执行成功则返回0,失败则返回-1,错误代码存于errno。一般在编写具setuid root的程序时,为减少此类程序带来的系统安全风险,在使用完root权限后建议马上执行setuid(getuid());来抛弃root权限。此外,进程uid和euid不一致时Linux系统将不会产生core dump。

       内核会给每个进程关联两个和进程ID无关的用户ID,一个是真实用户ID,还有一个是有效用户ID或者称为setuid(set user ID)。真实用户ID用于标识由谁为正在运行的进程负责。有效用户ID用于为新创建的文件分配所有权、检查文件访问许可,还用于通过kill系统调用向其它进程发送信号时的许可检查。内核允许一个进程以调用exec一个setuid程序或者显式执行setuid系统调用的方式改变它的有效用户ID。

       原文链接:https://blog.csdn.net/todd911/article/details/7738069

3  实验总结

      在这次实验中,我通过实践论证了SET-UID这个程序的必要性,以及SET-UID会存在安全隐患,使用SET-UID程序获得root权限后,可以通过setuid()系统调用函数永久的清除它们的root权限,这是一种保护机制以阻止SET-UID的滥用,在实验中,我们也能看到有时候通过这种方式清除是远远不够的,当然还存在一些其他的保护机制,这里就不再介绍了。

 

Guess you like

Origin www.cnblogs.com/chengzhenghua/p/11999820.html