我想要一个使用 gmail 帐户发送电子邮件的 hg hook。显然,我不希望除了我或 root 之外的任何人能够读取电子邮件发送脚本,因为它有密码,所以我尝试了以下方法:
-rwsr-xr-x 1 james james 58 Feb 18 12:05 incoming.email.sh
-rwx--x--x 1 james james 262 Feb 18 12:04 send-incoming-email.sh
incoming.email.sh
作为钩子执行的文件在哪里:
#! /bin/bash
/path/to/send-incoming-email.sh
但是,当我尝试以另一个用户身份运行时,出现错误:
/bin/bash: /path/to/send-incoming-email.sh: Permission denied
当我以自己的身份运行时该send-incoming-email.sh
文件运行正常。
我尝试做的事情是否可行,或者 setuid 是否不会传播到从 shell 脚本执行的命令?
系统是Ubuntu 10.04.2 LTS。
答案1
如果你需要你的解决方案按原样工作,一个简单的方法是使用一个简短的 C 程序而不是 shell 脚本:
int main(){
setuid(geteuid());
system("/path/to/send-incoming-email.sh");
}
并具有该 setuid,从而避免竞争条件,同时允许您以 root 身份执行脚本。
到目前为止,这不是最好的解决方案,但它可以解决所描述的问题。
答案2
Linux 将忽略setuid
shell 脚本的位以避免可能出现的竞争条件。
在 Unix/Linux 系统上发送电子邮件的“正确”方式是配置 MTA,例如后缀,Exim4或者发送邮件并让它处理 SMTP 身份验证问题。还有“仅中继”MTA -邮件传输协议,邮件传输协议,邮件传输协议. 所有这些都可以通过身份验证进行 SMTP 中继(“智能主机”),例如通过 Gmail 服务器。在多用户机器上会变得更加棘手,但仍然可行。
(配置 MTA 后,发送电子邮件是通过将数据传递给来完成的/usr/sbin/sendmail rcpt@address
。)
答案3
几乎所有系统都会忽略脚本中的setuid
和setgid
位。(这不是错误或疏忽,而是一项重要的安全功能;稍后会详细介绍。)
常见的解决方法是使用小型setuid
和/或setgid
二进制包装器。@jeremy-sturdivant 的答案中的基本版本是一个好的开始,但它不允许您传递任何参数。为此,您需要在修改它以指向实际脚本后将其传递argv
给execve
:
#include <sys/types.h>
#include <stdio.h> /* perror */
#include <unistd.h> /* execve, geteuid, setuid */
int main(int argc, char **argv, char **envp) {
setuid(geteuid());
*argv = "/real/path/to/script";
execve(*argv, argv, envp);
perror(*argv);
return 127;
}
如果您想要setgid
,setuid
只需将该行更改setuid...
为:
setgid(getegid());
这仍然相当基础,并且缺少setuid
/周围的错误检查setgid
。它还依赖于安装程序(您)检查是否存在其他泄漏,例如可写的祖先目录。
但是为什么在脚本中被忽略了呢setuid
?setgid
首先,了解调用脚本时发生的情况很有帮助。
与任何程序一样,脚本由execve
内核调用调用,它接受 3 个参数:(filename
一个字符串)、argv
(一个字符串数组)和envp
(另一个字符串数组)。按照惯例argv[0]
与“相同” filename
,但这只是一个近似值;它可能会省略或更改路径,并且/或者它可能-
在其前面加上一个以表明这应该是新登录会话的开始。(envp
包含环境变量;对于本次讨论来说并不重要。)
和参数通常会不加改变argv
地envp
传递给新程序中的argv
和参数。envp
main
但是如果程序文件以 开头#!
,则内核将读取该文件的第一行以获取解释器文件名,最多一个解释器选项.然后它将修改提供的argv
参数:
- 原来的
argv[0]
将被丢弃并替换为filename
,然后 - 该
filename
参数设置为解释器文件名。 - 在前面插入一个或两个元素
argv[]
,解释器文件名,以及解释器选项(如果给出)。
然后execve
用这些新的论点重新开始。
这意味着当解释器启动时,脚本文件名main
作为 的普通元素传递到其中argv
,解释器只需打开该文件名并开始读取它。
setuid
这意味着在系统调用期间检查位execve
和脚本解释器打开脚本进行读取之间存在短暂的间隔。在此期间,攻击者可以filename
用自己的脚本替换,然后使用新的 UID 或 GID 运行。
等一下,您说的是“几乎所有”系统。其他系统呢?
一些系统在execve
系统调用期间打开脚本并使用 /dev/fd/ 调用解释器3setuid
作为脚本的“名称”。这意味着或操作不会带来任何安全风险setgid
,但它的缺点$0
是在脚本中用处不大。