In a well controlled environment you typically do not want people logging
into servers with privileged access (absent of additional external
processes, such as a keystroke logged session manager). If you have 5
SAs all logging in as root
then how can you audit activity and determine
if it was Tom, Dick or Harry that rebooted the server? Similarly you don’t
want DBAs directly logging in as oracle
; how do you know who dropped
the production table?
Typically this is solved by having the SA (or DBA, or AppOp, or…) login as themselves and then use a privilege escalation method. This method can use alternative authentication processes (e.g. 2FA), audit activity and, if properly configured, limit activity.
On Unix the standard tool is sudo
. Some vendors may provide alternatives
(e.g. Centrify has dzdo
, Fox Technology’s Server Control has suexec
)
but the principles apply.
Business As Usual vs Elevated privileges
Any discussion of privilege escalation needs to consider the user’s work role. What an organisation considers privileged may not be the same as the operating system.
One simple example of this is the df
command. If a normal user runs
this then they may not see every file system mount.
$ df | grep dir1
$ sudo df | grep dir1
/dev/shm 249720 0 249720 0% /tmp/dir1/mountpoint
This situation may occur because the mountpoint may not be reachable from the normal user
drwx------ 3 root root 4096 Aug 26 15:42 /tmp/dir1
So from a Unix perspective, we need elevated permissions to run the df
command and get a full listing. But from a business perspective it
would seem reasonable to allow all the SAs to run sudo df
.
The distinction between what the platform considers privileged and what the business considers privileged really needs to be kept in mind, especially when formulating policies and procedures around how to manage this stuff.
Read-only vs Read-write
A number of commands may be obviously read-only; cat
is a great example
of this; it can read files, but not write to them.
Side Bar: I’ve seen people ask for
sudo echo
because they think they can do things likesudo echo hello > /etc/my.file
. That’s a misunderstanding of how the shell evaluates things; the>
redirection happens at the current shell level, with the normal user privileges. Similarlysudo cat myfile > /etc/my.file
doesn’t work; thecat
may have elevated permissions, but the>
redirect doesn’t.
Some commands can operate in both modes. For example miitool
and
ethtool
can be used to see the state of the network adapter, including
the negotiated speed and duplex. However it can also change the
adapter settings, force renegotiation or even break network connectivity.
Similarly, on some OS’s, ifconfig
will only show the MAC addresses if
run as root
. But now the SA has the ability to change IP addresses
and other settings on the fly.
Typically commands that can make changes should not be part of a BAU profile because they may require a change control process before they can be used.
However, commands that can operate in read and write mode may be able to
be limited; ifconfig
, for example, could be limited to sudo ifconfig -a
which would allow the SA to see the MAC addresses but not change things;
sudo fdisk -l
may be permitted because it allows listing of the
partition tables, without making changes.
A number of commands can be limited to read-only access, and so may be considered part of the BAU scope.
Read-only may also be bad!
At a first glance, it might make sense to be able to grant lots of read-only access commands to people. After all, an SA may need to read a root-only configuration file. For example…
$ cat /etc/securetty
cat: /etc/securetty: Permission denied
What would be the harm in allowing the SA to cat
every file?
Unfortunately there may be files on the system that should be protected
from the SA. Should the SA be allowed to read /opt/my_app/etc/super_secret.txt
? Unlimited cat
would allow this.
Unlimited read access may also allow a pivot to another server; for
example if there is an ssh
key used by a process to send data to another
server then this key is probably passphrase-less (it’s a common scenario).
Allowing unlimited cat
would allow the SA to take a copy of the key and
then try and access the remote server.
Obviously we may have audit logs of the abuse of this privilege, but prevention is always better than detection!
Wildcards
We need to be aware of how sudo
parses commands. The cat
example
might be mitigated by only allowing read access to files in /etc
;
%sa_group ALL=(root) /bin/cat /etc/*
Unfortunately…
$ sudo -l
User sweh may run the following commands on test1:
(root) /bin/cat /etc/*
$ cat /tmp/dir1/secret.txt
cat: /tmp/dir1/secret.txt: Permission denied
$ sudo cat /tmp/dir1/secret.txt
Sorry, user sweh is not allowed to execute '/bin/cat /tmp/dir1/secret.txt' as root on test1.spuddy.org.
$ sudo cat /etc/../tmp/dir1/secret.txt
hidden
$ sudo cat /etc/subgid /tmp/dir1/secret.txt
hidden
In this case I was able to make use of how filenames work to display the protected file, and just to use two filenames (one permitted) to match the pattern. Two problems with one configuration entry!
If you plan on using wildcards then determine if that will allow for excessive
privileges this may expose. In some cases this might be mitigated with the
!
configuration, which will deny commands.
Shell escape
Some commands allow for the user to drop into a subshell to perform
activity and then exit
back to the program. A common example of this
is the less
command.
$ sudo less test
myfile
!sh
sh-4.2# id
uid=0(root) gid=0(root) groups=0(root)
Now while the less
command itself was logged, all activity performed
inside the subshell isn’t properly tracked. The SA has full root
level permissions on the machine and we can’t audit their activity.
This may, sometimes, be mitigated with the NOEXEC
option in the
configuration:
$ sudo -l
User sweh may run the following commands on test1:
(root) NOEXEC: /bin/less
$ sudo less test
myfile
!sh
!done (press RETURN)
This isn’t infallible, however, and may block functionality the application needs. It may also not work on every operating system.
Sometimes the most unexpected of commands allows for a shell escape; the
one that surprised me was the Solaris format
command.
Open additional files.
This is related to shell escapes, but allows for files to be read or modified unexpectedly.
For example, we might want to allow for vi
to be run on a single file,
but the user can open additional files.
$ sudo -l
User sweh may run the following commands on test1:
(root) /bin/cat
(root) NOEXEC: /bin/vi /home/sweh/test
We’ve NOEXEC’d this, but the user can still do :r
inside the session
to read protected content. (This is hard to screen shot…)
$ sudo vi /home/sweh/test
myfile
:r /tmp/dir1/secret.txt
myfile
hidden
:w! /tmp/dir1/secret.txt
"/tmp/dir1/secret.txt" 2L, 14C written
:q!
$ sudo cat /tmp/dir1/secret.txt
myfile
hidden
sudoedit
Editing of files is a common requirement, so sudo
adds another option,
called sudoedit
. This can be invoked as sudoedit
or sudo -e
. The
way it works is clever; it makes a copy of the file you want to edit
in a temporary directory, then runs your editor as your real user, and
if changes have been detected copies the file back (with the correct
permissions, ownership, etc). In this way any shell escape or file
reads are done as the real user, and not privileged.
There are other limitations on sudoedit
(eg not in a writeable directory)
but it can handle a number of cases where BAU write-file access is
permitted.
Functional access (passwordless)
I’ve mostly been focusing on human level access (I’ve described SAs, but
this would also work for DBAs, AppOp, and similar, and the target user
need not be root
).
Sometimes there may be a need for a functional (or service) account to
need to perform privileged activity. Examples abound, but a simple one
might be a polling look that wants to see what servers have listening ports,
and what processes these are. Consider this job being
called from cron
every hour:
for host in test1 test2 test3
do
ssh svc_acct@$host sudo netstat -anp | grep -w LISTEN
done
We need root
here because of the -p
option to netstat
.
Obviously we can’t enter the password each time (indeed there may not be a
valid password, because we use key based authentication). Fortunately there’s
a NOPASSWD:
option
$ sudo -l
(root) NOPASSWD: /bin/netstat -anp
$ ssh test1 sudo netstat -anp | grep -w LISTEN
tcp 0 0 127.0.0.1:25 0.0.0.0:* LISTEN 859/master
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 759/sshd
tcp6 0 0 ::1:25 :::* LISTEN 859/master
tcp6 0 0 :::22 :::* LISTEN 759/sshd
These NOPASSWD:
entries can also be applied to human user accounts if
you consider the activity to be sufficiently low risk that the human
doesn’t need to re-authenticate (the df
example from earlier may
apply).
Block Lists don’t work – Example of a bad setup
I was working with a team some time back who wanted to perform centralised
data collection and some of the data required root
level permissions.
Now, naturally, I refused them ALL
access; having a tool with access to
every server and having unlimited root
on all those servers was an
unacceptable concentration of risk.
They went away and came back with the following request:
#List of commands svc_acct is not allowed to execute via sudo
Cmnd_Alias SVCNOEXEC=
adduser*,
chmod*,
chown*,
cp*,
...and so on
svc_acct ALL=NOPASSWD: ALL, !SVCNOEXEC
(In all there were 30 commands listed in the SVCNOEXEC alias).
Apart from the fact this wasn’t a valid sudoers
file, these block list
setups provide ZERO security in the environment. I was able to think
of three approaches within a few minutes:
First obvious way to bypass those restrictions:
Run the command under /bin/sh
, which wasn’t restricted:
sudo /bin/sh -c '<any_command_in_that_list>'
eg
sudo /bin/sh -c 'halt'
Second less obvious way to bypass these restrictions:
copy the file to a tmp directory and run that:
cp /sbin/halt /tmp/myprogram
sudo /tmp/myprogram
A third way
Create a wrapper and call that
echo halt > /tmp/myprogram
chmod 755 /tmp/myprogram
sudo /tmp/myprogram
I could probably come up with more esoteric variations as well (symlinks,
ld.so, perl, python… even use sed
to update the sudoers file,
itself!)
Basically, block lists like this can be bypassed with zero effort and should not be used.
Conclusion
The secure way of setting up a sudo profile is to default-deny and then only permit the minimum necessary commands.
These commands should be restricted with options to ensure they are
read-only and do not allow further privileges. So, for example, on
Solaris sudo format
may not be permitted because you can shell out
of it. Similarly hbacmd
may not be permitted without restriction
because it can be used to reset HBAs, change binding rules, etc. but
hbacmd ListHBAs
is OK.
Of course, understanding business requirements and not blocking efficiency also needs to feed into the decision of what commands you allow an SA to run BAU.
You may decide that sudo cat
is OK, trusting on the audit log to be a
sufficient activity trail (perhaps with reporting triggered from it if
a restricted file is accessed). That’s a definite productivity gain!
Also you may be able to use additional tools; e.g. SELinux
maybe able
to lock down access to secret.txt
, even to users running sudo cat
.
If you have any additional guidelines for sudo
entry creation, please
leave a message in the contents! I know I haven’t covered every
scenario (e.g. wrapper scripts) because some of those could be a post
in themselves!