aboutsummaryrefslogtreecommitdiff
path: root/progs/quicktest.sh
blob: ba64ab5d69384e79335df83463313d32f21f9348 (plain)
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
#!/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_sys_admin'

# 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 -- -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 -- -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 --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 ./tcapsh-static ./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, 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!"