我正在 Debian Gnu/Linux 上试验各种功能。
我已将 /bin/ping 复制到当前工作目录。果然不行,原来是setuid root。
然后,我通过执行 ping 操作来赋予我的 ping 最小功能(不是 root)sudo /sbin/setcap cap_net_raw=ep ./ping
,并且我的 ping 可以按预期工作。
然后sudo /sbin/setcap -r ./ping
撤销该能力。现在它没有按预期工作。
我现在尝试使用 ping 来工作capsh
。
capsh
没有权限,所以我需要以 root 身份运行它,然后删除 root 权限,从而删除所有其他权限。
我想我还需要secure-keep-caps
,这没有记录在 中capsh
,但在功能手册中。我从 得到了位数/usr/include/linux/securebits.h
。它们看起来是正确的,因为 的输出--print
显示这些位是正确的。
我已经摆弄了几个小时,到目前为止我已经有了这个。
sudo /sbin/capsh --keep=1 --secbits=0x10 --caps="cap_net_raw+epi" == --secbits=0x10 --user=${USER} --print -- -c "./ping localhost"
然而ping
,ping: icmp open socket: Operation not permitted
当它不具备该功能时会发生这种情况。同时也--print
表明Current: =p cap_net_raw+i
,这还不够我们所需要的e
。
sudo /sbin/capsh --caps="cap_net_raw+epi" --print -- -c "./ping localhost"
将能力设置为此Current: = cap_net_raw+eip
是正确的,但将我们保留为root
.
编辑-1
我现在已经尝试过sudo /sbin/capsh --keep=1 --secbits=0x11 --caps=cap_net_raw+epi --print -- -c "touch zz; ./ping -c1 localhost;"
这会产生:
touch: cannot touch `zz': Permission denied
ping: icmp open socket: Operation not permitted
第一个错误预计为secure-noroot: yes
但第二个错误不是Current: = cap_net_raw+eip
编辑2
如果我放在==
之前--print
,它现在显示Current: = cap_net_raw+i
,这样就解释了之前的错误,但不是为什么我们在切换出 root 时会失去能力,我认为这secure-keep-caps
应该可以解决这个问题。
编辑3
据我所见,当调用 exec 时,我失去了有效(e)和允许(p)。这是预料之中的,但我认为安全保存帽应该可以防止它们丢失。我是不是错过了什么。
编辑4
我做了更多研究,并再次阅读了手册。当您从用户切换(或 apply ,从而使 root 成为普通用户)时,通常e
和功能似乎会丢失,这可以用;覆盖当你打电话时,据我所知这是一个不变量。p
root
secure-noroot
secure-keep-caps
exec
据我所知,它正在按照手册工作。据我所知,没有办法做任何有用的事情capsh
。据我所知,要使用功能,您需要:使用文件功能或拥有功能感知程序,该程序不使用exec
.因此没有特权包装器。
所以现在我的问题是我错过了什么,我capsh
的目的是什么。
编辑5
我添加了有关环境功能的答案。也许capsh
也可以与继承的功能一起使用,但为了有用,需要在可执行文件上设置这些功能。我不明白 capsh 如何在没有环境功能或允许继承功能的情况下做任何有用的事情。
版本:
capsh
从包libcap2-bin
版本1:2.22-1.2
- 在 edit-3 之前,我获取了最新版本
capsh
并git://git.debian.org/collab-maint/libcap2.git
开始使用它。 uname -a
Linux richard-laptop 3.2.0-4-amd64 #1 SMP Debian 3.2.65-1+deb7u2 x86_64 GNU/Linux
用户空间是32位的。
答案1
能力是流程的属性。传统上有三组:
- 允许的能力(p):可以在当前进程中“激活”的功能。
- 有效能力(e):当前进程中当前可用的能力。
- 可遗传的能力(我):可以继承的文件能力。
以 root 身份运行的程序始终具有完全允许且有效的功能,因此“添加”更多功能不会产生明显的效果。 (可继承的功能集通常为空。)setcap cap_net_raw+ep ping
默认情况下,您将为运行此程序的任何用户启用这些功能。
不幸的是,这些功能绑定到执行的文件,并且在执行新的子进程后不会保留。 Linux 4.3 推出环境能力它允许子进程继承功能。 (也可以看看execve() 期间功能的转换在能力(7).)
在使用功能时,请注意以下陷阱:
- 当将用户从 root 更改为非 root 时,有效和允许的权限将被清除(请参见用户 ID 更改对功能的影响在能力(7))。您可以使用
--keep=1
选项来capsh
避免清除集合。 - 更改用户或组 ID 时,环境功能集将被清除。解决方案:添加环境功能后更改用户ID,但是前执行子进程。
- 仅当某个功能已同时位于允许的功能集和可继承的功能集中时,才能将其添加到环境功能集中。
从 libcap 2.26 开始,capsh
程序获得了通过以下选项修改环境功能的能力--addamb
:犯罪)。请注意,选项顺序很重要。用法示例:
sudo capsh --caps="cap_net_raw+eip cap_setpcap,cap_setuid,cap_setgid+ep" \
--keep=1 --user=nobody --addamb=cap_net_raw -- \
-c "./ping -c1 127.0.0.1"
提示:您可以--print
在命令行中的任意位置添加该选项capsh
并查看其当前的功能状态。
注意:cap_setpcap
是必需的,--addamb
而选项cap_setuid,cap_setgid
则需要--user
。
答案2
Lekensteyn 的答案似乎准确且完整,但我将尝试从不同的角度提供另一种解释,试图强调环境功能集解决的问题。
当您运行时,sudo capsh --user=<some_user> --
有 2 个系统调用会导致重新计算(并可能删除)功能:
setuid
: 根据man capabilities
:
SECBIT_KEEP_CAPS 设置此标志允许具有一个或多个 0 UID 的线程在将其所有 UID 切换为非零值时保留其功能。如果未设置此标志,则此类 UIDswitch 会导致线程失去所有功能。
换句话说,在capsh
上面的命令中,我们需要确保在setuid
系统调用期间设置了 SECBIT_KEEP_CAPS。否则所有能力都会丧失。这就是它--keep=1
所做的。所以现在命令变成sudo capsh --user=<some_user> --keep=1 --
execve
:如果我们使用该--keep=1
选项,则保留所有功能集(有效、允许、可继承)直到......为止但是,该execve
系统调用execve
也会导致重新计算权限(对于非 root 用户),而且方式不太明显。简而言之,在添加环境功能集之前,对于调用后线程“允许”集中的功能execve
,可以:- 该文件必须在其“允许”集中具有该功能。这可以通过 来完成
setcap cap_net_raw+p /bin/bash
。这样做会使整个练习变得毫无用处,因为线程的功能集(除了边界集之外)不再有任何效果。 - 文件和线程都必须在其“可继承”集中具有该功能。您可能认为这
setcap cap_net_raw+i
可以解决问题,但事实证明,execve
当非特权用户调用时,这会导致线程的固有权限被删除(我们目前要感谢这一点setuid
)。所以作为非特权用户没有办法满足这个条件。
- 该文件必须在其“允许”集中具有该功能。这可以通过 来完成
Linux 4.3 中引入的环境功能使得线程即使在 asetuid
给非特权用户后跟一个, 也能保留其功能execve
。无需依赖文件功能。
答案3
对 Lekensteyn 的答案进行轻微调整,可以缩短最近内核的调用时间:
sudo /usr/sbin/capsh --keep=1 --user=$USER \
--inh=cap_net_raw --addamb=cap_net_raw -- \
-c './ping -c1 localhost'
注意:根据您的 sudoers 文件,这可能会使您的环境变得混乱(例如更改 HOME)。 capsh 会更改您的 uid,但不会执行任何操作来恢复 sudo 的环境更改。
那么这是怎么回事呢?我们来看一下:
sudo /usr/sbin/capsh
:我们从 root 开始,它在其有效(可以执行此操作)和允许(可以将其添加到有效)集中具有所有功能,但在其他集中没有任何功能。我们稍后将讨论其他集合。--keep=1
:出于安全(读取:遗留)原因,功能通常不会跨根->非根 ID 交换机继承。该标志启用了一项称为 的功能SECBIT_KEEP_CAPS
,可以实现此目的。值得注意的是,它会在 exec 时自动清除,这是一个好主意。--user=$USER
:既然我们不会失去 UID 更改的所有功能,我们就退出 root 状态。感谢SECBIT_KEEP_CAPS
,我们保留了类似 root 的权限,这让我们进一步混乱了我们的能力。--inh=cap_net_raw
:这会将我们的目标功能添加到可继承集中,因为如果功能不可继承,则无法将其设置为环境功能(请参阅下一项)。--addamb=cap_net_raw
:即使我们已经请求SECBIT_KEEP_CAPS
,execve
非特权(无 setuid/setgid/setcap)二进制文件也会仍然明确我们的能力,导致没有特权。 Linux 4.3 添加了环境集,在执行非特权二进制文件时,该环境集会添加回有效集和允许集。完美的!-- -c ...
:设置完所有内容后,我们使用这些参数执行 bash。功能集被清除(因为 bash 没有特权),环境集被添加回来,瞧!我们拥有打开原始套接字所需的权限。
您可以使用 capsh 的特殊==
参数来检查这一点,这会导致它使用命令行的其余部分执行自身:
sudo /usr/sbin/capsh --keep=1 --user=$USER \
--inh=cap_net_raw --addamb=cap_net_raw == --print
Current: = cap_net_raw+eip
这意味着我们将 cap_net_raw 视为有效(可以执行)、可继承(可以将其传递给子进程)和允许(允许获取)。其中没有任何其他功能。
有关功能及其工作方式的更多信息,您最好的选择是功能(7) 手册页。具体是标题Transformation of capabilities during execve()
。
答案4
该setpriv
命令比 .com 更容易处理行为capsh
。考虑使用它来代替您问题的运行时功能部分。对零件没有太大影响setcap
。
$ setpriv --help
Usage:
setpriv [options] <program> [<argument>...]
Run a program with different privilege settings.
现在,ping
作为 Fedora 32 上的非特权用户,我可以正常工作:
$ cp /usr/bin/ping .
$ ./ping -c 1 ::1
PING ::1(::1) 56 data bytes
64 bytes from ::1: icmp_seq=1 ttl=64 time=0.030 ms
所以我选择了tcptraceroute
:
$ tcptraceroute ::1
Running:
traceroute -T -O info ::1
You do not have enough privileges to use this traceroute method.
socket: Operation not permitted
与
$ sudo setpriv --no-new-privs --inh-caps '-all,+net_raw' --bounding-set '-all,+net_raw' tcptraceroute ::1
Running:
traceroute -T -O info ::1
traceroute to ::1 (::1), 30 hops max, 80 byte packets
1 localhost (::1) <rst,ack> 0.041 ms 0.010 ms 0.007 ms
请注意,该命令没有广泛提升的权限:
$ sudo setpriv --no-new-privs --inh-caps '-all,+net_raw' --bounding-set '-all,+net_raw' touch /root/secret
touch: cannot touch '/root/secret': Permission denied
$ sudo setpriv --no-new-privs --inh-caps '-all,+net_raw' --bounding-set '-all,+net_raw' sudo touch /root/secret
sudo: PERM_SUDOERS: setresuid(-1, 1, -1): Operation not permitted
sudo: no valid sudoers sources found, quitting
sudo: error initializing audit plugin sudoers_audit
$ sudo setpriv --no-new-privs --inh-caps '-all,+net_raw' --bounding-set '-all,+net_raw' id
uid=0(root) gid=0(root) groups=0(root) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023