By Kurt Seifried [email protected]
Introduction
Many security problems arise from the simple fact that you must allow user
access to your systems. In some cases users can be malicious (i.e. internal
attacker, university students, etc.) or they may accidentally expose their
password to an attacker or simply use a weak password (like their username). In
any event it is all to possible for users to attack a system, so it is advisable
to monitor users, and limit the amount of damage they can do.
Limiting users
Users require resources like CPU time, memory and drive space to do their
work. On many systems it is possible for users to hog resources, reducing the
usefulness of the system to other users or in some cases actually bringing the
server to a slow halt or crashing it. Users can also inadvertently use up more
resources then they mean to, limiting their resources can prevent several
classes of problems. Programs can crash. generating huge core dumps, or go crazy
and user up all the available memory. Something to remember: global limits apply
to root, so be careful! If you do not allow root to run enough processes for
example cron jobs may fail or you may not even be able to log in to fix any
problems.
PAM
Almost all Linux distributions ship with PAM support making it universally
available. PAM limits provide a single standardized interface to setting user
limits, instead of having to write complex shell configuration files (such as
/etc/profile) you simply edit the "limits.conf" file. As well applying limits
selectively through the command shell is very difficult, whereas with PAM
applying limits globally, on groups or on individual users is quite simple.
Documentation is available on PAM usually in the "/usr/share/doc/" tree. To
enable PAM limits you need to add a line such as:
session required /lib/security/pam_limits.so
to the appropriate Pam configuration file (i.e. /etc/pam.d/sshd). You can
then define limits, typically these are in "/etc/security/limits.conf" or a
similar location. Because most of these limits are enforced by the shell the
system cannot log all violations of limits (i.e. you will be notified in syslog
when a user exceeds the number of times they are allowed to login, however you
will not receive a warning if the user tries to use more disk space then they
are allowed to).
The available limits are:
core -- Limits the core file size (KB); usually set to 0
for most users to prevent core dumps.
data -- Maximum data size
(KB).
fsize -- Maximum file size (KB).
memlock -- Maximum
locked-in-memory address space (KB).
nofile -- Maximum number of
open files.
rss -- Maximum resident set size (KB).
stack
-- Maximum stack size (KB).
cpu -- Maximum CPU time
(MIN).
nproc -- Maximum number of processes.
as -- Address
space limit.
maxlogins -- Maximum number of logins for this user or
group.
priority -- The priority to run user process with.
For example you can limit the amount of memory that user "bob" is allowed to
use:
user hard memlock 4096
This would place a hard (absolute) limit of 4 megabytes on memory usage for
"bob". Limits can be placed on users by listing the user name, groups by using
the syntax "@group" or globally by using "*".
core files can be created when a program crashes. They have been used in
security exploits, overwriting system files, or by containing sensitive
information (such as passwords). You can easily disable core dumps using PAM,
and generally speaking most users will not notice, however if you have software
developers they may complain.
* hard core 0
fsize is generally a good idea to set, many users will have a large
filesystem quota (i.e. tens of megabytes to several hundred or several
gigabytes), however if they are allowed to create a single file that is
abnormally large they can easily hog disk I/O resources (i.e. create a large
file and copy/delete the copy repeatedly). Setting this limit globally can also
prevent an attacker from trying to fill up the partitions your log files are
stored on, for example if you only have a single / partition an attacker can
easily fill it up by generating a lot of log events.
@notroot hard data 102400
Of course limiting CPU time is one of the classic administrative tasks, this
is very useful for preventing run-away processes from eating up all the cpu
time, and it ensures that if a user leaves something running in background (such
as a packet sniffer) it will eventually be killed. Limiting CPU time will have
several side effects however, once of which will be limiting the amount of time
a user can spend logged in (eventually they will run out of CPU time and the
session will be killed), this can lead to problems if users spend long periods
logged in. As well depending on the CPU(s) present in your machine the limits
can vary greatly (one minute on a 386 is quite a bit different then one minute
on a 1.3 GHz Athlon).
@students hard cpu 2
Limiting the number of times a user can login is strongly advised, for most
situations users should not need to log in to a server more then once, and
allowing them to do so let's them use more resources then you might intend. As
well it can be used to detect suspicious activity, if users know they can only
login once then attempts to log in multiple times can be viewed as suspicious
activity (i.e. an attacker with a stolen password trying to access the account).
@users hard maxlogins 1
Additionally when someone violated this limit it will be logged in syslog:
Apr 15 15:09:26 stench PAM_unix[9993]: (sshd) session opened for user test by (uid=0)
Apr 15 15:09:32 stench pam_limits[10015]: Too many logins (max 1) for test
Soft limit violations will not be logged (i.e. a soft limit of 1 and a hard
limit of 2).
Bash
Bash has built in limits, accessed via “ulimit”. Any hard limits cannot be
set higher, so if you have limits defined in /etc/profile, or in the users
.bash_profile (assuming they cannot edit/delete .bash_profile) you can enforce
limits on users with Bash shells. This is useful for older Linux distributions
that lack PAM support (however this is increasingly rare and PAM should be used
if possible). You must also ensure that the user cannot change their login
shell, if they use "chsh" to change their shell to ksh for example the next time
they login they will have no limits (assuming you cave not put limits on ksh).
Documentation is available on ulimit, log in using bash and issue:
[root@server /root]# <strong>help ulimit</strong>
ulimit: ulimit [-SHacdflmnpstuv] [limit]
Ulimit provides control over the resources available to processes
started by the shell, on systems that allow such control. If an
option is given, it is interpreted as follows:
-S use the `soft' resource limit
-H use the `hard' resource limit
-a all current limits are reported
-c the maximum size of core files created
-d the maximum size of a process's data segment
-f the maximum size of files created by the shell
-l the maximum size a process may lock into memory
-m the maximum resident set size
-n the maximum number of open file descriptors
-p the pipe buffer size
-s the maximum stack size
-t the maximum amount of cpu time in seconds
-u the maximum number of user processes
-v the size of virtual memory
If LIMIT is given, it is the new value of the specified resource.
Otherwise, the current value of the specified resource is printed.
If no option is given, then -f is assumed. Values are in 1024-byte
increments, except for -t, which is in seconds, -p, which is in
increments of 512 bytes, and -u, which is an unscaled number of
processes.
To disallow core files (by setting the maximum size to 0) for example you
would add:
ulimit -Hc 0
To set limits globally you would need to edit "/etc/profile", of course this
will also affect root, so be careful! To set limits on groups you would need to
add scripting to "/etc/profile" that would check for the user's membership in a
group and then apply the statements, however doing this with PAM is recommended
as it is a lot simpler.
Quota
Quota allows you to enforce user and group limits on disk usage. Quotas are
most often used to prevents users from hogging disk space that is shared with
other users (i.e. "/home"). There are several benefits to using quota instead of
PAM/shell limitations; the first being that you can apply it selectively to disk
systems, i.e. you can place a limit on /home/ but not on /tmp/. Additionally you
can place limits on the number of inodes used as well as space, running out of
inodes is as bad as running out of space since you won't be able to create new
files. To see how much space a disk has (in kilobytes):
[root@server /]# df -k
Filesystem 1k-blocks Used Available Use% Mounted on
/dev/hda2 2925900 948936 1828336 35% /
/dev/hda1 49743 2485 44690 6% /boot
And to show inode usage:
[root@dildo /]# df -i
Filesystem Inodes IUsed IFree IUse% Mounted on
/dev/hda2 371680 58737 312943 16% /
/dev/hda1 12880 27 12853 1% /boot
Quota support must be compiled into the kernel (this is the default with most
vendors), and the tools must be installed, usually in a package called "quota".
Once you have done this you need to edit your "/etc/fstab" file and add either
the keyword "grpquota" or "usrquota" in the options:
LABEL=/ / ext2 defaults 1 1
LABEL=/home /home ext2 defaults,grpquota 1 2
Then for each filesystem that you have enabled quotas on you must create two
files in the root of the filesystem:
[root@dildo /]# <strong> touch /home/quota.user </strong>
[root@dildo /]# <strong> touch /home/quota.group </strong>
These files should only be accessible to root. You will then need to reboot
the system.
Typically you will only need to place quota on filesystems directly writeable
to the user, usually this is:
/tmp
/var/tmp
/home
To edit user and group quotas you use the program "edquota", for example:
[root@dildo /]# <strong>edquota -u </strong><em><strong>username</strong></em>
This will then let you edit the appropriate quota.user or quota.group file
(these files should not be edited directly!). The editor uses vi by default and
the data looks like:
Quotas for user username:
/dev/hda1: blocks in use: 255, limits (soft = 5120, hard = 10240)
inodes in use: 123, limits (soft = 1000, hard = 1500)
Soft limits will generate warnings and hard limits will stop the user from
using more disk space or inodes. Documentation on this can be found in "man
quota" or in the Quota mini-howto available at: https://www.linux.com/howto/mini/Quota.html.
Monitoring users
Monitoring users may be a requirement of your security policy, or you may
have discovered an account that has been compromised and you wish to monitor the
attacker when they use it. Unfortunately while this used to be relatively easy
there are now a number of new issues that make monitoring users problematic. The
increased usage of encrypted protocols like SSH and SSL mean you can no longer
simply run a packet sniffer and monitor the attacker. The change in Linux's
tty's to pseudo terminals has "broken" many older software packages as well.
Typically the best way to monitor users is transparently, packet sniffers are
ideal for this as you need not make any changes on the server at all. The second
best is to use some form of monitoring on the server, this used to be possible
with ttysnoop however ttysnoop does not appear to be actively maintained
anymore. While you can use tricks like shell logging this will be obvious to a
skilled attacker as the first thing they usually do is disable shell logging. If
you are monitoring legitimate users you should inform them (this is a legal
requirement in some countries and states). If you are monitoring an attacker and
intend to use the log files as evidence you should probably consult a lawyer to
make sure it is done correctly.
sshsniff
This is actually a really useful tool I stumbled across. It allows you to
monitor all traffic going in and coming out of a process, such as an interactive
shell. The bad news is that it does not appear to be completely reliable. While
testing it sshsniff reliably captured keystrokes sent to the shell and the
replies, however when files were cat'ed, i.e. "cat /etc/passwd" the file was not
always displayed on the monitoring shell:
[test@server test]$ ???\033???[???Acat /etc/passwd???
--- SIGCHLD (Child exited) ---
[test@server test]$ ???\033???[???Acat /etc/passwd???
--- SIGCHLD (Child exited) ---
[test@server test]$ ???\033???[???Acat /etc/passwd???
[pid 1417]
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:
daemon:x:2:2:daemon:/sbin:
adm:x:3:4:adm:/var/adm:
lp:x:4:7:lp:/var/spool/lpd:
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/spool/mail:
operator:x:11:0:operator:/root:
games:x:12:100:games:/usr/games:
ftp:x:14:50:FTP User:/home/ftp:/bin/true
nobody:x:99:99:Nobody:/:
rpc:x:32:32:Portmapper RPC user:/:/bin/false
postfix:x:89:89::/var/spool/postfix:/bin/true
test:x:500:500::/home/test:/bin/bash
+++ exited (status 0) +++
[pid 1380]
--- SIGCHLD (Child exited) ---
sshsniff is not perfect but it is able to bind to any PID and not just
terminals, making it very flexible. You can download it from: https://www.psychoid.lam3rz.de/exploits.html. One note there is
a file called col.c that contains an entry for /etc/passwd, it does not appear
to be used but it does raise some questions. If you plan to use this code I
strongly suggest you audit it.
dsniff
With protocols like SSH and SSL being widely deployed simple packet sniffers
are no longer as useful as they once were. So people started writing more
complex packet sniffers, like dsniff. dsniff is capable of doing man in the
middle attacks against SSL and SSH. If you control the server then you can
easily proxy connections (as you have access to the secret keys on the server)
and thus decrypt the attackers SSH session and monitor what is going on. dsniff
is not capable of dealing with all version of the SSH protocol however, so
forcing only protocol 1 support on the server can solve this, in your
sshd_config file:
Protocol 1
is specified and not:
Protocol 2,1
dsniff is available from: https://www.monkey.org/~dugsong/dsniff/
Firewalling
One new capability in Linux 2.4 that should prove quite useful is the ability
to filter some types of outgoing packet based on who owns the process that
created them. Of course this is not 100% effective as some types of packets are
not owned ny anyone specifically (i.e. ICMP responses). Using the various flags:
--uid-owner
--gid-owner
--pid-owner
--sid-owner
which are discussed in more detail in the firewall section, documentation is
available via "man iptables".