Common binaries with support for command execution, like `awk` and `vim`, not respecting setuid bit?
I'm hardening a Linux system and wanted to test (setuid
-based) shell escapes using common binaries, like awk
, vim
, etc., supporting command executing.
However, all binaries I've tested except sh
and bash
don't respect their setuid
bit.
In particular, awk
continues to execute as a normal user:
$ ls -lL /usr/bin/awk
-rwsr-xr-x 1 root root 121976 Mar 23 2012 /usr/bin/awk
$ id
uid=1000(bob) gid=1000(bob) groups=1000(bob)
$ awk 'BEGIN{system("id")}'
uid=1000(bob) gid=1000(bob) groups=1000(bob)
In contrast, bash
executes as root
when given the -p
option:
$ ls -la /bin/bash
-rwsr-xr-x 1 root root 1168776 Apr 18 2019 /bin/bash
$ /bin/bash -p
# id
uid=1000(bob) gid=1000(bob) euid=0(root) groups=1000(bob)
Is there any way to make awk
, vim
, less
, etc. respect the setuid
bit and execute the command as root
?
OS:
# cat /etc/os-release
PRETTY_NAME="Debian GNU/Linux 10 (buster)"
NAME="Debian GNU/Linux"
VERSION_ID="10"
VERSION="10 (buster)"
VERSION_CODENAME=buster
ID=debian
HOME_URL="https://www.debian.org/"
SUPPORT_URL="https://www.debian.org/support"
BUG_REPORT_URL="https://bugs.debian.org/"
Update:
parallels@debian-gnu-linux-vm:~$ ls -la /proc/self/fd/0 /dev/fd/0 /dev/stdin
lrwx------ 1 parallels parallels 64 Mar 26 08:15 /dev/fd/0 -> /dev/pts/1
lrwxrwxrwx 1 root root 15 Mar 20 19:56 /dev/stdin -> /proc/self/fd/0
lrwx------ 1 parallels parallels 64 Mar 26 08:15 /proc/self/fd/0 -> /dev/pts/1
1 Answer 1
$ 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
-
So, the only way to have e.g.
awk
execute commands asroot
is to replace the system's shell with another one that doesn't reset the euid? I guess, that's good for security, but not from a developer's point of perspective.Shuzheng– Shuzheng2020年03月25日 09:47:45 +00:00Commented Mar 25, 2020 at 9:47 -
Are you using
/dev/fd/0
as an argument toperl
, since the piped input is available on that file descriptor?Shuzheng– Shuzheng2020年03月25日 09:51:29 +00:00Commented Mar 25, 2020 at 9:51 -
Because OpenBSD's perl will not accept an -e script or reading the script from stdin when running in setuid mode. See the edit history; the prev version used the less obtuse
env - /tmp/perl -e 'system("...")'
. I haven't investigated yet if this is a feature in new versions of perl, or it's an OpenBSD addition.env - /tmp/perl <(echo 'system("id -nu")')
would work too, and not mess with perl's stdin, but not all shells support<(...)
process substitution.user313992– user3139922020年03月25日 10:34:59 +00:00Commented Mar 25, 2020 at 10:34 -
But isn't
/dev/fd/0
stdin, which you sayperl
cannot read from setuid mode?Shuzheng– Shuzheng2020年03月25日 13:31:32 +00:00Commented Mar 25, 2020 at 13:31 -
1exactly user313992– user3139922020年03月26日 16:16:25 +00:00Commented Mar 26, 2020 at 16:16
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.