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