Search This Blog

Tuesday, 6 September 2016

Linux Ransomware and SSH

I recently came across this article on the FAIRWARE ransomware attacking Linux servers by brute forcing SSH according to the referenced article here. I thought why in this day-and-age is brute forcing SSH from the Internet working? Surely we are not exposing SSH administrative interfaces to the big bad Internet, let alone in an insecure way (rhetorical)?

So, whilst by no means a complete How-To, I thought I would go through a few areas that I hope may improve the security of systems running SSH. Though, as we all know, what you actually do depends on your risk appetite among other things.

In summary this is really about defence in depth. Not only make good security decisions, but also don't rely on a few. My view is if there is a shortcoming, no matter how minor, deal with it (assess it, then either fix it or accept the risk (with possible mitigations)).

For SSH, in no particular order.

1. Don't bind to all interfaces and certainly not your Internet facing one

In today's world of virtualisation, there really should be no excuse for not re-configuring a management service such as SSH to only listen on a management interface. Such a management network must not be directly accessible via the Internet; you should need to go via a VPN, Citrix or other strong security gate as a minimum to access such a network.

If your management services are only listening on your management interface, then even if your firewalls, or other Internet facing protections are breached, it is still going to be a bit of a challenge for an adversary to get shell access via that service.

This can be achieved using the Listen directive in sshd_config.

2. Never, ever, allow direct privileged logons

Being able to directly SSH (login) as root or a privileged user is just asking for trouble. Would you build a safe so that the door was accessible via an outside wall that the public walk past? I hope not; so should we really be doing the same thing with our servers holding our sensitive information assets?

The easiest way to prevent this is to only permit normal users (typically having a common group). i.e. we whitelist accounts rather than blacklist, so if a new account is created it needs to be assessed first.

PermitRootLogin should always be set to no, but we also need to take account of other 'unprivileged' users that may be applications (thus have access to or “own” potentially sensitive information) and users with enhanced 'root' rights (such as having capabilities in Linux, or via sudo).

3. Multi-factor authentication

It is always a good idea to examine a technology to fully understand what it can do. So, in this case, even if you don't have true multi-factor authentication, you would have spotted the RequiredAuthentications or AuthenticationMethods directive in SSH, which states what methods must succeed to allow access.

So the following on CentOS 7 requires both a publickey and a password to successfully log on (or more precisely the use of the associated private key on the client where the public key is configured on the user's account on the server).

AuthenticationMethods publickey,password

So, even if the public key does not have a paraphrase, we have the following, showing the public key authentication is not sufficient; you also need the account password.

$ ssh -x localhost
Authenticated with partial success.
auser@localhost's password:
Last login: Tue Sep  6 10:59:49 2016

I hope you would agree, even brute forcing that “poor-man's 2FA” is a bit more challenging.

4. Bar tunnelling over SSH

A tunnel allows you to tunnel any connection in any direction, via the SSH connection. Indeed the man page describes how to set up a VPN as well! This allows trivial bypass of firewalls and other security devices, by using the trusted encrypted SSH connection.

Unless you have very specific requirements, which are documented and limited by the configuration such as PermitOpen or the use of a directive in a cert, disable SSH tunnelling via PermitTunnel no. Don't forget the tap devices, if supported.

Even if you have a captive client, the user can press ~C to open an SSH “command line” to dynamically add a tunnel. So, if it isn't disabled at the server side a user can still add it in a captive setup. See the man page on ssh.

5. Require strong MACs and Ciphers

If both the client and server supports it, shouldn't we actively prevent the use of weak MACs and Ciphers?

The default configuration on CentOS 7 allows MD5 and SHA1 as MACs. It also allows sha2-256, sha2-512. Perhaps we should specify only the strongest subset and thus put a mandatory bar on MD5 and SHA1, for example.

6. Use only SSH version 2

Fortunately most modern configurations only enable version 2 (Protocol 2). Protocol version 1 has known design flaws, so should not be used. See an example from CERT here.

7. Client side

Don't forget the client side configuration as well.

If a client also only allows strong MACs and Ciphers, for example, and you suddenly can no longer access a host due to cipher support, does this suggest a problem? Certainly worth investigation.


This is just a small sample of the options open to you. There are many other configuration options for example, including Match statements for finer-grain configuration, adding options in the authorized_keys file, client IP lockdown, and SELinux tweaks, to name a few.

Oh, and monitoring. An unmonitored log is a useless log.

What would be your top recommendations?

Friday, 2 September 2016

A look at an SELinux error message

As a fan of the SELinux security framework that runs as part of Linux, I thought it would be a good idea to improve my skillset in this area, and go beyond the basics.

Part of that is research, subscribing to SELinux mailing lists, playing with test setups and, of course, reading what others are saying via Google searches.

One, whilst going off at a tangent to my goal, sparked my interest as all the posts I saw didn't have the answer. It was this type of error:

libsemanage.validate_handler: MLS range s0-s1:c1,c3 for Unix user XXX exceeds allowed range s0:c1,c3-s1:c1,c3 for SELinux user XXX
libsemanage.validate_handler: seuser mapping [XXX -> (XXX, s0-s1:c1,c3)] is invalid
libsemanage.dbase_llist_iterate: could not iterate over records

For example here, just search for libsemanage.validate_handler.

The answer should be straightforward, and relies on the fact that SELinux enforces mandatory access controls; aka stop you doing stupid or bad things (or at least what policy states are stupid/bad things), plus the tools stop stupid/bad configurations (again, at least what the developers state are such).

We can look at three types of user concept here. First, we have the traditional UNIX user as defined in /etc/passwd or your favourite password backend. Secondly, we have the concept of an SELinux user, which is more akin to a class or UNIX group. Finally, we have mappings which map the UNIX user to the SELinux user, which themselves (can) specify ranges.

SELinux users are managed by semanage user, whilst the mappings are managed by semanage login.

If we map a UNIX user to a SELinux user, we are stating that the UNIX user has a particular range of clearances to access and do stuff on the machine. However, when we set up the mapping we can further constrain the user, but cannot (shouldn't be able to) grant more than the base SELinux user they are mapped to.

So, here's an example on one of my test boxes.

The test box is running the mls policy and is using poly-instantiated directories of /tmp /var/tmp and $HOME based on the context (i.e. we can only see a home area or temp area commensurate to our current level.

First, lets create an SELinux user of devuser_u which has the standard user role of user_r:

[root@centos-7-2-t1 ~]# semanage user -a -R user_r -r s0-s1:c1,c3 devuser_u
[root@centos-7-2-t1 ~]# semanage user -l

                Labelling  MLS/       MLS/                         
SELinux User    Prefix     MCS Level  MCS Range            SELinux Roles

devuser_u       user       s0         s0-s1:c1,c3          user_r

Now  let's create a user that is of that type:

[root@centos-7-2-t1 ~]# useradd -m -Z devuser_u dev1
[root@centos-7-2-t1 ~]# semanage login -l

Login Name           SELinux User         MLS/MCS Range        Service

__default__          user_u               s0                   *
dev1                 devuser_u            s0-s1:c1,c3          *

Now lets say that we wish for the user to be able to access a new category (compartment) of c8. We may state that we do this with the mapping, but as this user is an SELinux user of devuser_u this isn't going to work:

[root@centos-7-2-t1 ~]# semanage login -m -r s0-s1:c1,c3,c8 dev1
libsemanage.validate_handler: MLS range s0-s1:c1,c3,c8 for Unix user dev1 exceeds allowed range s0-s1:c1,c3 for SELinux user devuser_u
libsemanage.validate_handler: seuser mapping [dev1 -> (devuser_u, s0-s1:c1,c3,c8)] is invalid
libsemanage.dbase_llist_iterate: could not iterate over records
ValueError: Could not commit semanage transaction

Instead we either need to create a new SELinux user (and map the unix user to it) or modify the existing one. So, lets modify the existing one so that it includes this new category:

[root@centos-7-2-t1 ~]# semanage user -m -r s0-s1:c1,c3,c8 devuser_u
[root@centos-7-2-t1 ~]# semanage user -l

                Labelling  MLS/       MLS/                          
SELinux User    Prefix     MCS Level  MCS Range            SELinux Roles

devuser_u       user       s0         s0-s1:c1,c3,c8       user_r

Cool, that worked. Note that this does not update the individual mappings.

[root@centos-7-2-t1 ~]# semanage login -l

Login Name           SELinux User         MLS/MCS Range        Service

__default__          user_u               s0                   *
dev1                 devuser_u            s0-s1:c1,c3          *

So, lets do that.

[root@centos-7-2-t1 ~]# semanage login -m -r s0-s1:c1,c3,c8 dev1
[root@centos-7-2-t1 ~]# semanage login -l

Login Name           SELinux User         MLS/MCS Range        Service

__default__          user_u               s0                   *
dev1                 devuser_u            s0-s1:c1,c3,c8       *

So now all is well.

Providing the MLS/MCS range of the SELinux user is a superset of the range an individual user has, then you shouldn't have a problem.

A side note, I have found it is occasionally possible to sometimes modify the SELinux user to no longer be a superset; but then all other modifications against that user breaks until you fix it (i.e. make it a superset, then clear the categories or sensitivities you wish to remove from the member UNIX user mappings, then update the SELinux user); a bug perhaps.