$ ls -lL /usr/bin/awk
-rwsr-xr-x 1 root root 121976 Mar 23 2012 /usr/bin/awk
$ awk 'BEGIN{system("id")}'
uid=1000(bob) gid=1000(bob) groups=1000(bob)
In your example, it's not awk
which is dropping privileges or not "respecting its setuid bit", but the /bin/sh
command that awk uses to implement its system()
function.
Just like its C counterpart, awk's system()
does not parse and run the command directly, but by passing it as an argument to /bin/sh -c
. If /bin/sh
is bash (or the Debian version of dash, or a couple of other shells which copied this misfeature from bash), it will reset its effective uid back to the real one.
The same thing applies to print | "cmd"
or "cmd" | getline
in awk -- they're implemented with popen(3)
which calls /bin/sh -c
. Notice that it's always /bin/sh
(or the system's shell, eg. /system/bin/sh
on Android), not the user's login shell or that from the $SHELL
environment variable. [1]
This is different in perl: perl's system
, exec
, open "|-"
, open2
, open3
, etc will run the command directly if they're called with multiple arguments or if the command does not contain shell metacharacters:
$ id -nu
ahq
$ ls -l /tmp/perl
-rwsr-xr-x 1 dummy_user dummy_user 3197768 Mar 24 18:13 /tmp/perl
$ env - /tmp/perl -e 'system("id -nu")'
dummy_user
$ env - /tmp/perl -e 'system("{ id -nu; }")'
ahq
This example is on Debian 10. On other systems like FreeBSD or older Debian, both commands will print the same thing, because their /bin/sh
does not drop privileges. [2]
Notes:
[1] Other programs like vim
and less
do use the $SHELL
environment variable, so they're easily "fixable" by pointing it to some wrapper. In vim
you could also use :set shcf=-pc
to pass the -p
option to the shell used for the :!
and similar commands.
[2] The perl example will also work on OpenBSD just like on FreeBSD, provided that you replace the env - /tmp/perl 'script'
with the more obtuse echo 'script' | /tmp/perl /dev/fd/0
.
OpenBSD's perl will reject the -e
argument and refuse to read its script from the stdin when running in setuid mode (see this which is ending here -- OpenBSD supposedly has secure setuid scripts).
But that does not apply to /dev/fd/N
, which perl is handling itself when given as a script name (only the /dev/fd/N
form, not /dev/stdin
or /proc/self/fd/N
).
obsd66$ ls -l /tmp/perl
-rwsr-xr-x 1 dummy_user dummy_user 10728 Mar 25 18:34 /tmp/perl
obsd66$ env - /tmp/perl -e 'system("{ id -nu; }")'
No -e allowed while running setuid.
obsd66$ echo 'system("{ id -nu; }")' | env - /tmp/perl
No program input from stdin allowed while running setuid.
obsd66$ echo 'system("{ id -nu; }")' | env - /tmp/perl /dev/stdin
Can't open perl script "/dev/stdin": Operation not permitted
obsd66$ echo 'system("{ id -nu; }")' | env - /tmp/perl /dev/fd/0
dummy_user
debian10$ su - other_user -c 'perl /dev/fd/7' 7<<<'print "OK\n"'
OK
debian10$ su - other_user -c 'perl /proc/self/fd/7' 7<<<'print "OK\n"'
Can't open perl script "/proc/self/fd/7": Permission denied
bash
root shell?awk
works as intended. Try this,awk 1 /etc/shadow
and if it honours its setuid bit it will display the contents of the otherwise protected shadow file.