setuid()
。在Linux中,你只需要CAP_SETUID
(和/或CAP_SETGID
)capability即可切换到任何用户。其次,setuid()
和setgid()
可以在真实(执行进程的用户)、有效(setuid/setgid二进制文件的所有者)和保存的标识之间更改进程身份。创建一个Unix域套接字对,用于服务和辅助程序之间的特权通信。
分叉。
在子进程中关闭所有多余的文件描述符,仅保留套接字对的一端。将标准输入、输出和错误重定向到/dev/null
。
在父进程中关闭子进程的套接字对端。
在子进程中执行特权辅助二进制文件。
父进程发送一个简单消息,可能没有任何数据,但包含其凭据的辅助信息。
辅助程序等待来自服务的初始消息。当它收到后,检查凭据。如果凭据不符合要求,立即退出。
辅助程序生成一个(加密安全的)伪随机数,比如1024位,并将其发送回父进程。
父进程将该数字发送回去,但再次添加了其凭据的附属消息。
辅助程序验证UID
、GID
和PID
未更改,并且/proc/PID/exe
仍然指向正确的服务守护程序二进制文件。(我会仅重复完整的检查。)
在第8步中,辅助程序已经确认套接字的另一端正在执行应该执行的二进制文件。发送一个要求对方返回的随机cookie,意味着对方不能事先通过消息“填充”套接字。当然,这假设攻击者无法提前猜测伪随机数。如果您想要谨慎,可以从/dev/random
读取一个合适的cookie,但请记住这是有限资源(如果内核中没有足够的随机性可用,则可能会阻塞)。 我个人会从/dev/urandom
中读取比如1024位(128字节)并使用它。
setresgid()
和setresuid()
和/或initgroups()
更改身份,将工作目录更改为适当的位置,并执行用户二进制文件。父助手进程关闭辅助消息中的文件描述符,并等待下一条消息。
如果助手在套接字不再有输入时退出,则当服务退出时,它将自动退出。
如果有足够的兴趣,我可以提供一些示例代码。有很多细节需要注意,所以编写代码有点繁琐。但是,正确编写的话,它比例如Apache SuEXEC更安全。
exec()
。
root
切换到非特权用户,然后通过[保存的用户ID机制](http://en.wikipedia.org/wiki/User_identifier#Saved_user_ID)切换回来。 - David Schwartz