from:https://www.cnblogs.com/iamfy/archive/2012/09/20/2694977.html
一)概述:
1)从2.1版开始,Linux内核有了能力(capability)的概念,即它打破了UNIX/LINUX操作系统中超级用户/普通用户的概念,由普通用户也可以做只有超级用户可以完成的工作.
2)capability可以作用在进程上(受限),也可以作用在程序文件上,它与sudo不同,sudo只针对用户/程序/文件的概述,即sudo可以配置某个用户可以执行某个命令,可以更改某个文件,而capability是让某个程序拥有某种能力,例如:
capability让/tmp/testkill程序可以kill掉其它进程,但它不能mount设备节点到目录,也不能重启系统,因为我们只指定了它kill的能力,即使程序有问题也不会超出能力范围.
3)每个进程有三个和能力有关的位图:inheritable(I),permitted(P)和effective(E),对应进程描述符task_struct(include/linux/sched.h)里面的cap_effective, cap_inheritable, cap_permitted,所以我们可以查看/proc/PID/status来查看进程的能力.
4)cap_effective:当一个进程要进行某个特权操作时,操作系统会检查cap_effective的对应位是否有效,而不再是检查进程的有效UID是否为0.
例如,如果一个进程要设置系统的时钟,Linux的内核就会检查cap_effective的CAP_SYS_TIME位(第25位)是否有效.
5)cap_permitted:表示进程能够使用的能力,在cap_permitted中可以包含cap_effective中没有的能力,这些能力是被进程自己临时放弃的,也可以说cap_effective是cap_permitted的一个子集.
6)cap_inheritable:表示能够被当前进程执行的程序继承的能力.
二)capability的设定与清除
我们在下面的程序中给当前的进程设定能力,最后我们清除掉所设定的能力,源程序如下:
?1?#include?<stdio.h>
?2?#include?<stdlib.h>
?3?#include?<string.h>
?4?#include?<sys/types.h>
?5?#include?<unistd.h>
?6??
?7?#undef?_POSIX_SOURCE
?8?#include?<sys/capability.h>
?9??
10?extern?int?errno;
11???
12?void?whoami(void)
13?{
14???printf("uid=%i??euid=%i??gid=%i\n",?getuid(),?geteuid(),?getgid());
15?}
16??
17?void?listCaps()
18?{
19???cap_t?caps?=?cap_get_proc();
20???ssize_t?y?=?0;
21???printf("The?process?%d?was?give?capabilities?%s\n",
22??????????(int)?getpid(),?cap_to_text(caps,?&y));
23???fflush(0);
24???cap_free(caps);
25?}
26???
27?int?main(int?argc,?char?**argv)
28?{
29???int?stat;
30???whoami();
31???stat?=?setuid(geteuid());
32???pid_t?parentPid?=?getpid();
33??
34???if(!parentPid)
35?????return?1;
36???cap_t?caps?=?cap_init();
37??
38??
39???cap_value_t?capList[5]?=
40???{?CAP_NET_RAW,?CAP_NET_BIND_SERVICE?,?CAP_SETUID,?CAP_SETGID,CAP_SETPCAP?}?;
41???unsigned?num_caps?=?5;
42???cap_set_flag(caps,?CAP_EFFECTIVE,?num_caps,?capList,?CAP_SET);
43???cap_set_flag(caps,?CAP_INHERITABLE,?num_caps,?capList,?CAP_SET);
44???cap_set_flag(caps,?CAP_PERMITTED,?num_caps,?capList,?CAP_SET);
45??
46???if?(cap_set_proc(caps))?{
47?????perror("capset()");
48??
49?????return?EXIT_FAILURE;
50???}
51???listCaps();
52?
53???printf("dropping?caps\n");
54???cap_clear(caps);??//?resetting?caps?storage
55??
56???if?(cap_set_proc(caps))?{
57?????perror("capset()");
58?????return?EXIT_FAILURE;
59???}
60???listCaps();
61?
62???cap_free(caps);
63???return?0;
64?
65?}
编译:
gcc capsettest.c -o capsettest -lcap
运行:
./capsettest?
uid=0 ?euid=0 ?gid=0
The process 2383 was give capabilities = cap_setgid,cap_setuid,cap_setpcap,cap_net_bind_service,cap_net_raw+eip
dropping caps
The process 2383 was give capabilities =
注:
1)我们对该进程增加了5种能力,随后又清除了所有能力.
2)首先通过cap_init()初始化存放cap能力值的状态,随后通过cap_set_flag函数的调用,将三种位图的能力设置给了变量caps,再通过cap_set_proc(caps)设定当前进程的能力值,通过cap_get_proc()返回当前进程的能力值,最后通过cap_free(caps)释放能力值.
3)cap_set_flag函数的原型是:
int cap_set_flag(cap_t cap_p, cap_flag_t flag, int ncap,const cap_value_t *caps, cap_flag_value_t value);
我们这里的调用语句是:cap_set_flag(caps, CAP_PERMITTED, num_caps, capList, CAP_SET);
第一个参数cap_p是存放能力值的变量,是被设定值.这里是caps.
第二个参数flag是是三种能力位图,这里是CAP_PERMITTED.
第三个参数ncap是要设定能力的个数,这里是num_caps,也就是5.
第四个参数*caps是要设定的能力值,这里是capList数组,也就是CAP_NET_RAW, CAP_NET_BIND_SERVICE , CAP_SETUID, CAP_SETGID,CAP_SETPCAP.
第五个参数value是决定要设定还是清除,这里是CAP_SET.
4)cap_set_proc函数的原型是:int cap_set_proc(cap_t cap_p);
cap_set_proc函数通过cap_p中的能力值设定给当前的进程.
5)cap_get_proc函数的原型是:cap_t cap_get_proc(void);
cap_get_proc函数返回当前进程的能力值给cap变量.
6)cap_free函数的原型是:cap_free(caps);
cap_free函数清理/释放cap变量.
7)如果我们fork()了子进程,那么子进程继承父进程的所有能力.
8)不能单独设定CAP_EFFECTIVE,CAP_INHERITABLE位图,必须要和CAP_PERMITTED联用,且CAP_PERMITTED一定要是其它两个位图的超集.
9)如果两次调用cap_set_proc函数,第二次调用的值力值不能少于或多于第一次调用.如第一次我们授权chown,setuid能力,第二次只能是chown,setuid不能是其它的能力值.
10)普通用户不能给进程设定能力.
三)进程的能力掩码:
我们可以通过下面的程序获取当前进程的掩码,它是通过capget函数来获取指定进程的能力掩码,当然我们也可以用capset来设定掩码,下面获取掩码的体现:
?1?#undef?_POSIX_SOURCE
?2?#include?<stdlib.h>
?3?#include?<stdio.h>
?4?#include?<sys/types.h>
?5?#include?<unistd.h>
?6?#include?<linux/capability.h>
?7?#include?<errno.h>
?8?
?9?int?main()
10?{
11??????struct?__user_cap_header_struct?cap_header_data;
12??????cap_user_header_t?cap_header?=?&cap_header_data;
13?
14??????struct?__user_cap_data_struct?cap_data_data;
15??????cap_user_data_t?cap_data?=?&cap_data_data;
16?
17??????cap_header->pid?=?getpid();
18??????cap_header->version?=?_LINUX_CAPABILITY_VERSION_1;
19?
20??????if?(capget(cap_header,?cap_data)?<?0)?{
21??????????perror("Failed?capget");
22??????????exit(1);
23??????}
24??????printf("Cap?data?0x%x,?0x%x,?0x%x\n",?cap_data->effective,
25??????????cap_data->permitted,?cap_data->inheritable);
26?}
gcc capget0.c -o capget0 -lcap
普通用户:
./capget0?
Cap data 0x0, 0x0, 0x0
超级用户:
/home/test/capget0?
Cap data 0xffffffff, 0xffffffff, 0x0
这也说明了默认情况下,root运行的进程是什么权限都有,而普通用户则什么权限都没有.
我们可以将本程序与上面的程序进行整合,如下:
?1?#include?<stdio.h>
?2?#include?<stdlib.h>
?3?#include?<string.h>
?4?#include?<sys/types.h>
?5?#include?<unistd.h>
?6??
?7?#undef?_POSIX_SOURCE
?8?#include?<sys/capability.h>
?9??
10?extern?int?errno;
11???
12?void?whoami(void)
13?{
14???printf("uid=%i??euid=%i??gid=%i\n",?getuid(),?geteuid(),?getgid());
15?}
16??
17?void?listCaps()
18?{
19???cap_t?caps?=?cap_get_proc();
20???ssize_t?y?=?0;
21???printf("The?process?%d?was?give?capabilities?%s\n",
22??????????(int)?getpid(),?cap_to_text(caps,?&y));
23???fflush(0);
24???cap_free(caps);
25?}
26??
27??
28?int?main(int?argc,?char?**argv)
29?{
30???int?stat;
31???whoami();
32???stat?=?setuid(geteuid());
33???pid_t?parentPid?=?getpid();
34??
35???if(!parentPid)
36?????return?1;
37???cap_t?caps?=?cap_init();
38??
39???cap_value_t?capList[5]?=
40???{?CAP_NET_RAW,?CAP_NET_BIND_SERVICE?,?CAP_SETUID,?CAP_SETGID,CAP_SETPCAP?}?;
41???unsigned?num_caps?=?5;
42???cap_set_flag(caps,?CAP_EFFECTIVE,?num_caps,?capList,?CAP_SET);
43???cap_set_flag(caps,?CAP_INHERITABLE,?num_caps,?capList,?CAP_SET);
44???cap_set_flag(caps,?CAP_PERMITTED,?num_caps,?capList,?CAP_SET);
45??
46???if?(cap_set_proc(caps))?{
47?????perror("capset()");
48??
49?????return?EXIT_FAILURE;
50???}
51???listCaps();
52?
53???cap_free(caps);
54?
55???struct?__user_cap_header_struct?cap_header_data;
56???cap_user_header_t?cap_header?=?&cap_header_data;
57?
58???struct?__user_cap_data_struct?cap_data_data;
59???cap_user_data_t?cap_data?=?&cap_data_data;
60?
61???cap_header->pid?=?getpid();
62???cap_header->version?=?_LINUX_CAPABILITY_VERSION_1;
63?
64???if?(capget(cap_header,?cap_data)?<?0)?{
65???????perror("Failed?capget");
66???????exit(1);
67???}
68???printf("Cap?data?0x%x,?0x%x,?0x%x\n",?cap_data->effective,
69???cap_data->permitted,?cap_data->inheritable);
70?
71???sleep(60);
72???return?0;
73?}
编译并执行:
gcc capsettest.c -o capsettest -lcap
./capsettest
uid=0 ?euid=0 ?gid=0
The process 3101 was give capabilities = cap_setgid,cap_setuid,cap_setpcap,cap_net_bind_service,cap_net_raw+eip
Cap data 0x25c0, 0x25c0, 0x25c0
注:0x25c0=10 0101 1100 0000(二进制)
对映的能力如下:
cap_setgid=6(位)
cap_setuid=7(位)
cap_setpcap=8(位)
cap_net_bind_service=10(位)
cap_net_raw=13(位)
在程序sleep的时候,我们查看一下进程的status,如下:
cat /proc/`pgrep capsettest`/status
略
CapInh: 00000000000025c0
CapPrm: 00000000000025c0
CapEff: 00000000000025c0
CapBnd: ffffffffffffffff
略
我们看到进程的status也反映了它的能力状态.
CapBnd是系统的边界能力,我们无法改变它.
四)capability的工具介绍
在我们的试验环境是RHEL6,libcap-2.16软件包中包含了相关的capability设置及查看工作,如下:
rpm -ql libcap-2.16-5.2.el6.i686?
/lib/libcap.so.2
/lib/libcap.so.2.16
/lib/security/pam_cap.so
/usr/sbin/capsh
/usr/sbin/getcap
/usr/sbin/getpcaps
/usr/sbin/setcap
/usr/share/doc/libcap-2.16
/usr/share/doc/libcap-2.16/License
/usr/share/doc/libcap-2.16/capability.notes
/usr/share/man/man8/getcap.8.gz
/usr/share/man/man8/setcap.8.gz
getcap可以获得程序文件所具有的能力(CAP).
getpcaps可以获得进程所具有的能力(CAP).
setcap可以设置程序文件的能力(CAP).
我们下面主要用setcap来进行调试.
五)CAP_CHOWN 0(允许改变文件的所有权)
授权普通用户可以用/bin/chown程序更改任意文件的owner,如下:
setcap cap_chown=eip /bin/chown?
查看/bin/chown程序的能力值,如下:
getcap /bin/chown ? ? ? ? ? ? ??
/bin/chown = cap_chown+eip
切换到test用户,将/bin/ls程序的owner改为test,如下:
su - test
chown test.test /bin/ls
ls -l /bin/ls
-rwxr-xr-x. 1 test test 118736 Jun 14 ?2010 /bin/ls
注:
1)cap_chown=eip是将chown的能力以cap_effective(e),cap_inheritable(i),cap_permitted(p)三种位图的方式授权给相关的程序文件.
2)如果改变文件名,则能力保留到新文件.
3)用setcap -r /bin/chown可以删除掉文件的能力.
4)重新用setcap授权将覆盖之前的能力.?
六)CAP_DAC_OVERRIDE 1(忽略对文件的所有DAC访问限制)
授权普通用户可以用/usr/bin/vim程序修改所有文件的内容,如下:
setcap cap_dac_override=eip /usr/bin/vim?
切换到普通用户
su - test
修改/etc/shadow文件内容
vim /etc/shadow
root:$6$3hJf.BoIVU/cdLKb$JxLXcQScrLS032aFPAQvVc4RzKYNadcIIzxmzAIw.jejrYOHhqdr0oV7sNBL.IhGBo.mMOYEdevlnCp2OGku8.:15094:0:99999:7:::
bin:*:14790:0:99999:7:::
daemon:*:14790:0:99999:7:::
adm:*:14790:0:99999:7:::
注:
DAC_OVERRIDE能力是DAC_READ_SEARCH能力的超集.
七)CAP_DAC_READ_SEARCH 2(忽略所有对读、搜索操作的限制)
授权普通用户可以用/bin/cat程序查看所有文件的内容,如下:
setcap cap_dac_read_search=eip /bin/cat
切换到普通用户
su - test
查看/etc/shadow,如下:
cat /etc/shadow
root:$6$3hJf.BoIVU/cdLKb$JxLXcQScrLS032aFPAQvVc4RzKYNadcIIzxmzAIw.jejrYOHhqdr0oV7sNBL.IhGBo.mMOYEdevlnCp2OGku8.:15094:0:99999:7:::
bin:*:14790:0:99999:7:::
daemon:*:14790:0:99999:7:::
adm:*:14790:0:99999:7:::
八)CAP_FOWNER 3(以最后操作的UID,覆盖文件的先前的UID)
cp /etc/passwd /tmp/
ls -l /tmp/passwd ??
-rw-r--r-- 1 root root 1171 2011-04-29 19:21 /tmp/passwd
授权cap_fowner权限给/usr/bin/vim
setcap cap_fowner=eip /usr/bin/vim
切换到test用户
su - test
编辑/tmp/passwd文件,存盘退出.
vi /tmp/passwd
修改文件,并保存退出.
查看/tmp/passwd,发现owner已经变成test
-rw-r--r-- 1 test test 1176 2011-04-29 19:21 /tmp/passwd
九)CAP_FSETID 4(确保在文件被修改后不修改setuid/setgid位)
起因是当文件被修改后,会清除掉文件的setuid/setgid位,而设定CAP_FSETID后将保证setuid/setgid位不被清除.但这对chown函数无用.
测试程序如下:
?1?#include?<sys/types.h>
?2?#include?<sys/types.h>
?3?#include?<sys/stat.h>
?4?#include?<fcntl.h>
?5?#include?<sys/stat.h>
?6?#include?<stdio.h>
?7?#include?<stdlib.h>
?8?#include?<string.h>
?9?main()
10?{
11?????????int?handle;
12?????????char?string[40];
13?????????int?length,?res;
14?
15?????????if?((handle?=?open("/tmp/passwd",?O_WRONLY?|?O_CREAT?|?O_TRUNC,S_IREAD?|?S_IWRITE))?==?-1)
16?????????{
17?????????????????printf("Error?opening?file.\n");
18?????????????????exit(1);
19?????????}
20?????????strcpy(string,?"Hello,?world!\n");
21?????????length?=?strlen(string);
22?????????if?((res?=?write(handle,?string,?length))?!=?length)
23?????????{
24?????????????????printf("Error?writing?to?the?file.\n");
25?????????????????exit(1);
26?????????}
27?????????printf("Wrote?%d?bytes?to?the?file.\n",?res);
28?????????close(handle);
29?}
gcc fsetid.c -o fsetid
先测试没有设FSETID的情况,如下:
chmod 6777 /tmp/passwd
ls -l /tmp/passwd
-rwsrwsrwx 1 test test 14 2011-04-30 14:22 /tmp/passwd
/tmp/fsetid?
Wrote 14 bytes to the file.
ls -l /tmp/passwd
-rwxrwxrwx 1 test test 14 2011-04-30 14:25 /tmp/passwd
我们看到setuid/setgid位被清除了.
下面是设定FSETID,如下:
chmod 6777 /tmp/passwd
ls -l /tmp/passwd ? ??
-rwsrwsrwx 1 test test 14 2011-04-30 14:25 /tmp/passwd
切换到root用户,给/tmp/fsetid程序授权CAP_FSETID能力,如下:
setcap cap_fsetid=eip /tmp/fsetid
切换到普通用户
/tmp/fsetid
Wrote 14 bytes to the file.
ls -l /tmp/passwd
-rwsrwsrwx 1 test test 14 2011-04-30 14:28 /tmp/passwd
十)CAP_KILL 5 (允许对不属于自己的进程发送信号)
我们先模拟没有加CAP_KILL能力的情况,如下:
终端1,用root用户启用top程序,如下:
su - root
top
终端2,用test用户kill之前的top进程,如下:
pgrep top ? ??
3114
/bin/kill 3114
kill: Operation not permitted
我们发现无法对不属于自己的进程发送信号.
下面我们用CAP_KILL能力的程序向不属于自己的进程发送信号,如下:
设定kill命令的kill位,如下:
setcap cap_kill=eip /bin/kill?
杀掉3114进程,没有问题,如下:
/bin/kill 3114
echo $?
0
注意:
普通用户要用/bin/kill这种绝对路径的方式,而不能用kill这种方式.
十一)CAP_SETGID 6 (设定程序允许普通用户使用setgid函数,这与文件的setgid权限位无关)
cp /etc/shadow /tmp/
chown root.root /tmp/shadow
chmod 640 /tmp/shadow
切换到普通用户test,并编写setgid测试程序,如下:
su - test
1?#include?<unistd.h>
2?int
3?main?()
4?{
5?????????gid_t?gid?=?0;
6?????????setgid(gid);
7?????????system("/bin/cat?/tmp/shadow");
8?????????return?0;
9?}
gcc setgid.c -o setgid
更改setgid程序为CAP_SETGID
setcap cap_setgid=eip /tmp/setgid
切换到普通用户,运行/tmp/setgid程序,如下:
su - test
/tmp/setgid?
root:$1$S9AmPHY8$ZIdORp6aLnYleb5EORxw8/:14479:0:99999:7:::
daemon:*:14479:0:99999:7:::
bin:*:14479:0:99999:7:::
sys:*:14479:0:99999:7:::
sync:*:14479:0:99999:7:::
games:*:14479:0:99999:7:::
man:*:14479:0:99999:7:::
lp:*:14479:0:99999:7:::
mail:*:14479:0:99999:7:::
news:*:14479:0:99999:7:::
uucp:*:14479:0:99999:7:::
proxy:*:14479:0:99999:7:::
www-data:*:14479:0:99999:7:::
backup:*:14479:0:99999:7:::
list:*:14479:0:99999:7:::
我们看到普通用户可以查看/tmp/shadow文件,而取消CAP_SETGID则使程序不能拥有setgid的权限,如下:
setcap -r /tmp/setgid
su - test
/tmp/setgid?
/bin/cat: /tmp/shadow: Permission denied
十二)CAP_SETUID 7 (设定程序允许普通用户使用setuid函数,这也文件的setuid权限位无关)
cp /etc/shadow /tmp/
chown root.root /tmp/shadow
chmod 640 /tmp/shadow
切换到普通用户test,并编写setuid测试程序,如下:
su - test
cd /tmp/
vi setuid.c
1?#include?<unistd.h>
2?int
3?main?()
4?{
5?????????uid_t?uid?=?0;
6?????????setuid(uid);
7?????????system("/bin/cat?/tmp/shadow");
8?????????return?0;
9?}
gcc setuid.c -o setuid
切换到root用户,更改setuid程序为CAP_SETUID
su - root
setcap cap_setuid=eip /tmp/setuid
切换到test用户,运行/tmp/setuid程序,如下:
su - test
/tmp/setuid
root:$1$S9AmPHY8$ZIdORp6aLnYleb5EORxw8/:14479:0:99999:7:::
daemon:*:14479:0:99999:7:::
bin:*:14479:0:99999:7:::
sys:*:14479:0:99999:7:::
sync:*:14479:0:99999:7:::
games:*:14479:0:99999:7:::
man:*:14479:0:99999:7:::
lp:*:14479:0:99999:7:::
我们看到普通用户可以查看/tmp/shadow文件,而取消CAP_SETUID则使程序不能拥有setuid的权限,如下:
setcap -r /tmp/setuid
su - test
/tmp/setuid?
/bin/cat: /tmp/shadow: Permission denied
十三)CAP_SETPCAP 8 (允许向其它进程转移能力以及删除其它进程的任意能力)
事实上只有init进程可以设定其它进程的能力,而其它程序无权对进程授权,root用户也不能对其它进程的能力进行修改,只能对当前进程通过cap_set_proc等函数进行修改,而子进程也会继承这种能力.
所以即使使用了CAP_SETPCAP能力,也不会起到真正的作用.
十四)CAP_LINUX_IMMUTABLE 9 (允许修改文件的不可修改(IMMUTABLE)和只添加(APPEND-ONLY)属性)
普通用户不能通过chattr对文件设置IMMUTABLE(chattr +i)和APPEND-ONLY(chattr +a)权限,而通过CAP_LINUX_IMMUTABLE可以使普通用户通过自己增减(immutable/append-only)权限.
普通用户通过chattr给文件增加immutable权限,如下:
touch /tmp/test
chattr +i /tmp/test
chattr: Operation not permitted while setting flags on /tmp/test
我们看到授权失败了,而如果我们对chattr增加了LINUX_IMMUTABLE权限,则可以成功,如下:
此时切换到root用户:
su -?
setcap cap_linux_immutable=eip /usr/bin/chattr?
切换到普通用户:
su - test
chattr +i /tmp/test
lsattr /tmp/test?
----i-------------- /tmp/test
我们看到授权成功了,注意,这里只能对自己的文件授权(immutable/append-only)权限,对于其它用户的权限LINUX_IMMUTABLE不起作用(root除外),如下:
su - test
chattr +i /etc/passwd
chattr: Permission denied while setting flags on /etc/passwd
十五)CAP_NET_BIND_SERVICE 10(允许绑定到小于1024的端口)
普通用户不能通过bind函数绑定小于1024的端口,而root用户可以做到,CAP_NET_BIND_SERVICE的作用就是让普通用户也可以绑端口到1024以下.
普通用户通过nc绑定端口500,如下:
nc -l -p 500
Can't grab 0.0.0.0:500 with bind : Permission denied
增加CAP_NET_BIND_SERVICE能力到nc程序,如下:
setcap cap_net_bind_service=eip /usr/bin/nc
再切换到普通用户,通过nc绑定端口500,如下:
nc -l -p 500
查看该端口:
netstat -tulnp|grep nc
tcp ? ? ? ?0 ? ? ?0 0.0.0.0:500 ? ? ? ? ? ? 0.0.0.0:* ? ? ? ? ? ? ? LISTEN ? ? ?2523/nc ? ? ??
十六)CAP_NET_BROADCAST 11(允许网络广播和多播访问)
事实上它并没有被应用,普通用户也可以使用ping -b 192.168.0.255也发送广播包
十七)CAP_NET_ADMIN 12(允许执行网络管理任务:接口,防火墙和路由等)
普通用户不能创建新的网络接口(interface),不能更改ip地址,而CAP_NET_ADMIN可以帮助普通用户完成这项工作,如下:
用普通用户创建新的网卡接口eth0:1失败
/sbin/ifconfig eth0:1 172.16.27.133 netmask 255.255.255.0
SIOCSIFADDR: Permission denied
SIOCSIFFLAGS: Permission denied
SIOCSIFNETMASK: Permission denied
此时我们把CAP_NET_ADMIN能力授权给ifconfig程序,如下:
setcap cap_net_admin=eip /sbin/ifconfig
我们再次用普通用户即可以新建网络接口eth0:1,并可以DOWN掉接口,如下:
/sbin/ifconfig eth0:1 172.16.27.133 netmask 255.255.255.0
/sbin/ifconfig eth0:1
eth0:1 ? ?Link encap:Ethernet ?HWaddr 00:0c:29:f9:5e:06 ?
?? ? ? ? ?inet addr:172.16.27.133 ?Bcast:172.16.27.255 ?Mask:255.255.255.0
?? ? ? ? ?UP BROADCAST RUNNING MULTICAST ?MTU:1500 ?Metric:1
?? ? ? ? ?Interrupt:18 Base address:0x1080?
/sbin/ifconfig eth0:1 down?
同样CAP_NET_ADMIN可以让普通用户增加/删除路由,如下:
/sbin/route add -host 192.168.27.139 gw 192.168.27.2
SIOCADDRT: Operation not permitted
授权NET_ADMIN,如下:
setcap cap_net_admin=eip /sbin/route
再次用普通用户增加路由,如下:
/sbin/route add -host 192.168.27.139 gw 192.168.27.2
/sbin/route ?-n
Kernel IP routing table
Destination ? ? Gateway ? ? ? ? Genmask ? ? ? ? Flags Metric Ref ? ?Use Iface
192.168.27.139 ?192.168.27.2 ? ?255.255.255.255 UGH ? 0 ? ? ?0 ? ? ? ?0 eth0
192.168.27.0 ? ?0.0.0.0 ? ? ? ? 255.255.255.0 ? U ? ? 0 ? ? ?0 ? ? ? ?0 eth0
0.0.0.0 ? ? ? ? 192.168.27.2 ? ?0.0.0.0 ? ? ? ? UG ? ?0 ? ? ?0 ? ? ? ?0 eth0
/sbin/route del -host 192.168.27.139 gw 192.168.27.2 ??
我们看到我们除了可以增加路由之外,也可以删除路由.
最后NET_ADMIN可以帮助我们让普通用户来管理防火墙.
普通用户不能用iptables来管理防火墙,如下:
/sbin/iptables -L -n
iptables v1.4.2: can't initialize iptables table `filter': Permission denied (you must be root)
Perhaps iptables or your kernel needs to be upgraded.
我们将CAP_NET_ADMIN授权给iptables程序,注意我们也要将CAP_NET_RAW授权给iptables,CAP_NET_RAW我们后面再解释,如下:
setcap cap_net_admin,cap_net_raw=eip /sbin/iptables-multi
此时就可以用普通用户来管理防火墙了,如下:
/sbin/iptables -A INPUT -p tcp -j ACCEPT
/sbin/iptables -L -n
Chain INPUT (policy ACCEPT)
target ? ? prot opt source ? ? ? ? ? ? ? destination ? ? ? ??
ACCEPT ? ? tcp ?-- ?0.0.0.0/0 ? ? ? ? ? ?0.0.0.0/0 ? ? ? ? ??
Chain FORWARD (policy ACCEPT)
target ? ? prot opt source ? ? ? ? ? ? ? destination ? ? ? ??
Chain OUTPUT (policy ACCEPT)
target ? ? prot opt source ? ? ? ? ? ? ? destination ? ??
我们也可以删除防火墙策略,并清空当前的数据流量,如下:
/sbin/iptables -F
/sbin/iptables -Z
/sbin/iptables -X
十八)CAP_NET_RAW 13 (允许使用原始(raw)套接字)
原始套接字编程可以接收到本机网卡上的数据帧或者数据包,对监控网络流量和分析是很有作用的.
最常见的就是ping的实现,如下:
socket(PF_INET, SOCK_RAW, IPPROTO_ICMP) = 3
我们先把ping的setuid权限去掉
chmod u-s /bin/ping
ls -l /bin/ping
-rwxr-xr-x 1 root root 30788 2007-12-09 23:03 /bin/ping
用普通用户使用ping
ping ?192.168.27.2 ?
ping: icmp open socket: Operation not permitted
提示没有权限,我们将ping授权CAP_NET_RAW能力,如下:
setcap cap_net_raw=eip /bin/ping
切换到普通用户再次ping 192.168.27.2,发现可以ping通,如下:
ping ?192.168.27.2
PING 192.168.27.2 (192.168.27.2) 56(84) bytes of data.
64 bytes from 192.168.27.2: icmp_seq=1 ttl=128 time=0.266 ms
64 bytes from 192.168.27.2: icmp_seq=2 ttl=128 time=0.280 ms
64 bytes from 192.168.27.2: icmp_seq=3 ttl=128 time=0.319 ms
NET_RAW也同样用于tcpdump/iftop,一个普通用户无法使用tcpdump/iftop,而CAP_NET_RAW可以解决这个问题,方式同ping一样,所以我们不做演示.
十九)CAP_IPC_LOCK 14 (在允许锁定内存片段)
root和普通用户都可以用mlock来锁定内存,区别是root不受ulimit下的锁定内存大小限制,而普通用户会受到影响.
测试程序如下:
?1?#include?<stdio.h>
?2?#include?<sys/mman.h>
?3?
?4?int?main(int?argc,?char*?argv[])
?5?{
?6?????????int?array[2048];
?7?
?8?????????if?(mlock((const?void?*)array,?sizeof(array))?==?-1)?{
?9?????????????????perror("mlock:?");
10?????????????????return?-1;
11?????????}
12?
13?????????printf("success?to?lock?stack?mem?at:?%p,?len=%zd\n",
14?????????????????????????array,?sizeof(array));
15?
16?
17?????????if?(munlock((const?void?*)array,?sizeof(array))?==?-1)?{
18?????????????????perror("munlock:?");
19?????????????????return?-1;
20?????????}
21?
22?????????printf("success?to?unlock?stack?mem?at:?%p,?len=%zd\n",
23?????????????????????????array,?sizeof(array));
24?
25?????????return?0;
26?}
gcc mlock.c -o mlock
切换到普通用户,我们看到mlock是不受限制
ulimit -a
max locked memory ? ? ? (kbytes, -l) unlimited
我们运行程序
./mlock
success to lock stack mem at: 0xbfd94914, len=8192
success to unlock stack mem at: 0xbfd94914, len=8192
我们将限制改为1KB,再次运行程序,如下:
ulimit -l 1
./mlock
mlock: : Cannot allocate memory
切换到root用户,将CAP_IPC_LOCK能力授权给mlock测试程序,如下:
setcap cap_ipc_lock=eip /tmp/mlock
用普通用户再次运行程序,执行成功:
./mlock
success to lock stack mem at: 0xbfec1584, len=8192
success to unlock stack mem at: 0xbfec1584, len=8192
二十)CAP_IPC_OWNER 15 (忽略IPC所有权检查)
这个能力对普通用户有作用,如果用root用户创建共享内存(shmget),权限为600,而普通用户不能读取该段共享内存.
通过CAP_IPC_OWNER可以让普通用户的程序可以读取/更改共享内存.
我们用下面的程序创建共享内存段,并写入0xdeadbeef到共享内存段中.
?1?#include?<stdio.h>
?2?#include?<string.h>
?3?#include?<stdlib.h>
?4?#include?<unistd.h>
?5?#include?<sys/ipc.h>
?6?#include?<sys/shm.h>
?7?#include?<sys/wait.h>
?8?
?9?void?error_out(const?char?*msg)
10?{
11?????????perror(msg);
12?????????exit(EXIT_FAILURE);
13?}
14?
15?
16?int?main?(int?argc,?char?*argv[])
17?{
18?????????key_t?mykey?=?12345678;
19?
20?????????const?size_t?region_size?=?sysconf(_SC_PAGE_SIZE);
21?????????int?smid?=?shmget(mykey,?region_size,?IPC_CREAT|0600);
22?????????if(smid?==?-1)
23?????????????????error_out("shmget");
24?
25?????????void?*ptr;
26?????????ptr?=?shmat(smid,?NULL,?0);
27?????????if?(ptr?==?(void?*)?-1)
28?????????????????error_out("shmat");
29?????????u_long?*d?=?(u_long?*)ptr;
30?????????*d?=?0xdeadbeef;
31?????????printf("ipc?mem?%#lx\n",?*(u_long?*)ptr);
32?
33?????????return?0;
34?}
gcc test.c -o test -lrt
我们用root用户来执行本程序,创建共享内存,如下:
/tmp/test
ipc mem 0xdeadbeef
查看当前的共享内存
ipcs -m
------ Shared Memory Segments --------
key ? ? ? ?shmid ? ? ?owner ? ? ?perms ? ? ?bytes ? ? ?nattch ? ? status ? ? ?
0x00bc614e 458752 ? ? root ? ? ?600 ? ? ? ?4096 ? ? ? 0 ? ? ? ? ? ? ?
修改程序,将*d = 0xdeadbeef;改为*d = 0xffffffff;
再编译,用普通用户运行程序,如下:
gcc test.c -o test -lrt
su - test
/tmp/test
shmget: Permission denied
我们看到没有权限更改共享内存.
用root用户把CAP_IPC_OWNER能力授权给test程序,如下:
setcap cap_ipc_owner=eip /tmp/test
再次用普通用户运行程序test,更改共享内存,如下:
/tmp/test
ipc mem 0xffffffff
我们看到修改成功,但要说明CAP_IPC_OWNER不能让进程/程序删除/脱离共享内存
二十一)CAP_SYS_MODULE 16 (允许普通用户插入和删除内核模块)
由于普通用户不能插入/删除内核模块,而CAP_SYS_MODULE可以帮助普通用户做到这点
例如,用户test插入内核模块nvram
/sbin/modprobe nvram ? ? ?
FATAL: Error inserting nvram (/lib/modules/2.6.38.4/kernel/drivers/char/nvram.ko): Operation not permitted
系统提示权限不足.
我们把CAP_SYS_MODULE能力授权给modprobe程序,如下:
setcap cap_sys_module=eip /sbin/modprobe
再用普通用户加载内核模块,如下:
/sbin/modprobe nvram
lsmod |grep nvram
nvram ? ? ? ? ? ? ? ? ? 3861 ?0?
我们看到可以加载.
同样我们可以将CAP_SYS_MODULE授权给rmmod程序,让其可以删除模块,如下:
setcap cap_sys_module=eip /sbin/rmmod
su - test
/sbin/rmmod nvram
lsmod |grep nvram
二十二)CAP_SYS_RAWIO 17 (允许用户打开端口,并读取修改端口数据,一般用ioperm/iopl函数)
ioperm只有低端的[0-0x3ff] I/O端口可被设置,且普通用户不能使用.
iopl可以用于所有的65536个端口,因此ioperm相当于iopl调用的一个子集.
下面的程序首先设置0x3FF端口的读写权限,然后读出原先的值,然后将原值的LSB翻转并写回端口,并在此读取端口值.
?1?#include?<unistd.h>
?2?#include?<sys/io.h>
?3?
?4?#define?PORT_ADDR?0x3FF
?5?
?6?int?main(void)
?7?{
?8???????int?ret;
?9???????char?port_val;
10???????
11???????/*set?r/w?permission?of?all?65536?ports*/
12???????ret?=?iopl(3);
13???????if(ret?<?0){
14????????????perror("iopl?set?error");
15????????????return?0;
16???????}
17?
18???????port_val?=?inb(PORT_ADDR);
19???????printf("Original?value?of?port?0x%x?is?:?%.2x\n",?PORT_ADDR,?port_val);
20???????
21???????/*reverse?the?least?significant?bit?*/
22?
23???????outb(port_val^0x01,?PORT_ADDR);
24???????port_val?=?inb(PORT_ADDR);
25???????printf("Current?value?of?port?0x%x?is?:?%.2x\n",?PORT_ADDR,?port_val);
26???????
27???????/*set?r/w?permission?of??all?65536?ports*/
28???????
29???????ret?=?iopl(0);
30???????if(ret?<?0){
31????????????perror("iopl?set?error");
32????????????return?0;
33???????}
34???????return?0;
35?}
编译:
gcc iopl.c -o iopl
普通用户运行iopl程序,提示没有权限.
/tmp/iopl?
iopl set error: Operation not permitted
给程序iopl授权CAP_SYS_RAWIO能力,此时普通用户可以执行iopl程序,如下:
setcap cap_sys_rawio=eip /tmp/iopl
su - test
/tmp/iopl?
Original value of port 0x3ff is : 01
Current value of port 0x3ff is : 00
/tmp/iopl?
Original value of port 0x3ff is : 00
Current value of port 0x3ff is : 01
二十三)CAP_SYS_CHROOT 18 (允许使用chroot()系统调用)
普通用户不能通过chroot系统调用更改程式执行时所参考的根目录位置,而CAP_SYS_CHROOT可以帮助普通用户做到这一点.
普通用户使用chroot,如下:
/usr/sbin/chroot / /bin/bash
/usr/sbin/chroot: cannot change root directory to /: Operation not permitted
通过root授权CAP_SYS_CHROOT能力给chroot程序,如下:
capset cap_sys_chroot=eip /usr/sbin/chroot
普通用户再次用chroot切换根目录,如下:
/usr/sbin/chroot / /bin/sh
sh-3.2$
二十四)CAP_SYS_PTRACE 19 (允许跟踪任何进程)
普通用户不能跟踪任何进程,不包括它自己的进程,而CAP_SYS_PTRACE可以帮助普通用户跟踪任何进程.
切换到普通用户,跟踪PID1的进程.
strace -p 1
attach: ptrace(PTRACE_ATTACH, ...): Operation not permitted
切换到root用户,将CAP_SYS_PTRACE能力授权给strace程序,用于跟踪进程,如下:
setcap cap_sys_ptrace=eip /usr/bin/strace
切换到普通用户,跟踪PID1的进程,如下:
strace -p 1
Process 1 attached - interrupt to quit
select(11, [10], NULL, NULL, {3, 771381}) = 0 (Timeout)
time(NULL) ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?= 1304348451
stat64("/dev/initctl", {st_mode=S_IFIFO|0600, st_size=0, ...}) = 0
fstat64(10, {st_mode=S_IFIFO|0600, st_size=0, ...}) = 0
stat64("/dev/initctl", {st_mode=S_IFIFO|0600, st_size=0, ...}) = 0
select(11, [10], NULL, NULL, {5, 0}) ? ?= 0 (Timeout)
time(NULL) ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?= 1304348456
二十五)CAP_SYS_PACCT 20 (允许配置进程记帐process accounting)?
要完成进程记帐,要保证有写入文件的权限,这里我们将进程记录写入到/home/test/log/psacct.
mkdir /home/test/log/
touch /home/test/log/psacct
程序通过acct函数,将进程的记帐写入到指定文件中,如果acct函数的参数为NULL,则关闭进程记帐.
?1?#include?<stdlib.h>
?2?#include?<sys/acct.h>
?3?
?4?int
?5?main()
?6?{
?7?????????int?ret;
?8?????????ret?=?acct("/home/test/log/pacct");
?9?????????if(ret?<?0){
10????????????perror("acct?on?error");
11????????????return?0;
12?????????}
13?????????system("/bin/ls?-l");
14?????????acct(NULL);
15?????????if(ret?<?0){
16????????????perror("acct?off?error");
17????????????return?0;
18?????????}
19?????????return?0;
20?}
gcc psacct.c -o psacct
./psacct?
acct on error: Operation not permitted
给psacct程序授权CAP_SYS_PACCT能力,如下:
setcap cap_sys_psacct /home/test/psacct
注意这里的能力是sys_psacct,而不是sys_pacct
回到普通用户,执行psacct
./psacct?
total 24
drwxr-xr-x 2 test test 4096 2011-05-02 13:28 log
-rw-r--r-- 1 test test ? 64 2011-05-02 13:25 pacct
-rwxr-xr-x 1 test test 6590 2011-05-02 13:31 psacct
-rw-r--r-- 1 test test ?314 2011-05-02 13:31 psacct.c
我们看到已经执行成功,下面我们再看下进程记录,如下:
lastcomm -f /home/test/log/pacct?
ls ? ? ? ? ? ? ? ? ? ? test ? ? pts/0 ? ? ?0.03 secs Mon May ?2 13:33
二十六)CAP_SYS_ADMIN 21 (允许执行系统管理任务,如挂载/卸载文件系统,设置磁盘配额,开/关交换设备和文件等)
下面是普通用户拥有相关管理权限的测试
1)更改主机名
setcap cap_sys_admin=eip /bin/hostname
su - test
hostname test2
hostname
test2
2)挂载/卸载文件系统
这里我们注意,系统的mount命令不能做这个试验,因为程序中做了判断,如果不是root用户请退出.所以我们用mount()函数来完成这个试验,程序如下:
?1?#include?<stdlib.h>
?2?#include?<sys/mount.h>
?3?int
?4?main?()
?5?{
?6?????????int?ret;
?7?????????ret?=?mount("/dev/sda1",?"/mnt/",?"ext3",?MS_MGC_VAL,?NULL);
?8?????????if(ret?<?0){
?9????????????perror("mount?error");
10????????????return?0;
11?????????}
12?????????return?0;
13?}
用普通用户编译运行:
gcc mounttest.c -o mounttest
./mounttest?
mount error: Operation not permitted
我们看到普通用户不能完成mount操作,而CAP_SYS_ADMIN可以帮助普通用户完成该操作,如下:
setcap cap_sys_admin=eip /home/test/mounttest?
切换到普通用户,执行mounttest程序,如下:
./mounttest?
cat /proc/mounts?
略
/dev/sda1 /mnt ext3 rw,relatime,errors=remount-ro,barrier=0,data=writeback 0 0
umount和mount一样,我们在这里不做演示.
3)swapon/swapoff
普通用户不能进行swapon/swapoff操作,而CAP_SYS_ADMIN可以帮助普通用户完成swapon/swapoff操作,如下:
dd if=/dev/zero of=/tmp/testdb bs=10M count=1
1+0 records in
1+0 records out
10485760 bytes (10 MB) copied, 0.164669 s, 63.7 MB/s
/sbin/mkswap /tmp/testdb
Setting up swapspace version 1, size = 10481 kB
no label, UUID=0ff46dc8-781c-4c3f-81b3-fe860f74793e
/sbin/swapon /tmp/testdb
swapon: /tmp/testdb: Operation not permitted
我们看到swapon操作被拒绝,这里我们对swapon进行授权,如下:
setcap cap_sys_admin /sbin/swapon
普通用户再次swapon,如下:
/sbin/swapon /tmp/testdb
/sbin/swapon?
/sbin/swapon -s
Filename ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?Type ? ? ? ? ? ?Size ? ?Used ? ?Priority
/dev/sda6 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? partition ? ? ? 7815584 0 ? ? ? -1
/tmp/testdb ? ? ? ? ? ? ? ? ? ? ? ? ? ? file ? ? ? ? ? ?10236 ? 0 ? ? ? -2
我们看到swapon操作成功.因为swapoff是swapon的软链接,所以可以直接swapoff掉交换分区,如下:
/sbin/swapoff /tmp/testdb
/sbin/swapon -s
Filename ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?Type ? ? ? ? ? ?Size ? ?Used ? ?Priority
/dev/sda6 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? partition ? ? ? 7815584 0 ? ? ? -1
ls -l /sbin/swapoff ? ?
lrwxrwxrwx 1 root root 6 2009-08-23 07:49 /sbin/swapoff -> swapon
二十七) CAP_SYS_BOOT 22 (允许普通用使用reboot()函数)
这里我们无法用reboot命令来做测试,因为reboot命令做了判断,只允许root(UID=0)的用户可以使用.
我们用下面的程序进行测试.
1?#include?<unistd.h>
2?#include?<sys/reboot.h>
3?int?main()
4?{
5?????sync();?
6?????return?reboot(RB_AUTOBOOT);
7?}
编译:
gcc reboot1.c -o reboot1
./reboot1
这时系统没有重启.
我们查看程序的返回码,这里是255,说明程序没有运行成功.
echo $?
255
我们用CAP_SYS_BOOT能力使reboot1程序可以被普通用户重启,如下:
setcap cap_sys_boot=eip /home/test/reboot1
用普通用户再试运行reboot1,如下:
reboot1
此时系统被重启了.
二十八)CAP_SYS_NICE 23(允许提升优先级,设置其它进程的优先级)
对于普通用户程序的NICE优先级,不能超过ulimit对它的限制,如下:
nice -n -5 ls?
nice: cannot set niceness: Permission denied
而CAP_SYS_NICE可以帮助普通用户设置一个想要的一个任意优先级.
setcap cap_sys_nice=eip /usr/bin/nice
切换到普通用户,指定优先级,如下:
nice -n -5 ls?
log ?mnt ?mount.c ?mounttest ?pacct ?psacct ?psacct.c ?reboot1 ?reboot1.c ?test
普通用户也不能给指定进程指定NICE优先级,而CAP_SYS_NICE也可以做的,如下:
renice -5 2255
renice: 2255: setpriority: Operation not permitted
setcap cap_sys_nice=eip /usr/bin/renice
renice -5 2255
2255: old priority 0, new priority -5
我们甚至可以用CAP_SYS_NICE来指定实时优先级
chrt -f 50 ?ls
chrt: failed to set pid 0's policy: Operation not permitted
给/usr/bin/chrt命令授权CAP_SYS_NICE能力,如下:
setcap ?cap_sys_nice=eip ?/usr/bin/chrt
切换到普通用户,如下:
chrt -f 50 ?ls
log ?mnt ?setulimit ?setulimit.c
我们也可以指定它的CPU亲和性,如下:
taskset -p 1 2255
pid 2255's current affinity mask: 1
sched_setaffinity: Operation not permitted
failed to set pid 2255's affinity.
给/usr/bin/taskset命令授权CAP_SYS_NICE能力,如下:
setcap ?cap_sys_nice=eip ?/usr/bin/taskset
切换到普通用户,再次运行taskset,可以成功的设置CPU亲和性
taskset -p 1 2255
pid 2255's current affinity mask: 1
pid 2255's new affinity mask: 1
二十九) CAP_SYS_RESOURCE 24 忽略资源限制
普通用户不能用setrlimit()来突破ulimit的限制
我们用下面的程序进行测试,普通用户是无法修改RLIMIT_STACK(堆栈大小)的.如下:
?1?#include?<signal.h>
?2?#include?<stdio.h>
?3?#include?<string.h>
?4?#include?<stdlib.h>
?5?#include?<limits.h>
?6?#include?<unistd.h>
?7?#include?<sys/types.h>
?8?#include?<sys/stat.h>
?9?#include?<sys/resource.h>
10?
11?int
12?main?(int?argc,?char?*argv[])
13?{
14?int?r?=?0;
15?struct?rlimit?rl;
16?
17?getrlimit?(RLIMIT_STACK,&rl);
18?
19?printf("crrent?hard?limit?is?%ld\n",
20?(u_long)?rl.rlim_max);
21?
22?rl.rlim_max?=?rl.rlim_max+1;
23?r?=?setrlimit?(RLIMIT_STACK,?&rl);
24?if?(r){
25?perror("setrlimit");
26?return?-1;
27?}
28?
29?printf("limit?set?to?%ld?\n",?(u_long)?rl.rlim_max+1);
30?return?0;
31?}
gcc setulimit.c -o setulimit
我们先来查看当前的限制,这里是10MB,如下:
ulimit -H -s?
10240
./setulimit ? ?
crrent hard limit is 10485760
setrlimit: Operation not permitted
我们给setulimit程序以CAP_SYS_RESOURCE的能力,如下:
setcap cap_sys_resource=eip /home/test/setulimit?
用普通用户再次运行程序,已经可以通过setrlimit()设定超过限额了,如下:
./setulimit?
crrent hard limit is 10485760
limit set to 10485762?
同样我们也可以用CAP_SYS_RESOURCE能力使程序超出磁盘限额,如下:
quotacheck -avug
quotaon -avug
edquota -u test
Filesystem ? ? ? ? ? ? ? ? ? blocks ? ? ? soft ? ? ? hard ? ? inodes ? ? soft ? ? hard
??/dev/sda7 ? ? ? ? ? ? ? ? ? ? ? ? 0 ? ? ? ? ?3 ? ? ? ? ?5 ? ? ? ? ?0 ? ? ? ?0 ? ? ? ?0
mkdir /export/test
chown -R test.test /export/test/
切换到普通用户,用dd命令产生3MB的文件,这明显超过了5kb的限制,如下:
su - test
dd if=/dev/zero of=/export/test/test bs=1M count=3
sda7: warning, user block quota exceeded.
sda7: write failed, user block limit reached.
dd: writing `test': Disk quota exceeded
1+0 records in
0+0 records out
4096 bytes (4.1 kB) copied, 0.0117371 s, 349 kB/s
授权CAP_SYS_RESOURCE能力给/bin/dd命令,如下:
setcap cap_sys_resource=eip /bin/dd
再次用普通用户运行dd命令,即可以超过5kb的限额了.
su - test
dd if=/dev/zero of=test bs=1M count=3
sda7: warning, user block quota exceeded.
3+0 records in
3+0 records out
3145728 bytes (3.1 MB) copied, 0.423662 s, 7.4 MB/s
三十)CAP_SYS_TIME 25(允许改变系统时钟)
普通用户不能改变系统时钟,如下:
date -s 2012-01-01
date: cannot set date: Operation not permitted
Sun Jan ?1 00:00:00 EST 2012
CAP_SYS_TIME可以帮助普通用户改变系统时钟,如下:
setcap cap_sys_time=eip /bin/date
切换到普通用户再次改变时间,发现已经可以改变了
su - test
date -s 2012-01-01
Sun Jan ?1 00:00:00 EST 2012
date
Sun Jan ?1 00:00:02 EST 2012
三十一)CAP_SYS_TTY_CONFIG 26(允许配置TTY设备)
我们下面用vhangup()函数来挂起当前的tty
程序如下:
?1?#include?<stdio.h>
?2?#include?<unistd.h>
?3?
?4?int?main?()
?5?{
?6???int?r;
?7???r=vhangup();
?8???if?(r){?
?9???perror?("vhanguo");
10???}
11???return?0;
12?}
gcc vhup.c -o vhup
./vhup?
vhanguo: Operation not permitted
我们给vhup程序设定CAP_SYS_TTY_CONFIG能力,如下:
setcap cap_sys_tty_config=eip /home/test/vhup
再次用普通用户执行程序vhup,tty被挂起,如下:
./vhup?
此时当前tty被挂起.
三十二) CAP_MKNOD 27 (允许使用mknod系统调用)
普通用户不能用mknod()来创建设备文件,而CAP_MKNOD可以帮助普通用户做到这一点,如下:
mknod /tmp/tnod1 c 1 5
mknod: `/tmp/tnod1': Operation not permitted
setcap cap_mknod=eip /bin/mknod
切换到普通用户,再次用mknod命令创建设备文件,如下:
mknod /tmp/tnod1 c 1 5
ls -l /tmp/tnod1?
crw-r--r-- 1 test test 1, 5 2012-01-01 00:31 /tmp/tnod1
三十三) CAP_LEASE 28(允许在文件上建立租借锁)
系统调用fcntl()可以用于租借锁,此时采用的函数原型如下:
?? ? ? int fcntl(int fd, int cmd, long arg);
与租借锁相关的 cmd 参数的取值有两种:F_SETLEASE 和 F_GETLEASE。其含义如下所示:
F_SETLEASE:根据下面所描述的 arg 参数指定的值来建立或者删除租约:
F_RDLCK:设置读租约。当文件被另一个进程以写的方式打开时,拥有该租约的当前进程会收到通知
F_WRLCK:设置写租约。当文件被另一个进程以读或者写的方式打开时,拥有该租约的当前进程会收到通知
F_UNLCK:删除以前建立的租约
F_GETLEASE:表明调用进程拥有文件上哪种类型的锁,这需要通过返回值来确定,返回值有三种:F_RDLCK、F_WRLCK和F_UNLCK,分别表明调用进程对文件拥有读租借、写租借或者根本没有租借
某个进程可能会对文件执行其他一些系统调用(比如 OPEN() 或者 TRUNCATE()),如果这些系统调用与该文件上由 F_SETLEASE 所设置的租借锁相冲突,内核就会阻塞这个系统调用;
同时,内核会给拥有这个租借锁的进程发信号,告知此事。拥有此租借锁的进程会对该信号进行反馈,它可能会删除这个租借锁,也可能会减短这个租借锁的租约,从而可以使得该文件可以被其他进程所访问。
如果拥有租借锁的进程不能在给定时间内完成上述操作,那么系统会强制帮它完成。通过 F_SETLEASE 命令将 arg 参数指定为 F_UNLCK 就可以删除这个租借锁。
不管对该租借锁减短租约或者干脆删除的操作是进程自愿的还是内核强迫的,只要被阻塞的系统调用还没有被发出该调用的进程解除阻塞,那么系统就会允许这个系统调用执行。
即使被阻塞的系统调用因为某些原因被解除阻塞,但是上面对租借锁减短租约或者删除这个过程还是会执行的。
源程序如下:
?1?#define?_GNU_SOURCE?
?2?
?3?#include?<unistd.h>
?4?#include?<fcntl.h>
?5?#include?<stdio.h>
?6?#include?<sys/file.h>
?7?static?void?show_lease(int?fd)
?8?{
?9?????????int?res;
10?
11?????????res?=?fcntl(fd,?F_GETLEASE);
12?????????switch?(res)?{
13?????????????????case?F_RDLCK:
14?????????????????????????printf("Read?lease\n");
15?????????????????????????break;
16?????????????????case?F_WRLCK:
17?????????????????????????printf("Write?lease\n");
18?????????????????????????break;
19?????????????????case?F_UNLCK:
20?????????????????????????printf("No?leases\n");
21?????????????????????????break;
22?????????????????default:
23?????????????????????????printf("Some?shit\n");
24?????????????????????????break;
25?????????}
26?}
27?
28?int?main(int?argc,?char?**argv)
29?{
30?????????int?fd,?res;
31?
32?????????fd?=?open(argv[1],?O_RDONLY);
33?????????if?(fd?==?-1)?{
34?????????????????perror("Can't?open?file");
35?????????????????return?1;
36?????????}
37?
38?????????res?=?fcntl(fd,?F_SETLEASE,?F_WRLCK);
39?????????if?(res?==?-1)?{
40?????????????????perror("Can't?set?lease");
41?????????????????return?1;
42?????????}
43?
44?????????show_lease(fd);
45?
46?????????if?(flock(fd,?LOCK_SH)?==?-1)?{
47?????????????????perror("Can't?flock?shared");
48?????????????????return?1;
49?????????}
50?
51?????????show_lease(fd);
52?
53?????????return?0;
54?}
编译:
gcc fcntl.c -o fcntl
我们使用普通用户在/etc/passwd文件上建立租借锁,由于普通用户没有/etc/passwd上建租借锁的权限,故而报错,如下:
su - test
/tmp/fcntl /etc/passwd
Can't set lease: Permission denied
注:
普通用户可以在自己的文件上(owner)建立租借锁.
授权lease给/tmp/fcntl文件,如下:
setcap cap_lease=eip /tmp/fcntl
再次运行/tmp/fcntl,可以建立租借锁了,如下:
/tmp/fcntl /etc/passwd
Write lease
Write lease
三十四)CAP_SETFCAP 31 (允许在指定的程序上授权能力给其它程序)
例如我们让普通用户也能用setcap给其它程序授权,如下:
setcap CAP_SETFCAP=eip /usr/sbin/setcap?
su - test
setcap CAP_SETPCAP=eip /bin/ls
getcap /bin/ls ? ? ? ? ? ? ? ?
/bin/ls = cap_setpcap+eip
三十六)其它的能力
CAP_AUDIT_WRITE
CAP_AUDIT_CONTROL
CAP_MAC_OVERRIDE
CAP_MAC_ADMIN
CAP_SYSLOG
这五组只能涉及到syslog,audit,mac等安全模块,以后专门对其进行分析.
三十七)总结:
CAP_CHOWN 0 允许改变文件的所有权?
CAP_DAC_OVERRIDE 1 忽略对文件的所有DAC访问限制?
CAP_DAC_READ_SEARCH 2 忽略所有对读、搜索操作的限制?
CAP_FOWNER 3 以最后操作的UID,覆盖文件的先前的UID
CAP_FSETID 4 确保在文件被修改后不修改setuid/setgid位
CAP_KILL 5 允许对不属于自己的进程发送信号?
CAP_SETGID 6 允许改变组ID?
CAP_SETUID 7 允许改变用户ID?
CAP_SETPCAP 8 允许向其它进程转移能力以及删除其它进程的任意能力(只限init进程)
CAP_LINUX_IMMUTABLE 9 允许修改文件的不可修改(IMMUTABLE)和只添加(APPEND-ONLY)属性?
CAP_NET_BIND_SERVICE 10 允许绑定到小于1024的端口?
CAP_NET_BROADCAST 11 允许网络广播和多播访问(未使用)?
CAP_NET_ADMIN 12 允许执行网络管理任务:接口、防火墙和路由等.
CAP_NET_RAW 13 允许使用原始(raw)套接字?
CAP_IPC_LOCK 14 允许锁定共享内存片段?
CAP_IPC_OWNER 15 忽略IPC所有权检查?
CAP_SYS_MODULE 16 插入和删除内核模块?
CAP_SYS_RAWIO 17 允许对ioperm/iopl的访问?
CAP_SYS_CHROOT 18 允许使用chroot()系统调用?
CAP_SYS_PTRACE 19 允许跟踪任何进程?
CAP_SYS_PACCT 20 允许配置进程记帐(process accounting)?
CAP_SYS_ADMIN 21 允许执行系统管理任务:加载/卸载文件系统、设置磁盘配额、开/关交换设备和文件等.
CAP_SYS_BOOT 22 允许重新启动系统?
CAP_SYS_NICE 23 允许提升优先级,设置其它进程的优先级?
CAP_SYS_RESOURCE 24 忽略资源限制?
CAP_SYS_TIME 25 允许改变系统时钟?
CAP_SYS_TTY_CONFIG 26 允许配置TTY设备?
CAP_MKNOD 27 允许使用mknod()系统调用?
CAP_LEASE 28 允许在文件上建立租借锁
CAP_SETFCAP 31 允许在指定的程序上授权能力给其它程序
参考:
man capabilities
linux-2.6.38.5/include/linux/capability.h?
from:https://blog.csdn.net/wwwdc1012/article/details/78759326
函数名 | 描述 | 文件 |
---|---|---|
fork | 创建一个新进程 | kernel/fork.c |
clone | 按指定条件创建子进程 | kernel/fork.c |
execve | 运行可执行文件 | fs/exec.c |
exit | 中止进程 | kernel/exit.c |
_exit | 立即中止当前进程 | |
getdtablesize | 进程所能打开的最大文件数 | kernel/sys.c |
getpgid | 获取指定进程组标识号 | kernel/sys.c |
setpgid | 设置指定进程组标志号 | kernel/sys.c |
getpgrp | 获取当前进程组标识号 | kernel/sys.c |
setpgrp | 设置当前进程组标志号 | kernel/sys.c |
getpid | 获取进程标识号 | kernel/sys.c |
getppid | 获取父进程标识号 | kernel/sys.c |
getpriority | 获取调度优先级 | kernel/sys.c |
setpriority | 设置调度优先级 | kernel/sys.c |
modify_ldt | 读写进程的本地描述表 | arch/x86/um/ldt.c |
nanosleep | 使进程睡眠指定的时间 | kernel/hrtimer.c |
nice | 改变分时进程的优先级 | |
pause | 挂起进程,等待信号 | kernel/signal.c |
personality | 设置进程运行域 | kernel/exec_domain.c |
prctl | 对进程进行特定操作 | kernel/sys.c |
ptrace | 进程跟踪 | kernel/ptrace.c |
sched_get_priority_max | 取得静态优先级的上限 | kernel/sched/core.c |
sched_get_priority_min | 取得静态优先级的下限 | kernel/sched/core.c |
sched_getparam | 取得进程的调度参数 | kernel/sched/core.c |
sched_getscheduler | 取得指定进程的调度策略 | kernel/sched/core.c |
sched_rr_get_interval | 取得按RR算法调度的实时进程的时间片长度 | kernel/sched/core.c |
sched_setparam | 设置进程的调度参数 | kernel/sched/core.c |
sched_setscheduler | 设置指定进程的调度策略和参数 | kernel/sched/core.c |
sched_yield | 进程主动让出处理器,并将自己等候调度队列队尾 | kernel/sched/core.c |
vfork | 创建一个子进程,以供执行新程序,常与execve等同时使用 | kernel/fork.c |
wait | 等待子进程终止 | |
wait3 | 参见wait | |
waitpid | 等待指定子进程终止 | |
wait4 | 参见waitpid | kernel/exit.c |
capget | 获取进程权限 | kernel/capability.c |
capset | 设置进程权限 | kernel/capability.c |
getsid | 获取会晤标识号 | kernel/sys.c |
setsid | 设置会晤标识号 | kernel/sys.c |
函数名 | 描述 | 文件 |
---|---|---|
fcntl | 文件控制 | fs/fcntl.c |
open | 打开文件 | fs/open.c |
creat | 创建新文件 | fs/open.c |
close | 关闭文件描述字 | fs/open.c |
read | 读文件 | fs/read_write.c |
write | 写文件 | fs/read_write.c |
readv | 从文件读入数据到缓冲数组中 | fs/read_write.c |
writev | 将缓冲数组里的数据写入文件 | fs/read_write.c |
pread | 对文件随机读 | fs/read_write.c |
pwrite | 对文件随机写 | fs/read_write.c |
lseek | 移动文件指针 | fs/read_write.c |
_llseek | 在64位地址空间里移动文件指针 | |
dup | 复制已打开的文件描述字 | fs/file.c |
dup2 | 按指定条件复制文件描述字 | fs/file.c |
flock | 文件加/解锁 | fs/locks.c |
poll | I/O多路转换 | fs/select.c |
truncate | 截断文件 | fs/open.c |
ftruncate | 参见truncate | fs/open.c |
umask | 设置文件权限掩码 | kernel/sys.c |
fsync | 把文件在内存中的部分写回磁盘 | fs/sync.c |
access | 确定文件的可存取性 | fs/open.c |
chdir | 改变当前工作目录 | fs/open.c |
fchdir | 参见chdir | fs/open.c |
chmod | 改变文件方式 | fs/open.c |
fchmod | 参见chmod | fs/open.c |
chown | 改变文件的属主或用户组 | fs/open.c |
fchown | 参见chown | fs/open.c |
lchown | 参见chown | fs/open.c |
chroot | 改变根目录 | fs/open.c |
stat | 取文件状态信息 | fs/stat.c |
lstat | 参见stat | fs/stat.c |
fstat | 参见stat | fs/stat.c |
statfs | 取文件系统信息 | fs/statfs.c |
fstatfs | 参见statfs | fs/statfs.c |
readdir | 读取目录项 | |
getdents | 读取目录项 | fs/readdir.c |
mkdir | 创建目录 | fs/namei.c |
mknod | 创建索引节点 | fs/namei.c |
rmdir | 删除目录 | fs/namei.c |
rename | 文件改名 | fs/namei.c |
link | 创建链接 | fs/namei.c |
symlink | 创建符号链接 | fs/namei.c |
unlink | 删除链接 | fs/namei.c |
readlink | 读符号链接的值 | fs/stat.c |
mount | 安装文件系统 | fs/namespace.c |
umount | 卸下文件系统 | |
ustat | 取文件系统信息 | fs/statfs.c |
utime | 改变文件的访问修改时间 | fs/utimes.c |
utimes | 参见utime | fs/utimes.c |
quotactl | 控制磁盘配额 | fs/quota/quota.c |
函数名 | 描述 | 文件 |
---|---|---|
ioctl | I/O总控制函数 | fs/ioctl.c |
_sysctl | 读/写系统参数 | kernel/sysctl_binary.c |
acct | 启用或禁止进程记账 | kernel/acct.c |
getrlimit | 获取系统资源上限 | kernel/sys.c |
setrlimit | 设置系统资源上限 | kernel/sys.c |
getrusage | 获取系统资源使用情况 | kernel/sys.c |
uselib | 选择要使用的二进制函数库 | fs/exec.c |
ioperm | 设置端口I/O权限 | arch/x86/kernel/ioport.c |
iopl | 改变进程I/O权限级别 | arch/x86/kernel/ioport.c |
outb | 低级端口操作 | |
reboot | 重新启动 | kernel/reboot.c |
swapon | 打开交换文件和设备 | mm/swapfile.c |
swapoff | 关闭交换文件和设备 | mm/swapfile.c |
bdflush | 控制bdflush守护进程 | |
sysfs | 取核心支持的文件系统类型 | fs/filesystems.c |
sysinfo | 取得系统信息 | kernel/sys.c |
adjtimex | 调整系统时钟 | kernel/time.c |
alarm | 设置进程的闹钟 | kernel/timer.c |
getitimer | 获取计时器值 | kernel/itimer.c |
setitimer | 设置计时器值 | kernel/itimer.c |
gettimeofday | 取时间和时区 | kernel/time.c |
settimeofday | 设置时间和时区 | kernel/time.c |
stime | 设置系统日期和时间 | |
time | 取得系统时间 | |
times | 取进程运行时间 | kernel/sys.c |
uname | 获取当前UNIX系统的名称、版本和主机等信息 | kernel/sys.c |
vhangup | 挂起当前终端 | fs/open.c |
nfsservctl | 对NFS守护进程进行控制 | |
vm86 | 进入模拟8086模式 | |
create_module | 创建可装载的模块项 | |
delete_module | 删除可装载的模块项 | kernel/module.c |
init_module | 初始化模块 | kernel/module.c |
query_module | 查询模块信息 | |
*get_kernel_syms | 取得核心符号,已被query_module代替 |
函数名 | 描述 | 文件 |
---|---|---|
brk | 改变数据段空间的分配 | mm/mmap.c |
sbrk | 参见brk | |
mlock | 内存页面加锁 | mm/mlock.c |
munlock | 内存页面解锁 | mm/mlock.c |
mlockall | 调用进程所有内存页面加锁 | mm/mlock.c |
munlockall | 调用进程所有内存页面解锁 | mm/mlock.c |
mmap | 映射虚拟内存页 | arch/x86/kernel/sys_x86_64.c |
munmap | 去除内存页映射 | mm/mmap.c |
mremap | 重新映射虚拟内存地址 | mm/mmap.c |
msync | 将映射内存中的数据写回磁盘 | mm/msync.c |
mprotect | 设置内存映像保护 | mm/mprotect.c |
getpagesize | 获取页面大小 | |
sync | 将内存缓冲区数据写回硬盘 | fs/sync.c |
cacheflush | 将指定缓冲区中的内容写回磁盘 |
函数名 | 描述 | 文件 |
---|---|---|
getdomainname | 取域名 | |
setdomainname | 设置域名 | kernel/sys.c |
gethostid | 获取主机标识号 | |
sethostid | 设置主机标识号 | |
gethostname | 获取本主机名称 | |
sethostname | 设置主机名称 | kernel/sys.c |
函数名 | 描述 | 文件 |
---|---|---|
socketcall | socket系统调用 | |
socket | 建立socket | net/socket.c |
bind | 绑定socket到端口 | net/socket.c |
connect | 连接远程主机 | net/socket.c |
accept | 响应socket连接请求 | net/socket.c |
send | 通过socket发送信息 | |
sendto | 发送UDP信息 | net/socket.c |
sendmsg | 参见send | net/socket.c |
recv | 通过socket接收信息 | |
recvfrom | 接收UDP信息 | net/socket.c |
recvmsg | 参见recv | net/socket.c |
listen | 监听socket端口 | net/socket.c |
select | 对多路同步I/O进行轮询 | fs/select.c |
shutdown | 关闭socket上的连接 | net/socket.c |
getsockname | 取得本地socket名字 | net/socket.c |
getpeername | 获取通信对方的socket名字 | net/socket.c |
getsockopt | 取端口设置 | net/socket.c |
setsockopt | 设置端口参数 | net/socket.c |
sendfile | 在文件或端口间传输数据 | fs/read_write.c |
socketpair | 创建一对已联接的无名socket | net/socket.c |
函数名 | 描述 | 文件 |
---|---|---|
getuid | 获取用户标识号 | kernel/sys.c |
setuid | 设置用户标志号 | kernel/sys.c |
getgid | 获取组标识号 | kernel/sys.c |
setgid | 设置组标志号 | kernel/sys.c |
getegid | 获取有效组标识号 | kernel/sys.c |
setegid | 设置有效组标识号 | kernel/sys.c |
geteuid | 获取有效用户标识号 | kernel/sys.c |
seteuid | 设置有效用户标识号 | kernel/sys.c |
setregid | 分别设置真实和有效的的组标识号 | kernel/sys.c |
setreuid | 分别设置真实和有效的用户标识号 | kernel/sys.c |
getresgid | 分别获取真实的,有效的和保存过的组标识号 | kernel/sys.c |
setresgid | 分别设置真实的,有效的和保存过的组标识号 | kernel/sys.c |
getresuid | 分别获取真实的,有效的和保存过的用户标识号 | kernel/sys.c |
setresuid | 分别设置真实的,有效的和保存过的用户标识号 | kernel/sys.c |
setfsgid | 设置文件系统检查时使用的组标识号 | kernel/sys.c |
setfsuid | 设置文件系统检查时使用的用户标识号 | kernel/sys.c |
getgroups | 获取后补组标志清单 | kernel/groups.c |
setgroups | 设置后补组标志清单 | kernel/groups.c |
函数名 | 描述 | 文件 |
---|---|---|
ipc | 进程间通信总控制调用 | |
信号 | ||
sigaction | 设置对指定信号的处理方法 | |
sigprocmask | 根据参数对信号集中的信号执行阻塞/解除阻塞等操作 | |
sigpending | 为指定的被阻塞信号设置队列 | |
sigsuspend | 挂起进程等待特定信号 | |
signal | 参见signal | |
kill | 向进程或进程组发信号 | kernel/signal.c |
*sigblock | 向被阻塞信号掩码中添加信号,已被sigprocmask代替 | |
*siggetmask | 取得现有阻塞信号掩码,已被sigprocmask代替 | |
*sigsetmask | ||
*sigmask | 将给定的信号转化为掩码,已被sigprocmask代替 | |
*sigpause | 作用同sigsuspend,已被sigsuspend代替 | |
sigvec | 为兼容BSD而设的信号处理函数,作用类似sigaction | |
ssetmask | ANSI C的信号处理函数,作用类似sigaction | |
2、消息 | ||
msgctl | 消息控制操作 | ipc/msg.c |
msgget | 获取消息队列 | ipc/msg.c |
msgsnd | 发消息 | ipc/msg.c |
msgrcv | 取消息 | ipc/msg.c |
3、管道 | ||
pipe | 创建管道 | fs/pipe.c |
4、信号量 | ||
semctl | 信号量控制 | ipc/sem.c |
semget | 获取一组信号量 | ipc/sem.c |
semop | 信号量操作 | ipc/sem.c |
5、共享内存 | ||
shmctl | 控制共享内存 | ipc/shm.c |
shmget | 获取共享内存 | ipc/shm.c |
shmat | 连接共享内存 | ipc/shm.c |
shmdt | 拆卸共享内存 | ipc/shm.c |
转自?
https://www.ibm.com/developerworks/cn/linux/kernel/syscall/part1/appendix.html?
http://blog.csdn.net/sinat_26227857/article/details/44244433
from:https://blog.csdn.net/cheng_fangang/article/details/9333037
所谓“监牢”就是指通过chroot机制来更改某个进程所能看到的根目录,即将某进程限制在指定目录中,保证该进程只能对该目录及其子目录的文件有所动作,从而保证整个服务器的安全。
????创建chroot“监牢”
????以前,Unix/Linux上的daemon都是以root权限启动的。当时,这似乎是一件理所当然的事情,因为像Apache这样的服务器软件需要绑定到“众所周知”的端口上(小于1024)来监听HTTP请求,而root是惟一有这种权限的用户。
????但是,随着攻击者活动的日益频繁,尤其是缓冲区溢出漏洞数量的激增,使服务器安全受到了更大的威胁。一旦某个网络服务存在漏洞,攻击者就能够访问并控制整个系统。因此,为了减缓这种攻击所带来的负面影响,现在服务器软件通常设计为以root权限启动,然后服务器进程自行放弃root,再以某个低权限的系统账号来运行进程。这种方式的好处在于一旦该服务被攻击者利用漏洞入侵,由于进程权限很低,攻击者得到的访问权限又是基于这个较低权限的,对系统造成的危害比以前减轻了许多。
????有些攻击者会试图找到系统其它的漏洞来提升权限,直至达到root。由于本地安全性远低于远程安全保护,因此攻击者很有可能在系统中找到可以提升权限的东西。即使没有找到本地漏洞,攻击者也可能会造成其它损害,如删除文件、涂改主页等。
????为了进一步提高系统安全性,Linux内核引入了chroot机制。chroot是内核中的一个系统调用,软件可以通过调用库函数chroot,来更改某个进程所能见到的根目录。比如,Apache软件安装在/usr/local/httpd/目录下,以root用户(或具有相同权限的其它账号)启动Apache,这个root权限的父进程会派生数个以nobody权限运行的子进程,具体情况取决于个人设置。父进程监听请求自80端口的tcp数据流,然后根据内部算法将这个请求分配给某个子进程来处理。这时Apache子进程所处的目录继承自父进程,即/usr/local/httpd/。
????但是,一旦目录权限设定失误,被攻击的Apache子进程可以访问/usr/local、/usr、/tmp,甚至整个文件系统,因为Apache进程所处的根目录仍是整个文件系统的根。如果能够利用chroot将Apache限制在/usr/local/httpd/,那么,Apache所能存取的文件都是/usr/local/httpd/下的文件或其子目录下的文件。创建chroot“监牢”的作用就是将进程权限限制在文件系统目录树中的某一子树中。
????为什么需要jail
????将软件chroot化的一个问题是该软件运行时需要的所有程序、配置文件和库文件都必须事先安装到chroot目录中,通常称这个目录为chroot jail(chroot“监牢”)。如果要在“监牢”中运行/sbin/httpd,而事实上根本看不到文件系统中那个真正的/sbin目录。因此需要事先创建/sbin目录,并将httpd复制到其中。同时httpd需要几个库文件,执行如下命令可以看到这些库文件(在真实的文件系统下运行)。
#ldd /sbin/httpd libaprutil-0.so.0 => /usr/local/httpd/lib/libaprutil-0.so.0 (0x40017000) libgdbm.so.2 => /usr/lib/libgdbm.so.2 (0x4003c000) libdb-4.0.so => /lib/libdb-4.0.so (0x40043000) libpthread.so.0 => /lib/tls/libpthread.so.0 (0x400eb000) libexpat.so.0 => /usr/lib/libexpat.so.0 (0x400f8000) libapr-0.so.0 => /usr/local/httpd/lib/libapr-0.so.0 (0x40118000) librt.so.1 => /lib/librt.so.1 (0x40139000) lIBM.so.6 => /lib/tls/lIBM.so.6 (0x4014b000) libcrypt.so.1 => /lib/libcrypt.so.1 (0x4016d000) libnsl.so.1 => /lib/libnsl.so.1 (0x4019a000) libdl.so.2 => /lib/libdl.so.2 (0x401af000) libc.so.6 => /lib/tls/libc.so.6 (0x42000000) /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000) |
????这意味着还需要在“监牢”中创建lib目录,并将库文件复制到其中。这一工作可以交由计算机完成,用jail等软件包来帮助简化chroot“监牢”建立的过程。
????编译和安装jail
????从http://www.jmcresearch.com/static/dwn/projects/jail/jail.tar.gz可以下载到jail的最新版本,它是由位于http://www.jmcresearch.com/projects/jail/的jail chroot项目小组开发的。该软件包包含了帮助自动创建chroot“监牢”的C程序、Perl程序和Bash脚本。
????首先将jail.tar.gz置于任意目录,然后执行命令:
????#tar xzvf jail.tar.gz && cd jail/src
????按照个人实际情况修改makefile文件,尤其是安装路径(默认安装路径是/usr/local)、体系结构(jail支持Linux、FreeBSD、IRIX和Solaris),以及编译选项等。最后执行命令:
????#make && make install
????为jail创建chroot“监牢”
????现在创建一个目录作为chroot“监牢”,以/var/chroot/为例。执行下面的命令为chroot“监牢”创建环境:
????#/usr/local/bin/mkjailenv /var/chroot
????这样“监牢”就建好了。jail软件包提供了几个Perl脚本作为其核心命令,包括mkjailenv、addjailuser和addjailsw。如addjailsw会从真实文件系统中拷贝二进制可执行文件及其相关的其它文件(包括库文件、辅助性文件和设备文件)到该“监牢”中。
????为jail“监牢”添加软件
????接下来需要为这个“监牢”增加一些软件,以便让它运行起来。执行以下命令安装一些基本的软件,包括ls、cat、cp等程序和ld-linux.so.2等库文件。
????#/usr/local/bin/addjailsw /var/chroot
????事实上仅有这些基本软件是不够的,还需要把一些真正有用的东西限制起来。下面的例子展示了为“监牢”添加arp程序的过程:
#/usr/local/bin/addjailsw /var/chroot -P arp addjailsw A component of Jail (version 1.9 for linux) http://www.jmcresearch.com/projects/jail/ Juan M. Casillas <juanm.casillas@jmcresearch.com> Guessing arp args(0) Warning: file .//lib/tls/libc.so.6 exists. Overwritting it Warning: file .//lib/ld-linux.so.2 exists. Overwritting it Warning: file .//etc/ld.so.cache exists. Overwritting it Warning: file .//usr/lib/locale/locale-archive exists. Overwritting it Warning: file .//usr/share/locale/locale.alias exists. Overwritting it Warning: can't create /proc/net/arp from the /proc filesystem Done. |
????再以Apache服务器软件为例:
#addjailsw /var/chroot/ -P /usr/local/httpd/bin/httpd addjailsw A component of Jail (version 1.9 for linux) http://www.jmcresearch.com/projects/jail/ Juan M. Casillas <juanm.casillas@jmcresearch.com> Guessing /usr/local/httpd/bin/httpd args(0) Warning: file /var/chroot//lib/libssl.so.4 exists. Overwritting it Warning: file /var/chroot//lib/libcrypto.so.4 exists. Overwritting it Warning: file /var/chroot//lib/libresolv.so.2 exists. Overwritting it …… Done. |
????不用在意那些警告信息,因为jail会调用ldd检查httpd用到的库文件。而几乎所有基于共享库的二进制可执行文件都需要上述的几个库文件。
接下来将Apache的相关文件拷贝到“监牢”中:
????#cp -a /usr/local/httpd/ /var/chroot/usr/local/
????可根据个人情况依次将Apache需要的文件复制到“监牢”中。
????“监禁”囚犯
????有时候需要为chroot“监牢”创建新的用户,比如Apache要求创建nobody用户作为子进程用户。鉴于可能有其它进程使用nobody,还可以使用另一用户——httpd。首先需要在真实系统中创建httpd用户:
????#useradd -d /var/chroot -s /usr/local/bin/jail httpd
????然后执行以下命令在chroot“监牢”中创建httpd用户:
????#/usr/local/bin/addjailuser /var/chroot /usr/local/httpd /usr/sbin/httpd httpd
????接下来修改/var/chroot/usr/local/httpd/conf/httpd.conf,将User nobody替换为User httpd。由于chroot后Apache将以httpd身份启动进程,只有root有权将Apache绑定在低端口上(通常为80),因此还需要修改端口值,该值必须大于1024(假设为8080)。这个修改要应用到Apache的所有配置文件中,包括虚拟主机的配置。至于Apache的其它设置,与在真实文件系统时一样配置即可。
????接下来需要复制一些其它的文件。启动Apache最常见的方式就是调用apachectl,这是个Bash脚本。查看这个文件,会发现如下行:
HTTPD='/usr/local/httpd/bin/httpd' LYNX="lynx -dump" ULIMIT_MAX_FILES="ulimit -S -n `ulimit -H -n`" ARGV="-h" $HTTPD -k $ARGV $HTTPD -k start -DSSL $HTTPD -t $LYNX $STATUSURL | awk ' /process$/ { print; exit } { print } ' |
????其中ulimit、lynx和awk是辅助性的程序。另外需要注意的是,程序使用不同的参数时,可能会使用不同的库文件,因此为了让Apache完整运行,使用下面的命令来跟踪所有可能的文件:
????#/usr/local/bin/addjailsw /var/chroot -P httpd "-k start -DSSL"
????用上述的参数替换引号中的参数,完成全部的工作。
????最后,让成功jail的Apache运行起来:
????#su - httpd &
????打开浏览器进行测试,访问Web服务器时记住加上8080端口号。
????jail高级应用
????在前面的介绍中,使用了jail软件包中的三个Perl脚本。这里详细介绍这三个脚本的使用,以便高级用户使用。
mkjailenv
????用法:mkjailenv chrootdir
????作用:创建chroot“监牢”目录,并且从真实文件系统中拷贝基本的软件环境。
????参数:
????chrootdir指定chroot“监牢”的路径。
addjailsw
????用法:addjailsw chrootdir [-D] [-P program args]?
????作用:从真实文件系统中拷贝指定的文件及其相关文件。
????????参数:
????chrootdir指定chroot“监牢”的路径。
????-D显示详细信息。
????-P program args指定要添加到“监牢”中的软件。program可以是个文件名,也可以是文件的完整路径;args是参数。比如可以这样执行addjailsw:
????#addjailsw /var/chroot -P vi "-c q"
addjailuser
????用法:addjailuser chrootdir userdir usershell username?
????作用:创建新的chroot“监牢”用户。
????????参数:
????chrootdir指定chroot“监牢”的路径。
????userdir指定新添加用户的主目录(相对于chroot“监牢”目录)。
????usershell指定新用户使用的Shell的完整路径(比如/bin/bash)。
????username为新添加的用户名。
????比如:
????#addjailuser /var/chroot /home/ftp /bin/csh ftp
????这个脚本会自动修改“监牢”中的/etc/passwd、/etc/group和/etc/shadow文件。
????从上文看,如果仅使Apache一个软件运行在“监牢”中,mkjailenv似乎过于“热心”了,因此可以不运行mkjailenv /var/chroot命令,而只运行addjailsw /var/chroot -P httpd或在调试完chroot“监牢”后删除多余的文件,并修改/etc/passwd中多余的用户信息。由此想到,现在大多数流行的Web站点都采用Apache+PHP+MySQL+SSL的搭配(可能还会有FTP、Mail、Perl等组件),因此完全可以建立一个综合的Web“监牢”。系统管理员可以为这个“监牢”设置软件环境,当然这个环境只包括维护Apache+PHP+MySQL+SSL这些组件的必备工具,如使用Bash、SSH、编译软件或上传等。这可能是一个浩大的工程,但是却非常有意义。参考上面的方法,大家可以尝试jail出完美的服务器来。