2023-2024-1 20232809《Linux内核原理与分析》第十二周作业

一、环境搭建

$ cd /home/shiyanlou
$ wget http://labfile.oss.aliyuncs.com/libcap-2.21.tar.gz
$ tar xvf libcap-2.21.tar.gz
$ sudo rm /usr/include/sys/capability.h
$ sudo rm /lib/libcap.so*
$ cd /home/shiyanlou/libcap-2.21/
$ sudo make
$ sudo make install


 

 掌握以下命令:

setcap: 给一个文件分配capabilities
getcap: 显示文件所带的capabilities
getpcaps: 显示线程所在的capabilities
这些命令在安全方面非常有用,因为它们允许管理员精确地控制进程的权限,而不必将其运行在完全特权的环境中。能力系统使得进程能够执行特定的操作,而无需具备 root 用户的完整权限。这对于提高系统的安全性和降低攻击面非常有帮助。


二、开始实验


        1、ping实验
        在一个capability系统中,当一个程序运行时,对应的线程会初始化一系列capabilities(令牌)。当线程尝试访问某个对象时,操作系统会检查该线程的capabilities,并决定是否授权访问。

        在操作系统中,有许多只允许超级用户使用的操作,比如配置网卡,备份所有用户文件,关闭计算机等,但如果要进行这些操作就必须先成为超级用户的话,那就违背了最小权限原则。

        Set-UID程序允许用户暂时以root权限进行操作,即使程序中所进行的权限操作用不到root权限的所有权利,这很危险:因为如果程序被入侵了的话,攻击者可能得到root权限。

        Capabilities将root权限分割成了权利更小的权限。小权限被称作capability。如果使用capabilities,那么攻击者最多只能得到小权限,无法得到root权限。这样,风险就被降低了。

        在kernel版本2.6.24之后,Capabilities可以分配给文件(比如程序文件),线程会自带程序文件被分配到的capabilities。

        下面这个例子演示capabilities如何移除root特权程序中的不必要的权利。

        首先,以普通用户登录并运行以下命令:

$ ping -c 3 www.baidu.com

        命令成功运行,如果你查看/bin/ping的属性会发现它是一个root所有的Set-UID程序。如果ping中包含漏洞,那么整个系统就可能被入侵。问题是我们是否能移除ping的这些权限。 

$ sudo su
# chmod u-s /bin/ping


        现在再ping百度:

# exit
$ ping www.baidu.com
ping: icmp open socket: Operation not permitted

        它会提示你操作不被允许。这是因为ping命令需要打开RAW套接字,该操作需要root特权,这就是为什么ping是Set-UID程序了。但有了capability,我们就可以杯酒释兵权了,让我们分配cap_net_raw给ping,看看会发生什么:

$ sudo su
# setcap cap_net_raw=ep /bin/ping
# exit
$ ping -c 3 www.baidu.com

 2、修改密码实验
         这一步证明一开始无法修改密码,但是在分配了cap之后就可以成功修改密码:

$ sudo su seed
$ sudo chmod u-s /usr/bin/passwd
$ passwd
$ sudo setcap cap_chown,cap_dac_override,cap_fowner=ep /usr/bin/passwd
$ passwd


        尝试修改:

         分配之后修改成功:

        3、调整权限实验
        跟使用ACL的访问控制相比,capabilities有其它优势:它可以动态调整大量线程的权限,这对于遵守最小权限原则是很有必要的。当线程中某个权限不再需要时,它应当移除所有相对应的capabilities。这样,即使线程被入侵了,攻击者也得不到已经被删除的capabilities。使用以下管理操作调整权限:

Deleting:线程永久删除某个capability
Disabling:线程会暂时停用某个capability。
Enabling:对应Disabling,启用capability。
        为了支持动态的capability分配,Linux使用一个与Set-UID相近的机制。举个例子,一个线程具有3组capability设置:允许(permitted P),可继承(inheritable I),和有效(effective E)。允许组由允许线程使用的cap组成,但其中的cap可能还没有激活。有效组由线程当前可以使用的cap组成。有效组是允许组的子集。线程可以随时更改有效组的内容只要不越过允许组的范围。可继承组是在程序运行exec()调用后计算新子线程的cap组用的。

        当一个线程fork新线程的时候,子线程的cap组从父线程拷贝。当在一个线程中运行一个新程序时,它的新cap组将根据以下公式计算:

pI_new = pI
pP_new = fP | (fI & pI)
pE_new = pP_new if fE = true
pE_new = empty if fE = false


        new后缀指新计算值,p前缀指线程,f前缀指文件cap。I,P,E分别指代 inheritable,permitted,effective,是一个cap位一个cap位计算的。

        切换到 /home/shiyanlou/libcap-2.21/libcap 目录下,编辑 cap_proc.c文件。

$ exit
$ cd /home/shiyanlou/libcap-2.21/libcap
$ sudo vi cap_proc.c


        为了让程序操作cap变得简单,添加以下三个函数到 /home/shiyanlou/libcap-2.21/libcap/cap_proc.c 中:

/

* Disable a cap on current process */
int cap_disable(cap_value_t capflag)
{
    cap_t mycaps;
    
    mycaps = cap_get_proc();
    if (mycaps == NULL)
        return -1;
    if (cap_set_flag(mycaps, CAP_EFFECTIVE, 1, &capflag, CAP_CLEAR) != 0)
        return -1;
    if (cap_set_proc(mycaps) != 0)
        return -1;
    return 0;
}
/* Enalbe a cap on current process */
int cap_enable(cap_value_t capflag)
{
    cap_t mycaps;
    
    mycaps = cap_get_proc();
    if (mycaps == NULL)
        return -1;
    if (cap_set_flag(mycaps, CAP_EFFECTIVE, 1, &capflag, CAP_SET) != 0)
        return -1;
    if (cap_set_proc(mycaps) != 0)
        return -1;
    return 0;
}
/* Drop a cap on current process */
int cap_drop(cap_value_t capflag)
{
    cap_t mycaps;
    
    mycaps = cap_get_proc();
    if (mycaps == NULL)
        return -1;
    if (cap_set_flag(mycaps, CAP_EFFECTIVE, 1, &capflag, CAP_CLEAR) != 0)
        return -1;
    if (cap_set_flag(mycaps, CAP_PERMITTED, 1, &capflag, CAP_CLEAR) != 0)
        return -1;
    if (cap_set_proc(mycaps) != 0)
        return -1;
    return 0;
}

        运行以下命令编译安装libcap:

$ sudo make
$ sudo make install    


         在 /home/shiyanlou/libcap-2.21/libcap 目录下新建一个 use_cap.c 文件,并分配cap_dac_read_search给它。

#include <fcntl.h>
#include <sys/types.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <linux/capability.h>
#include <sys/capability.h>
int main( void )
{
    if ( open( "/etc/shadow", O_RDONLY ) < 0 )
        printf( "(a) Open failed\n" );
 
    if ( cap_disable( CAP_DAC_READ_SEARCH ) < 0 )
        return(-1);
    if ( open( "/etc/shadow", O_RDONLY ) < 0 )
        printf( "(b) Open failed\n" );
 
    if ( cap_enable( CAP_DAC_READ_SEARCH ) < 0 )
        return(-1);
    if ( open( "/etc/shadow", O_RDONLY ) < 0 )
        printf( "(c) Open failed\n" );
 
    if ( cap_drop( CAP_DAC_READ_SEARCH ) < 0 )
        return(-1);
    if ( open( "/etc/shadow", O_RDONLY ) < 0 )
        printf( "(d) Open failed\n" );
 
    if ( cap_enable( CAP_DAC_READ_SEARCH ) == 0 )
        return(-1);
    if ( open( "/etc/shadow", O_RDONLY ) < 0 )
        printf( "(e) Open failed\n" );
 
}

 使用以下命令编译运行:

$ gcc -c use_cap.c
$ gcc -o use_cap use_cap.o -lcap
$ ./use_cap


        说明:        

        解释上面的输出很简单,在use_cap.c的执行顺序中,程序试图打开敏感文件/etc/shadow(a)时,程序以普通用户打开文件,没有权限(b)把CAP_DAC_READ_SEARCH隐去,实际上和(a)也是一样,但当到(c)及以后时,用户有了CAP_DAC_READ_SEARCH,这时候就可以打开文件了

三、课后问题
问题1: 当我们想动态调整基于ACL访问控制权限的数量时,应该怎么做?与capabilities比较哪种更加便捷?

ACL的使用:
http://vbird.dic.ksu.edu.tw/linux_basic/0410accountmanager_3.php
http://vbird.dic.ksu.edu.tw/linux_basic/0410accountmanager_3.php

ACL(Access Control Lists)和 capabilities 是两种不同的权限控制机制,各自有其优势和用途。

ACL:

动态性: ACL 允许您在文件或目录级别上定义详细的访问控制规则,包括用户、组、和其他特定主体的权限。这使得 ACL 在需要动态调整权限时更为灵活。
细粒度控制: ACL 允许您为不同的用户或组分配不同的权限,提供了更细粒度的控制。
复杂性: 管理 ACL 可能会变得复杂,特别是当需要处理大量用户和精细的权限设置时。
Capabilities:

简化权限模型: Capabilities 使用一个进程的能力集来定义其权限。这是一种更为简化的权限模型,允许进程执行特定的操作而无需完整的权限。
不同层次的权限: Capabilities 分为不同的类别,如 EFFECTIVE、INHERITABLE、和 PERMITTED。这使得它们可以提供一定程度的层次结构和控制。
不同用途: Capabilities 更适用于需要执行受限操作的进程,而不是文件或目录的访问控制。
使用 ACL 的情况:

当需要在文件或目录级别上进行动态和细粒度的权限设置时。
当需要为多个用户或组指定不同的权限。
 使用 capabilities 的情况:

当关注的是进程的执行权限而不是文件访问权限时。
当希望使用更简单的权限模型,而不涉及复杂的 ACL 设置。
问题2: 当程序(以普通用户运行)禁用cap A时,它遭到了缓冲区溢出攻击。攻击者成功注入恶意代码并运行。他可以使用cap A么? 如果线程删除了cap A呢,可以使用cap A么?

Capabilities(能力)是 Linux 系统中用于细粒度权限控制的一种机制,允许进程在不具备完整特权的情况下执行一些特定的操作。在这里,我将 cap A 视为一种特定的能力,而不是系统中实际存在的特定能力。实际上,Linux 的能力列表中没有明确的 "cap A",而是使用一系列预定义的能力标志。

在缓冲区溢出攻击成功注入恶意代码后,攻击者通常能够执行任意代码,这可能包括获取或修改进程的能力集。以下是一些可能的情况:

禁用 Cap A 的情况:

如果攻击者成功注入了恶意代码,他有可能执行能够禁用 "cap A" 的相关系统调用,例如 cap_disable。
一旦能力被禁用,攻击者将失去 "cap A" 的能力。
线程删除 Cap A 的情况:

如果攻击者注入的代码能够调用相关系统调用,例如 cap_disable 或 cap_drop,并成功删除 "cap A",那么攻击者将失去 "cap A" 的能力。
删除 "cap A" 意味着攻击者不能再使用 "cap A" 来执行特权操作。
需要注意的是,缓冲区溢出攻击是一种严重的安全问题,攻击者在成功注入恶意代码后通常可以执行几乎任何操作。此时,能力是系统的一部分,但攻击者的权限可能远远超出能力的范围。

综上所述,一旦攻击者成功注入了恶意代码,他们可能有能力执行一系列系统调用,包括改变或禁用进程的能力集。因此,能力只是安全机制的一部分,而不是完全阻止所有可能的攻击。在设计安全系统时,应该采用多层次的防御机制,包括但不限于能力、SELinux、ASLR 等。

问题3: 问题如上,改用竞态条件攻击。他可以使用cap A么? 如果线程删除了cap A呢,可以使用cap A么?

在竞态条件攻击中,攻击者利用并发执行中的竞态条件来达到其目的,例如修改敏感资源或获取不应该访问的权限。在这个背景下,无论是使用 cap A 还是删除 cap A,攻击者可能会面临一些特定的情况:

使用 Cap A 的情况:

如果攻击者成功地注入了竞态条件攻击的代码,并且该代码在竞争的条件下成功执行,那么攻击者可能在代码执行的上下文中拥有原始线程的权限,包括 cap A。
在这种情况下,攻击者可能可以使用 cap A 来执行相关的特权操作。
线程删除 Cap A 的情况:

如果竞态条件攻击的代码成功地删除了 cap A,并且在竞态条件下顺利执行,攻击者可能能够绕过 cap A 的限制。
在这种情况下,攻击者删除了 cap A,但是他们需要确保在竞争条件下成功地执行其恶意操作。
总体而言,竞态条件攻击是一种非常复杂和困难防范的攻击,因为它涉及到并发执行中的时序问题。攻击者可能需要精确地控制线程执行的时序,以确保其在特定条件下成功执行攻击代码。而无论是使用 cap A 还是删除 cap A,攻击者都需要非常深入地理解系统的内部工作方式,以便有效地实施竞态条件攻击。

防范竞态条件攻击的方法包括使用同步机制(如锁)来防止竞争条件、使用事务性操作来确保一系列步骤的原子性,以及对关键代码段进行仔细的审查和测试。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值