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_sys_admin' 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_admin=ip\" --print --uid=1 --print --caps=\"cap_net_raw,cap_net_admin=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_admin=ip cap_setuid=p\" --print --cap-uid=2 --print --caps=\"cap_net_raw,cap_net_admin=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_admin=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 -- -c "./privileged --uid=$nouid" 134 135# change the way the capability is obtained (make it inheritable) 136./setcap cap_setuid,cap_setgid=ei ./privileged 137 138# Note, the bounding set (edited with --drop) only limits p 139# capabilities, not i's. 140pass_capsh --secbits=47 --inh=cap_setuid,cap_setgid --drop=cap_setuid \ 141 --uid=1 --print -- -c "./privileged --uid=$nouid" 142 143# test that we do not support capabilities on setuid shell-scripts 144/bin/cat > hack.sh <<EOF 145#!/bin/bash 146/usr/bin/id 147mypid=\$\$ 148caps=\$(./getpcaps \$mypid 2>&1 | /usr/bin/cut -d: -f2) 149if [ "\$caps" != " =" ]; then 150 echo "Shell script got [\$caps] - you should upgrade your kernel" 151 exit 1 152else 153 ls -l \$0 154 echo "Good, no capabilities [\$caps] for this setuid-0 shell script" 155fi 156exit 0 157EOF 158/bin/chmod +xs hack.sh 159./capsh --uid=1 --inh=none --print -- ./hack.sh 160status=$? 161/bin/rm -f ./hack.sh 162if [ $status -ne 0 ]; then 163 echo "shell scripts can have capabilities (bug)" 164 exit 1 165fi 166 167# Max lockdown (ie., pure capability model as POSIX.1e intended). 168secbits=0x2f 169if ./capsh --has-ambient ; then 170 secbits="0xef --noamb" 171fi 172pass_capsh --keep=1 --uid=$nouid --caps=cap_setpcap=ep \ 173 --drop=all --secbits=$secbits --caps= --print 174 175# Verify we can chroot 176pass_capsh --chroot=$(/bin/pwd) 177pass_capsh -- -c "./tcapsh-static --chroot=$(/bin/pwd) ==" 178fail_capsh --chroot=$(/bin/pwd) -- -c "echo oops" 179 180./capsh --has-ambient 181if [ $? -eq 0 ]; then 182 echo "test ambient capabilities" 183 184 # Ambient capabilities (any file can inherit capabilities) 185 pass_capsh --noamb 186 187 # test that shell scripts can inherit through ambient capabilities 188 /bin/cat > hack.sh <<EOF 189#!/bin/bash 190/usr/bin/id 191mypid=\$\$ 192caps=\$(./getpcaps \$mypid 2>&1 | /usr/bin/cut -d: -f2) 193if [ "\$caps" != " = cap_setuid+i" ]; then 194 echo "Shell script got [\$caps]" 195 exit 0 196fi 197ls -l \$0 198echo "no capabilities [\$caps] for this shell script" 199exit 1 200EOF 201 /bin/chmod +x hack.sh 202 pass_capsh --keep=1 --uid=$nouid --inh=cap_setuid --addamb=cap_setuid -- ./hack.sh 203 204 /bin/rm -f hack.sh 205 206 # Next force the privileged binary to have an empty capability set. 207 # This is sort of the opposite of privileged - it should ensure that 208 # the file can never acquire privilege by the ambient method. 209 ./setcap = ./privileged 210 fail_capsh --keep=1 --uid=$nouid --inh=cap_setuid --addamb=cap_setuid -- -c "./privileged --print --uid=1" 211 212 # finally remove the capability from the privileged binary and try again. 213 ./setcap -r ./privileged 214 pass_capsh --keep=1 --uid=$nouid --inh=cap_setuid --addamb=cap_setuid -- -c "./privileged --print --uid=1" 215 216 # validate IAB setting with an ambient capability 217 pass_capsh --iab='!%cap_chown,^cap_setpcap,cap_sys_admin' 218 fail_capsh --mode=PURE1E --iab='!%cap_chown,^cap_sys_admin' 219fi 220/bin/rm -f ./privileged 221 222echo "testing namespaced file caps" 223 224# nsprivileged capsh will have an ns rootid value (this is 225# the same setup as an earlier test but with a ns file cap). 226rm -f nsprivileged 227cp ./tcapsh-static ./nsprivileged && /bin/chmod -s ./nsprivileged 228./setcap -n 1 all=ep ./nsprivileged 229if [ $? -eq 0 ]; then 230 ./getcap -n ./nsprivileged | fgrep "[rootid=1]" 231 if [ $? -ne 0 ]; then 232 echo "FAILED setting ns rootid on file" 233 exit 1 234 fi 235 # since this is a ns file cap and not a regular one, it should not 236 # lead to a privilege escalation outside of the namespace it 237 # refers to. We suppress uid=0 privilege and confirm this 238 # nsprivileged binary does not have the power to change uid. 239 fail_capsh --secbits=$secbits --print -- -c "./nsprivileged --uid=$nouid" 240else 241 echo "ns file caps not supported - skipping test" 242fi 243rm -f nsprivileged 244 245# If the build tree compiled the Go cap package. 246if [ -f ../go/compare-cap ]; then 247 cp ../go/compare-cap . 248 LD_LIBRARY_PATH=../libcap ./compare-cap 249 if [ $? -ne 0 ]; then 250 echo "FAILED to execute go binary" 251 exit 1 252 fi 253 LD_LIBRARY_PATH=../libcap ./compare-cap 2>&1 | grep "skipping file cap tests" 254 if [ $? -eq 0 ]; then 255 echo "FAILED not engaging file cap tests" 256 fi 257 echo "PASSED" 258else 259 echo "no Go support compiled, so skipping Go tests" 260fi 261rm -f compare-cap 262 263echo "attempt to exploit kernel bug" 264./uns_test 265if [ $? -ne 0 ]; then 266 echo "upgrade your kernel" 267 exit 1 268fi 269 270echo "ALL TESTS PASSED!" 271