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