You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何setuid切换用户后无法访问/proc/self/fd/N?

问题解析:/proc/self/fd/N访问权限疑问

测试代码

多行Python代码

import os
f=os.open("/etc/shadow", os.O_RDONLY)
os.setuid(65535)
os.open(f"/proc/self/fd/{f}", os.O_RDONLY)

单行执行命令

python3 -c 'import os; f=os.open("/etc/shadow", os.O_RDONLY); os.setuid(65535); os.open(f"/proc/self/fd/{f}", os.O_RDONLY)'

核心疑问

  • 以root身份打开/etc/shadow后切换到nobody用户(UID 65535),尝试通过/proc/self/fd/N重新打开该文件时得到-EACCES错误,但根据proc_fd_permission的注释预期应该成功,原因是什么?
  • proc_fd_permission的源码注释实际想表达什么?

补充疑问

若权限限制仅针对符号链接本身而非目标文件,为何能通过/proc/self/fd/N访问套接字和已删除文件?例如执行以下命令能正常输出hello:

exec 3>foo; echo hello >&3; rm foo; cat /proc/self/fd/3

问题解答

为什么重新打开/proc/self/fd/N会失败?

核心在于**open()系统调用处理/proc/self/fd/N的逻辑**:当调用open("/proc/self/fd/N", O_RDONLY)时,内核会先解析这个符号链接到原文件路径(比如/etc/shadow),然后对原文件路径执行权限检查——这个检查基于当前进程的有效UID/GID,而非你是否持有该文件描述符。

切换到UID 65535后,尽管进程还握着/etc/shadow的文件描述符,但重新打开时,内核会以nobody的身份去检查/etc/shadow的权限,显然nobody没有读取权限,因此返回-EACCES。

proc_fd_permission注释的实际含义

proc_fd_permission只负责**/proc/self/fd/N这个符号链接本身的权限校验**,比如允许你用readlink()获取它指向的路径,只要进程拥有对应的文件描述符,就能通过这个符号链接的权限检查。但它管不到后续open()目标文件的步骤——后者的权限检查是独立的,完全基于当前用户对目标文件的访问权限。注释想表达的是符号链接本身的访问规则,而非重新打开目标文件的规则。

关于套接字和已删除文件的例外情况

对于套接字、已删除文件这类特殊对象,内核无法通过/proc/self/fd/N解析出一个有效的、可做权限检查的常规文件路径:

  • 套接字没有对应的文件系统路径,内核找不到可检查权限的目标,因此直接允许通过文件描述符对应的/proc/self/fd/N访问。
  • 已删除文件的原路径已经失效,内核无法定位到原文件的inode来做路径权限检查,这时会直接使用文件描述符关联的inode权限(而你原本就持有该文件的打开权限),因此可以正常访问。

这类场景不存在“重新检查原路径权限”的前提,所以能绕过常规的路径权限校验,直接通过/proc/self/fd/N访问。


内容的提问来源于stack exchange,提问作者Helmut Grohne

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 01:57:37