增强Linux内核中访问控制安全的方法

  


  

  

前段时间,我们的项目组在帮客户解决一些操作系统安全领域的问题,涉及到windows, Linux, macOS三大操作系统平台。无论什么操作系统,本质上都是一个软件,任何软件在一开始设计的时候,都不能百分之百的满足人们的需求,所以操作系统也是一样,为了尽可能的满足人们需求,不得不提供一些供人们定制操作系统的机制。当然除了官方提供的一些机制,也有一些黑魔法,这些黑魔法不被推荐使用,但是有时候面对具体的业务场景,可以作为一个参考的思路。

  

  

本文着重介绍Linux平台上常见的拦截:

  
      <李>用户态动态库拦截。   <李>内核态系统调用拦截。   <李>堆栈式文件系统拦截。   <李>内联钩拦截。   <李> LSM (Linux安全模块)   
  


  

  

Linux上的动态库劫持主要是基于LD_预加载环境变量,这个环境变量的主要作用是改变动态库的加载顺序,让用户有选择的载入不同动态库中的相同函数。但是使用不当就会引起严重的安全问题,我们可以通过它在主程序和动态连接库中加载别的动态函数,这就给我们提供了一个机会,向别人的程序注入恶意的代码。

  

假设有以下用户名密码验证的函数:

        # include & lt; stdio.h>   # include & lt; string.h>   # include & lt; stdlib.h>   int主要(int命令行参数个数,char * * argv)   {   char passwd[]=懊苈搿?   如果(命令行参数个数& lt;2){   printf("无效的命令行参数个数! \ n ");   返回;   }   如果(!比较字符串(passwd, argv [1])) {   printf("正确的密码! \ n ");   返回;   }   printf("无效的密码! \ n ");   } 之前      

我们再写一段hookStrcmp的程序,让这个比较永远正确。

        # include & lt; stdio.h>   int strcmp (const char * s1, s2 const char *)   {/*永远返回0,表示两个字符串相等*/返回0;   } 之前      

依次执行以下命令,就会使我们的钩程序先执行。

        - wall gcc - fpic - shared - o hookStrcmp。所以hookStrcmp.c   出口LD_PRELOAD=" ./hookStrcmp.so”      

结果会发现,我们自己写的比较字符串函数优先被调用了。这是一个最简单的劫持,但是如果劫持了类似于geteuid/getuid/getgid,让其返回0,就相当于暴露了根权限,所以为了安全起见,一般将LD_预加载环境变量禁用掉。

  


  

  

最近发现在4.4.0的内核中有513多个系统调用(很多都没用过),系统调用劫持的目的是改变系统中原有的系统调用,用我们自己的程序替换原有的系统调用.Linux内核中所有的系统调用都是放在一个叫做sys_叫_table的内核数组中,数组的值就表示这个系统调用服务程序的入口地址。整个系统调用的流程如下:

  

增强Linux内核中访问控制安全的方法”> <br/>
  </p>
  <p>当用户态发起一个系统调用时,会通过80软中断进入到系统调用夹头,进而进入全局的系统调用表sys_ _table打电话去查找具体的系统调用,那么如果我们将这个数组中的地址改成我们自己的程序地址,就可以实现系统调用劫持。但是内核为了安全,对这种操作做了一些限制:</p>
  <ul>
  <李> sys_叫_table的符号没有导出,不能直接获取。</李>
  <李> sys_叫_table所在的内存页是只读属性的,无法直接进行修改。</李>
  </ul>
  <p>对于以上两个问题,解决方案如下(方法不止一种):</p>
  <ul>
  <李>获取系统调用表的地址:grep sys _叫_table/boot/系统。李map-uname - r </>
  <李>控制页表只读属性是由CR0寄存器的WP位控制的,只要将这个位清零就可以对只读页表进行修改。</李>
  </ul>
  
  <pre类=   *//*使页面可写的   int make_rw(无符号长地址)   {   unsigned int水平;   pte_t * pte=lookup_address(地址,和水平);//查找虚拟地址所在的页表地址   pte→pte |=_PAGE_RW;//设置页表读写属性   返回0;   } 之前         /*使页面写保护*/int make_ro(无符号长地址)   {   unsigned int水平;   pte_t * pte=lookup_address(地址,和水平);   pte→pte,=~ _PAGE_RW;//设置只读属性   返回0;   } 之前      


  

  

增强Linux内核中访问控制安全的方法