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 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301
|
#!/bin/bash
#
# Run through a series of tests to try out the various capability
# manipulations possible 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
pass_capsh --current
# Validate that PATH expansion works
PATH=$(/bin/pwd)/junk:$(/bin/pwd) capsh == == == --modes
if [ $? -ne 0 ]; then
echo "Failed to execute capsh consecutively for capability manipulation"
exit 1
fi
# Make a local non-setuid-0 version of capsh and call it privileged
cp ./tcapsh-static ./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_setuid'
# Explore keep_caps support
pass_capsh --keep=0 --keep=1 --keep=0 --keep=1 --print
/bin/rm -f tcapsh
/bin/cp tcapsh-static tcapsh
/bin/chown root.root tcapsh
/bin/chmod u+s tcapsh
/bin/ls -l tcapsh
# leverage keep caps to maintain capabilities across 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 --current -- -c "./tcapsh --keep=1 --caps=\"cap_net_raw,cap_net_bind_service=ip\" --current --uid=1 --current --caps=\"cap_net_raw,cap_net_bind_service=pie\" --current"
# 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 --current -- -c "./tcapsh --caps=\"cap_net_raw,cap_net_bind_service=ip cap_setuid=p\" --current --cap-uid=2 --current --caps=\"cap_net_raw,cap_net_bind_service=pie\" --current"
# This fails, on 2.6.24, but shouldn't
pass_capsh --uid=1 -- -c "./tcapsh --keep=1 --caps=\"cap_net_raw,cap_net_bind_service=ip\" --uid=1 --forkfor=10 --caps= --current --killit=9 --current"
# 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"
# observe that effective cap_setpcap is required to drop bset
fail_capsh --caps="=ep cap_setpcap-ep" --drop=cap_setuid --current
pass_capsh --strict --caps="cap_setpcap=ep" --drop=cap_setuid --current
fail_capsh --strict --caps="cap_setpcap=p" --drop=cap_setuid --current
fail_capsh --strict --caps="=ep cap_setpcap-e" --drop=cap_setuid --current
# observe that effective cap_setpcap is required to raise non-p bits
fail_capsh --strict --caps="cap_setpcap=p" --inh=cap_chown --current
# non-strict mode and capsh figures it out
pass_capsh --caps="cap_setpcap=p" --inh=cap_chown --current
# permitted bits can be raised in inheritable flag without being effective.
pass_capsh --strict --caps="cap_chown=p" --inh=cap_chown --current
# 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 -- -c "./tcapsh-static --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 acquire privilege by the ambient method.
./setcap = ./privileged
fail_capsh --keep=1 --uid=$nouid --inh=cap_setuid --addamb=cap_setuid -- \
-c "./privileged --current --uid=1"
pass_capsh --keep=1 --uid=$nouid --strict \
--caps="cap_setuid=p cap_setpcap=ep" \
--inh=cap_setuid --addamb=cap_setuid --current
# No effective capabilities are needed to raise or lower ambient values.
pass_capsh --keep=1 --uid=$nouid --strict --caps="cap_setuid=p" \
--inh=cap_setuid --addamb=cap_setuid --current
pass_capsh --keep=1 --uid=$nouid --strict --iab="!^cap_setuid" \
--caps="cap_setuid=pi" --current --delamb=cap_setuid --current
# 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 --current --uid=1"
# validate IAB setting with an ambient capability
pass_capsh --iab='!%cap_chown,^cap_setpcap,cap_setuid'
fail_capsh --mode=PURE1E --iab='!%cap_chown,^cap_setuid'
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 ./tcapsh-static ./nsprivileged && /bin/chmod -s ./nsprivileged
./setcap -n 1 all=ep ./nsprivileged
if [ $? -eq 0 ]; then
./getcap -n ./nsprivileged | grep -F "[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"
exit 1
fi
echo "PASSED"
else
echo "no Go support compiled, so skipping Go tests"
fi
rm -f compare-cap
echo "attempt to exploit kernel bug"
./uns_test
if [ $? -ne 0 ]; then
echo "upgrade your kernel"
exit 1
fi
echo "ALL TESTS PASSED!"
|