1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
|
#!/bin/bash
#
# Run through a series of tests to try out the various capability
# manipulations posible through exec.
#
# [Run this as root in a root-enabled process tree.]
try_capsh () {
echo "TEST: ./capsh $*"
./capsh "$@"
if [ $? -ne 0 ]; then
echo FAILED
return 1
else
echo PASSED
return 0
fi
}
fail_capsh () {
echo -n "EXPECT FAILURE: "
try_capsh "$@"
if [ $? -eq 1 ]; then
echo "[WHICH MEANS A PASS!]"
return 0
else
echo "Undesired result - aborting"
echo "PROBLEM TEST: $*"
exit 1
fi
}
pass_capsh () {
echo -n "EXPECT SUCCESS: "
try_capsh "$@"
if [ $? -eq 0 ]; then
return 0
else
echo "Undesired result - aborting"
echo "PROBLEM TEST: $*"
exit 1
fi
}
pass_capsh --print
# Make a local non-setuid-0 version of capsh and call it privileged
cp ./capsh ./privileged && /bin/chmod -s ./privileged
if [ $? -ne 0 ]; then
echo "Failed to copy capsh for capability manipulation"
exit 1
fi
# Give it the forced capability it could need
./setcap all=ep ./privileged
if [ $? -ne 0 ]; then
echo "Failed to set all capabilities on file"
exit 1
fi
./setcap cap_setuid,cap_setgid=ep ./privileged
if [ $? -ne 0 ]; then
echo "Failed to set limited capabilities on privileged file"
exit 1
fi
# validate libcap modes:
pass_capsh --inh=cap_chown --mode=PURE1E --print --inmode=PURE1E
pass_capsh --mode=NOPRIV --print --inmode=NOPRIV
pass_capsh --mode=PURE1E --print --mode=NOPRIV --inmode=NOPRIV
fail_capsh --mode=NOPRIV --print --mode=PURE1E
fail_capsh --user=nobody --mode=NOPRIV --print -- ./privileged
# simple IAB setting (no ambient) in pure1e mode.
pass_capsh --mode=PURE1E --iab='!%cap_chown,cap_sys_admin'
# Explore keep_caps support
pass_capsh --keep=0 --keep=1 --keep=0 --keep=1 --print
/bin/rm -f tcapsh
/bin/cp capsh tcapsh
/bin/chown root.root tcapsh
/bin/chmod u+s tcapsh
/bin/ls -l tcapsh
# leverage keep caps to maintain capabilities accross a change of euid
# from setuid root to capable luser (as per wireshark/dumpcap 0.99.7)
# This test is subtle. It is testing that a change to self, dropping
# euid=0 back to that of the luser keeps capabilities.
pass_capsh --uid=1 -- -c "./tcapsh --keep=1 --caps=\"cap_net_raw,cap_net_admin=ip\" --print --uid=1 --print --caps=\"cap_net_raw,cap_net_admin=pie\" --print"
# this test is a change of user to a new user, note we need to raise
# the cap_setuid capability (libcap has a function for that) in this case.
pass_capsh --uid=1 -- -c "./tcapsh --caps=\"cap_net_raw,cap_net_admin=ip cap_setuid=p\" --print --cap-uid=2 --print --caps=\"cap_net_raw,cap_net_admin=pie\" --print"
# This fails, on 2.6.24, but shouldn't
pass_capsh --uid=1 -- -c "./tcapsh --keep=1 --caps=\"cap_net_raw,cap_net_admin=ip\" --uid=1 --forkfor=10 --caps= --print --killit=9 --print"
# only continue with these if --secbits is supported
./capsh --secbits=0x2f > /dev/null 2>&1
if [ $? -ne 0 ]; then
echo "unable to test securebits manipulation - assume not supported (PASS)"
rm -f tcapsh
rm -f privileged
exit 0
fi
# nobody's uid. Static compilation of the capsh binary can disable pwd
# info discovery.
nouid=$(/usr/bin/id nobody -u)
pass_capsh --secbits=42 --print
fail_capsh --secbits=32 --keep=1 --keep=0 --print
pass_capsh --secbits=10 --keep=0 --keep=1 --print
fail_capsh --secbits=47 -- -c "./tcapsh --uid=$nouid"
/bin/rm -f tcapsh
# Suppress uid=0 privilege
fail_capsh --secbits=47 --print -- -c "./capsh --uid=$nouid"
# suppress uid=0 privilege and test this privileged
pass_capsh --secbits=0x2f --print -- -c "./privileged --uid=$nouid"
# observe that the bounding set can be used to suppress this forced capability
fail_capsh --drop=cap_setuid --secbits=0x2f --print -- -c "./privileged --uid=$nouid"
# change the way the capability is obtained (make it inheritable)
./setcap cap_setuid,cap_setgid=ei ./privileged
# Note, the bounding set (edited with --drop) only limits p
# capabilities, not i's.
pass_capsh --secbits=47 --inh=cap_setuid,cap_setgid --drop=cap_setuid \
--uid=1 --print -- -c "./privileged --uid=$nouid"
# test that we do not support capabilities on setuid shell-scripts
/bin/cat > hack.sh <<EOF
#!/bin/bash
/usr/bin/id
mypid=\$\$
caps=\$(./getpcaps \$mypid 2>&1 | /usr/bin/cut -d: -f2)
if [ "\$caps" != " =" ]; then
echo "Shell script got [\$caps] - you should upgrade your kernel"
exit 1
else
ls -l \$0
echo "Good, no capabilities [\$caps] for this setuid-0 shell script"
fi
exit 0
EOF
/bin/chmod +xs hack.sh
./capsh --uid=1 --inh=none --print -- ./hack.sh
status=$?
/bin/rm -f ./hack.sh
if [ $status -ne 0 ]; then
echo "shell scripts can have capabilities (bug)"
exit 1
fi
# Max lockdown (ie., pure capability model as POSIX.1e intended).
secbits=0x2f
if ./capsh --has-ambient ; then
secbits="0xef --noamb"
fi
pass_capsh --keep=1 --uid=$nouid --caps=cap_setpcap=ep \
--drop=all --secbits=$secbits --caps= --print
# Verify we can chroot
pass_capsh --chroot=$(/bin/pwd)
pass_capsh --chroot=$(/bin/pwd) ==
fail_capsh --chroot=$(/bin/pwd) -- -c "echo oops"
./capsh --has-ambient
if [ $? -eq 0 ]; then
echo "test ambient capabilities"
# Ambient capabilities (any file can inherit capabilities)
pass_capsh --noamb
# test that shell scripts can inherit through ambient capabilities
/bin/cat > hack.sh <<EOF
#!/bin/bash
/usr/bin/id
mypid=\$\$
caps=\$(./getpcaps \$mypid 2>&1 | /usr/bin/cut -d: -f2)
if [ "\$caps" != " = cap_setuid+i" ]; then
echo "Shell script got [\$caps]"
exit 0
fi
ls -l \$0
echo "no capabilities [\$caps] for this shell script"
exit 1
EOF
/bin/chmod +x hack.sh
pass_capsh --keep=1 --uid=$nouid --inh=cap_setuid --addamb=cap_setuid -- ./hack.sh
/bin/rm -f hack.sh
# Next force the privileged binary to have an empty capability set.
# This is sort of the opposite of privileged - it should ensure that
# the file can never aquire privilege by the ambient method.
./setcap = ./privileged
fail_capsh --keep=1 --uid=$nouid --inh=cap_setuid --addamb=cap_setuid -- -c "./privileged --print --uid=1"
# finally remove the capability from the privileged binary and try again.
./setcap -r ./privileged
pass_capsh --keep=1 --uid=$nouid --inh=cap_setuid --addamb=cap_setuid -- -c "./privileged --print --uid=1"
# validate IAB setting with an ambient capability
pass_capsh --iab='!%cap_chown,^cap_setpcap,cap_sys_admin'
fail_capsh --mode=PURE1E --iab='!%cap_chown,^cap_sys_admin'
fi
/bin/rm -f ./privileged
echo "testing namespaced file caps"
# nsprivileged capsh will have an ns rootid value (this is
# the same setup as an earlier test but with a ns file cap).
rm -f nsprivileged
cp ./capsh ./nsprivileged && /bin/chmod -s ./nsprivileged
./setcap -n 1 all=ep ./nsprivileged
if [ $? -eq 0 ]; then
./getcap -n ./nsprivileged | fgrep "[rootid=1]"
if [ $? -ne 0 ]; then
echo "FAILED setting ns rootid on file"
exit 1
fi
# since this is a ns file cap and not a regular one, it should not
# lead to a privilege escalation outside of the namespace it
# refers to. We suppress uid=0 privilege and confirm this
# nsprivileged binary does not have the power to change uid.
fail_capsh --secbits=$secbits --print -- -c "./nsprivileged --uid=$nouid"
else
echo "ns file caps not supported - skipping test"
fi
rm -f nsprivileged
# If the build tree compiled the Go cap package.
if [ -f ../go/compare-cap ]; then
cp ../go/compare-cap .
LD_LIBRARY_PATH=../libcap ./compare-cap
if [ $? -ne 0 ]; then
echo "FAILED to execute go binary"
exit 1
fi
LD_LIBRARY_PATH=../libcap ./compare-cap 2>&1 | grep "skipping file cap tests"
if [ $? -eq 0 ]; then
echo "FAILED not engaging file cap tests"
fi
echo "PASSED"
else
echo "no Go support compiled"
fi
rm -f compare-cap
|