在Python中即使经过seteuid也无法放弃root权限,是个bug吗?
在Python中,根权限无法被取消,即使使用了seteuid。这是个bug吗?
编辑 总结:我忘记取消gid(组ID)了。不过,接受的答案可能对你有帮助。
你好。我在我的Linux上使用Python 3.2时,无法取消根权限。实际上,即使在执行seteuid(1000)后,它仍然可以读取根用户拥有的400权限的文件。euid确实被设置为1000了!
我发现,在调用空的os.fork()后,特权访问确实被拒绝了。(但这只在父进程中。子进程仍然可以不合法地读取文件。)这是Python的bug,还是Linux的问题呢?
试试下面的代码。把底部三行中的一行注释掉,然后以根用户身份运行。
提前谢谢你。
#!/usr/bin/python3
# Python seteuid pitfall example.
# Run this __as__ the root.
# Here, access to root-owned files /etc/sudoers and /etc/group- are tried.
# Simple access to them *succeeds* even after seteuid(1000) which should fail.
# Three functions, stillRoot(), forkCase() and workAround() are defined.
# The first two seem wrong. In the last one, access fails, as desired.
# ***Comment out*** one of three lines at the bottom before execution.
# If your python is < 3.2, comment out the entire def of forkCase()
import os
def stillRoot():
"""Open succeeds, but it should fail."""
os.seteuid(1000)
open('/etc/sudoers').close()
def forkCase():
"""Child can still open it. Wow."""
# setresuid needs python 3.2
os.setresuid(1000, 1000, 0)
pid = os.fork()
if pid == 0:
# They're surely 1000, not 0!
print('uid: ', os.getuid(), 'euid: ', os.geteuid())
open('/etc/sudoers').close()
print('open succeeded in child.')
exit()
else:
print('child pid: ', pid)
open('/etc/group-').close()
print('parent succeeded to open.')
def workAround():
"""So, a dummy fork after seteuid is necessary?"""
os.seteuid(1000)
pid = os.fork()
if pid == 0:
exit(0)
else:
os.wait()
open('/etc/group-').close()
## Run one of them.
# stillRoot()
# forkCase()
# workAround()
1 个回答
在Unix系统上处理进程的凭证是个复杂的事情。我强烈建议你先搞清楚真实用户ID、有效用户ID和保存用户ID之间的关系。搞错了“降低权限”的操作是很容易出错的。
关于你提到的具体情况……我在想,可能有个简单的原因你没注意到。你的代码在进行不一致的测试,而且你没有明确指定/etc/sudoers
和/etc/group-
文件的具体权限。如果/etc/sudoers
的权限是440,用户是root,组也是root(这是我系统上的默认权限),而/etc/group-
的权限是400,那么你的代码表现得就会和你描述的一样。
你并没有修改进程的组ID,所以如果/etc/sudoers
是可被组读取的,那就能解释为什么它总是可读的。fork()
并不会修改进程的凭证。不过,在你的示例代码中,它似乎会这样做,因为你在父进程和子进程中检查了不同的文件。如果/etc/group-
没有组读取权限,而/etc/sudoers
有,那就能解释你所看到的问题。
如果你只是想“降低权限”,可以使用以下代码:
os.setgid( NEW_GID )
os.setuid( NEW_UID )
一般来说,只有在你的进程需要在运行过程中切换根权限时,才想要修改有效用户ID。如果你只是需要用根权限做一些设置操作,而在这些操作完成后不再需要根权限,那就用上面的代码来永久性地降低权限。
哦,还有一个在Linux上处理进程凭证的有用调试工具,就是打印/proc/self/status
的输出。这个文件中的Uid和Gid行显示了当前进程的真实、有效、保存的用户ID和文件ID(按这个顺序)。虽然可以用Python的API获取相同的信息,但你可以把这个文件的内容视为“真实数据”,这样可以避免使用Python跨平台API可能带来的复杂问题。