为了安全保护,apache默认的编译选项是不支持root作为其服务运行的用户的,而应使用apache用户来运行。但在某些情况下,我们确实有这样的需求,如:编写了某个CGI程序,其中需要调用/sbin下的命令,挂载某个设备文件、修改iptables参数等。下面我们利用suid权限来解决,提供两个方法。
(注意,一旦给脚本赋予suid权限,让apache可运行root权限的程序时,务必做好安全检查,特别是由客户端输入的信息。)
一、apache 默认状态
1、先来看看apache的配置文件
引用
# cat /etc/httpd/conf/httpd.conf
......
User apache
Group apache
......
可见,是使用apache用户和组来运行httpd服务的。
2、验证
写个CGI脚本:
# cat /var/www/cgi-bin/cgitest.pl
#!/usr/bin/perl -w
use strict;
use CGI qw(:all);
use CGI::Carp qw(fatalsToBrowser);
print header (-charset=>"gb2312");
my $HTML=qx(id);
print $HTML;
print "
End";
运行结果:
可见,运行的用户确实是apache(48) 。
3、测试
修改一些CGI脚本,让其运行一个需要root权限的命令:
......
my $HTML=qx(id);
print $HTML,"
";
$HTML=qx(/sbin/iptables -L);
print $HTML;
print "
End";
命令行下,使用root用户运行该脚本:
引用
# ./cgitest.pl
Content-Type: text/html; charset=gb2312
uid=0(root) gid=0(root) groups=0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel)
Chain INPUT (policy ACCEPT)
target prot opt source destination
ACCEPT tcp -- anywhere anywhere tcp dpt:rfe
Chain FORWARD (policy ACCEPT)
target prot opt source destination
Chain OUTPUT (policy ACCEPT)
target prot opt source destination
再来看看浏览器上运行的结果:
HTML源码是:
引用
uid=48(apache) gid=48(apache) groups=48(apache)
End
运行命令的结果没有显示出来,看看httpd服务器上的后台日志:
引用
10.8.1.10 - - [17/Mar/2009:11:29:06 +0800] "GET /cgi-bin/cgitest.pl HTTP/1.1" 200 59
[Tue Mar 17 11:34:04 2009] [error] [client 10.8.1.10] iptables v1.3.5:
[Tue Mar 17 11:34:04 2009] [error] [client 10.8.1.10] can't initialize iptables table `filter': Permission denied (you must be root)
[Tue Mar 17 11:34:04 2009] [error] [client 10.8.1.10]
10.8.1.10 - - [17/Mar/2009:11:34:04 +0800] "GET /cgi-bin/cgitest.pl HTTP/1.1" 200 59
信息非常清楚了,因为是使用apache用户来运行httpd上的CGI程序,所以权限不足。
二、解决问题
要解决这个问题,可以使用suid权限。
1、借用一个第三方代理
由于shell本身不能直接利用suid特性,所以,需要使用一个第三方的代理来完成。下面是一个用C写的程序:
(程序源码来自:)
# cat run_root.c
#include
#include
#include
#include
int main()
{
uid_t uid ,euid;
uid = getuid() ;
euid = geteuid();
//printf("my uid :%u\n",getuid()); //这里显示的是当前的uid 可以注释掉.
//printf("my euid :%u\n",geteuid()); //这里显示的是当前的euid
if(setreuid(euid, uid)) //交换这两个id
perror("setreuid");
//printf("after setreuid uid :%u\n",getuid());
//printf("afer sertreuid euid :%u\n",geteuid());
//system("/sbin/iptables -L"); //执行iptables -L命令
system("/var/www/cgi-bin/test.sh"); //执行一个需root权限运行的脚本
return 0;
}
关键在于,利用该程序交换执行用户的uid和程序的euid,以实现apache用户执行root权限的功能。因程序中没有提供参数,可以把需要root权限执行的命令写在某个脚本(test.sh)中一同运行。该脚本的内容:
# cat /var/www/cgi-bin/test.sh
#!/bin/bash
echo 'From test.sh:
'
/usr/bin/id
echo '
'
/sbin/iptables -L
编译生成run_root程序:
# gcc -o run_root -Wall run_root.c
修改CGI脚本,调用run_root插件:
......
print "From CGI scripts:
";
my $HTML=qx(id);
print $HTML,"
";
$HTML=qx(/var/www/cgi-bin/run_root);
print $HTML;
print "
End";
◎ 调用前,先来看看run_root的用户和权限:
引用
# ll run_root
-rwxr-xr-x 1 root root 5162 03-17 12:00 run_root
看看命令行的调用结果:
# ./cgitest.pl
引用
Content-Type: text/html; charset=gb2312
From CGI scripts:
uid=0(root) gid=0(root) groups=0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel)
From test.sh:
uid=0(root) gid=0(root) groups=0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel)
Chain INPUT (policy ACCEPT)
target prot opt source destination
ACCEPT tcp -- anywhere anywhere tcp dpt:rfe
Chain FORWARD (policy ACCEPT)
target prot opt source destination
Chain OUTPUT (policy ACCEPT)
target prot opt source destination
看看浏览器的显示:
似乎还是不行哦。
◎ 给run_root赋予suid权限
引用
# chmod u+s run_root
# ll run_root
-rwsr-xr-x 1 root root 5162 03-17 12:00 run_root
再看看浏览器的结果:
成功了。看看图中红色标记的地方,运行test.sh命令时,是作为root(0)用户执行的,权限正确。
阅读(2787) | 评论(0) | 转发(0) |