« LinkStation | Main

Blocking SSH Dictionary Attacks

Posted by Nick L
Dec 24 2010

If you've ever bothered looking at your system logs, you may have noticed repeated (automated) attempts from hosts attempting to gain SSH access to your box, using seemingly random (predictable/common) usernames, and perhaps some default passwords.

Dec 20 06:24:10 router sshd[18755]: Failed password for invalid user patrick from 202.129.204.199 port 53485 ssh2
Dec 20 06:24:15 router sshd[18767]: Failed password for invalid user patrick from 202.129.204.199 port 53683 ssh2
Dec 20 06:24:45 router sshd[18795]: Failed password for invalid user rolo from 202.129.204.199 port 55169 ssh2
Dec 20 06:24:50 router sshd[18797]: Failed password for invalid user iceuser from 202.129.204.199 port 55357 ssh2
Dec 21 05:37:23 router sshd[13305]: Failed password for invalid user user from 203.172.22.84 port 56622 ssh2
Dec 21 05:37:28 router sshd[13307]: Failed password for invalid user admin from 203.172.22.84 port 56746 ssh2
Dec 21 05:37:43 router sshd[13313]: Failed password for invalid user soni from 203.172.22.84 port 57111 ssh2
Dec 21 05:37:55 router sshd[13319]: Failed password for invalid user soni from 203.172.22.84 port 57362 ssh2
Dec 21 05:38:15 router sshd[13347]: Failed password for invalid user test from 203.172.22.84 port 57875 ssh2
Dec 21 05:38:21 router sshd[13349]: Failed password for invalid user admin from 203.172.22.84 port 57993 ssh2
Dec 21 05:38:27 router sshd[13351]: Failed password for invalid user admin from 203.172.22.84 port 58133 ssh2
Dec 21 05:38:44 router sshd[13359]: Failed password for invalid user test from 203.172.22.84 port 58534 ssh2
Dec 21 05:38:49 router sshd[13361]: Failed password for invalid user testuser from 203.172.22.84 port 58670 ssh2
Dec 21 05:38:55 router sshd[13364]: Failed password for invalid user test123 from 203.172.22.84 port 58792 ssh2
Dec 21 05:39:01 router sshd[13366]: Failed password for invalid user toor from 203.172.22.84 port 58927 ssh2
Dec 21 05:39:21 router sshd[13392]: Failed password for invalid user user from 203.172.22.84 port 59440 ssh2
Dec 21 05:39:26 router sshd[13394]: Failed password for invalid user admin from 203.172.22.84 port 59556 ssh2
Dec 21 05:39:32 router sshd[13396]: Failed password for invalid user test from 203.172.22.84 port 59665 ssh2

In the past I have always ignored these since using strong passwords, limiting users with shell access, and not exposing usernames to the outside world should provide a adequate protection. As the numbers of users on my systems has increased, and I as an admin have chosen convenient (sometimes not very obscure usernames), and left it up to users to determine their passwords, I have become more concerned about this type of attack.

Common Approaches

The obvious way to block this kind of attack is to block access to the server from the host machine, via its IP address.

As far as I can tell, their are two main approaches to blocking this kind of attack:

  1. iptables recent module - See here 
  2. Retrospective syslog monitoring - See here

The iptables solution is pretty cool, its kernel level, so a very low overhead, however it cannot differentiate between genuine (successfull) connections and illicit attempts. Depending on your threshold, you can quite easily lock yourself out of your own server (by making a number of sequential SSH connections), or allowing more attempts than you are comfortable with to prevent the previous issue.

Monitoring syslog is a more accurate solution, from the perspective of detecting whether a connection was legitimate or not, however processing log files can be expensive on high load systems, and will either require additional daemons to be run, or be run out of cron, which may mean that the attack is finished by the time the IP is blocked.

An alternative approach - rsyslog

Newer distributions come with a new syslog daemon, rsyslog, which offers some very powerful features we can use:

  1. On-the-fly message filtering
  2. Shell-script execution

We can use these features to perform similar function to the retrospective syslog monitoring, however it can be done in real-time, and without such a hefty overhead of additional daemons, and parsing the whole log file on each invocation.

Configuration of rsyslog is a bit tricky, but since its configuration format supports conventional BSD syslog notation, and a couple of other rsyslog extension formats that have been developed over the years.

First thing to note is I already have a firewall blacklist feature on my gateway, so I already have a script to call to perform the blacklist. It just requires an IP address.

For the SSH blocking, I am going to wrap this script up in another shell script, that is able to handle incorrect passwords for valid users in a slightly more gracious way...it gives you 2 chances to get your password right, before the IP is blocked. Attempts to login as an invalid user cause instant blacklist of the IP.

My wrapper script is '/usr/local/bin/sshLoginFail'

It takes two parameters - the source IP of the attempted connection, and the username for the login attempt (if its a valid username for the system)

First thing to do is define a couple of rsyslog templates which can extract the required information for the script parameters out of the log message.

The syslog messages are formatted as follows, and we need to extract the information highlighted:

  Failed password for root from 203.172.22.84 port 51920 ssh2
  Invalid user oracle from 203.172.22.84

The %msg% token contains the message being processed. The first template is a regex that will extract the IP address from the 'Invalid user' message. The second uses field extraction to extract the username and the IP address.

#Invalid user - We only care about the IP address, - use a regex because there could be spaces in the username
#which would confuse us
$template SSHInvalidUser,"%msg:R,ERE,1,DFLT:from ([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})--end%"

#Valid user, wrong password, extract both, to pass to our script
$template SSHValidUserFailedPassword,"%msg:F,32:7% %msg:F,32:5%"

These template are used to create the command-line parameters with which to call our script, we now need to determine when were are processing a message that needs to trigger our script.

There are two mechanisms available in rsyslog, Property-based filters and Expression-based filters. Both will do the job, but with the Expression-based filters, you can explicitly select messages that have come from sshd.

The property-based filters are commented out, but left in for completeness

    
#Hit on messages that come out of SSHD - ignore the Failed password for invalid user message, - Invalid user is enough
#:msg,contains,"Invalid user " ^/usr/local/bin/sshLoginFail;SSHInvalidUser
#:msg,contains,"Failed password for invalid user " ~
#:msg,contains,"Failed password for " ^/usr/local/bin/sshLoginFail;SSHValidUserFailedPassword



#Same as above but using Expression-based filters, which should be more efficient and accurate, since they will match based on facility
#and application
if $syslogfacility-text == 'auth' and $app-name == 'sshd' and $msg contains 'Invalid user ' then ^/usr/local/bin/sshLoginFail;SSHInvalidUser
if $syslogfacility-text == 'auth' and $app-name == 'sshd' and $msg contains 'Failed password for ' and not ($msg contains 'Failed password for invalid user ') then ^/usr/local/bin/sshLoginFail;SSHValidUserFailedPassword

Categories: Linux

N900 Sent Items

Posted by Nick L
Jun 20 2010

Probably the only thing about the N900 that I have found really annoying is the lack of support for the 'Standard' IMAP Sent Items feature. You know the one, where your sent mail goes after you send it.

On the N900 the email application (modest), has a local sent items folder, and it is possible to manually move the items (in bulk) to the IMAP sent items folder, but what a pain.

Anyway, since I have now got fully working server-side filtering (Sieve) I decided it should be possible to solve this problem, server-side.

Since all my outgoing mail goes via Exim, on my own server, I can work out that its being sent from the N900 (I have a different 'from' address). So I can get exim to deliver a copy of the mail to myself. Then I can get Sieve to deliver the mail to my Sent Items.

First I need to add a system filter to exim:

I add the following line to /etc/exim4/conf.d/main/02_exim4-config_options

system_filter=/etc/exim4/system.filter

Exim will now evaluate the rules in /etc/exim4/system.filter before routing the mail.

Create a rule:

if $sender_address is "n900_from_address@wherever.com" then unseen deliver some_local_user@here.com endif

Then add a Sieve rule:

if envelope :is "from" "n900_from_address@wherever.com" { fileinto :flags ["\\Seen"] "INBOX.Sent"; stop; }

Categories: Debian, Linux

Exim + Dovecot + Sieve + Avelsieve

Posted by Nick L
Jun 19 2010

One of the problems (and best things) with using IMAP is that you really need to have you email rules and filters server side if you intend to use more than one client.

Sieve Avelsieve , and a Thunderbird plugin .

Since the LinkStation is already short of memory and CPU I've been thinking of re-vamping the e-mail handling for quite a while.

The current configuration uses exim & courier (IMAP and POP3). I have wanted to get rid of POP3 entirely, but the lack of server-side rules made that undesirable (I don't like the idea of client-side rules, very inefficient with large messages).

Exim and Courier are currently tied together, since SMTP user authentication is currently done through the courier-authdaemon (yet another unnecessary process).

The plan is as follows:

  1. Remove the tie between Exim and Courier, this will involve making Exim authenticate using PAM.
  2. Convert users mailboxes from Courier format to Dovecot (this is only required for users with mail in their Maildir.
  3. Install and Configure Dovecot IMAP, POP3 IMAPS, and POP3S
  4. Have exim perform its local delivery using the Dovecot Local Delivery Agent (LDA)
  5. Configure Sieve
  6. Install & Configure Avelsieve

Step 1.

PAM authentication for Exim.

It turns out that the exim-heavy Debian package is already built with PAM support, so we dont have too much to do.

Create a file /etc/pam.d/exim

#%PAM-1.0 @include common-auth @include common-account @include common-session

Add Debian-exim to the shadow group

adduser Debian-exim shadow

Create the auth configuration for Exim. I am using Exim split configuration, so the configuration should go in /etc/exim4/conf.d/auth . I removed the previous auth files leaving only 00_exim4-config_header . Create a file 30_exim4-config_pam_auth .

# /etc/exim4/conf.d/auth/30_exim4-config_pam_auth login: driver = plaintext public_name = LOGIN server_prompts = "Username:: : Password::" server_condition = "${if pam{$1:$2}{1}{0}}" server_set_id = $1 .ifndef AUTH_SERVER_ALLOW_NOTLS_PASSWORDS server_advertise_condition = ${if eq{$tls_cipher}{}{}{*}} .endif

The server is configured only to offer LOGIN authentication when the connection is encrypted, otherwise we'll have cleartext passwords flying all over the 'net.

Step 2.

Although Maildir is some kind of standard, it seems the IMAP and POP servers keep control files with the directory. If the Maildir contains mails (read or unread), it needs to be converted to work with Dovecot, otherwise the mail client will download all the mail again, and wont have a record of which mail is read and unread etc.

This process did not seem to work 100%, Thunderbird still insisted on downloading a bunch of mails, however for the most part everything was fine, and no mail was lost.

Other ideas for converting the Maildir are to copy all mails locally (to local folders in Thunderbird), recreate the Maildir, and then copy them back from Thunderbird, after Dovecot has been upgraded. The Dovecot Wiki has a number of pages on migration.

I used this script , following the instructions from here .

Step 3.

Dovecot configuration is pretty easy. The Debian packages come pre-configured with pretty much everything ready to go.

The basic Dovecot config (all comments removed) looks like this:

protocols = imap imaps pop3 pop3s

listen = * disable_plaintext_auth = no shutdown_clients = yes log_timestamp = "%Y-%m-%d %H:%M:%S "
ssl = yes ssl_cert_file = /etc/ssl/certs/CERT.crt ssl_key_file = /etc/ssl/private/PRIVATE.key
mail_location = maildir:~/Maildir
namespace private { prefix = INBOX. separator = . inbox = yes }
mail_privileged_group = mail
protocol imap { } protocol pop3 { pop3_uidl_format = %08Xu%08Xv }
protocol lda { postmaster_address = postmaster }
auth default { mechanisms = plain passdb pam { args = dovecot } userdb passwd { } user = root
}

NOTE:

It looks like we are allowing plaintext authentication....we are, however our firewall blocks the non-TLS ports from the outside world, so a plain-text password should only ever be passed on the LAN.

Step 4.

As it happens, everything should be up-and-running just fine now. Mail can be delivered, and collected, however the main thrust of this exercise is to get us into a state where we can run have Sieve rules running against received mails. The Dovecot Sieve implementation is a plugin to deliver , the Dovecot Local Delivery Agent. This means that we need to tell exim to use this program to perform local delivery.

Once again Debian make this particularly easy for us:

First create the config for exim:

#/etc/exim4/conf.d/transport/30_exim4-config_dovecot_delivery

dovecot_delivery: driver = pipe command = /usr/lib/dovecot/deliver message_prefix = message_suffix = log_output delivery_date_add envelope_to_add return_path_add #group = mail #mode = 0660 temp_errors = 64 : 69 : 70: 71 : 72 : 73 : 74 : 75 : 78

This defines the transport for the delivery, now we just tell exim to use this transport, edit /etc/exim4/update-exim4.conf.conf and change the dc_localdelivery setting:

dc_localdelivery='dovecot_delivery'

Be sure to restart exim after making these changes.

At this point end-to-end functionality should be tested. Everything should be working as before.

Step 5.

After completing this step we should have 2 pieces of new functionality.

  1. Dovecot deliver should evaluate Sieve scripts during mail delivery
  2. The Dovecot daemon should listen on port 4190 for Managesieve requests, which allows us to update Sieve scripts remotely

We need to make a couple of modifications to the Dovecot configuration, dovecot.conf .

Modify the protocols line

protocols = imap imaps pop3 pop3s managesieve

This enables the managesieve protocol in Dovecot, however by default Dovecot seems to listen on port 2000. This will most likely be changed in a future versions, since IANA have assigned the port now according to this .

Add the following section to the Dovecot configuration, the make it listen on 4190:

protocol managesieve { listen = localhost:4190 }

Since I am planning only to allow script editing via Avelsieve, I have chosen to only have this listen on localhost, Change 'localhost' to a '*' for all interfaces, but you may want to confirm that the connection is such that you are not passing passwords in cleartext across the net.

Now we need to configure the Dovecot LDA to run sieve scripts:

protocol lda { postmaster_address = postmaster mail_plugins = sieve }

And configure the delivery plugin itself:

plugin { sieve=~/.dovecot.sieve sieve_dir=~/sieve }

See the Dovecot Sieve page for all of the options.

You should be able to check its working using the sieve-connect util, but thats far too much like hard work, we'll use Avelsieve.

Step 6.

Avelsieve is a plugin for SquirrelMail. The last release was over a year ago now, so I dont know how active the development is, but it looks promising enough. There is a Debian package in unstable, which seems to have everything configured.

I had to make one other modification to my system, but that may just be because of the way I am configured...

The Avelsieve client makes use of the Scriptaculous JS library, which although was installed by the Debian package, I needed to make a symlink at the root of my web server to ensure these files were loadable via HTTP.

# cd <WEBROOT>

# ln -s /usr/share/javascript/

Job Done!

Categories: Debian, Linux

Cross compiling SpamAssassin Rules

Posted by Nick L
May 30 2010

sa-compile should speed up SpamAssassin, by pre-compiling rules into Native code.

Seem like you run sa-compile once, and the debian cron job takes care of re-running sa-compile whenever the rules are updated.

Trouble for me is that the load on the NAS of the compile took it down.

Since I have the cross compilers on my desktop PC, I figured it should be possible to build the native rules there, and distribute them to all the NAS drives.

Cross compile packages installed

acid:~# dpkg --get-selections | grep -e gnueabi -e armel

binutils-arm-linux-gnueabi install cpp-4.4-arm-linux-gnueabi install gcc-4.4-arm-linux-gnueabi install gcc-4.4-arm-linux-gnueabi-base install gcc-4.4-base-armel-cross install libc6-armel-cross install libc6-dev-armel-cross install libgcc1-armel-cross install libgomp1-armel-cross install

Compile the rules into some directory

export PERL_MM_OPT="CC=arm-linux-gnueabi-gcc LD=arm-linux-gnueabi-gcc AR=arm-linux-gnueabi-ar"

sa-compile --updatedir /root/sa-updates

I'll probably use a combination of scp and incrond to distribute the rules

Categories: Debian, LinkStation, Linux

SpamAssassin timeout issue

Posted by Nick L
May 30 2010

Just been suffering from an odd SpamAssassin timeout issue. Some mails were taking forever to scan. Timing out (and being allowed thru, as per config). Running them through

spamassassin -D < blah.msg

had them scanned in about 60 secs.

Running with

spamc -c < blah.msg

was timing out after 10 minutes. strace showed the spamc client was never receiving the response from spamd, and running spamd in debug mode I could see the scan finishing after about 1 minute

Anyway, turns out default in Debian is to run the scan over port TCP/IP 783, I found changing it to a UNIX domain socket fixed the issue.

/etc/default/spamassassin OPTIONS="--create-prefs --max-children 4 --helper-home-dir --socketpath=/var/run/spamd.sock"
/etc/exim4/sa-exim.conf SAspamcSockPath: /var/run/spamd.sock #SAspamcHost: 127.0.0.1 #SAspamcPort: 783

That scan now completes in under 60 secs.

Categories: Debian, Linux