Protection from sshd brute force :: an experiment


Introduction
I was worried with the Sep 30 17:32:39 devone sshd[6601]: Failed password for marc from 192.168.0.140 port 1044 ssh2 messages in the /var/log/secure for a very long time and searched far and long, and did a lot of googling and all in vain. Till recently when my box got rooted, and was demolished to the core all folders [etc, dev, lib, bin, sbin, usr, var/log] got deleted, and the system was prompting in a kernel panic with out init!

Solution
The simple solution which I devised needed some kind of scripting and I choosed php ( like always ), and some tweaking in the manner of syslogd, and creating a fifo using the mkfifo, achived a kind of protection.

Proceedure

mkfifo -m 600 /var/run/seclog

Add the following line to the /etc/syslog.conf
authpriv.* |/var/run/seclog
restart the syslogd

Create (or copy paste) a php file and change the mod to +x, making required modifications where ever needed to match your system configurations
#!/usr/bin/php
<?

/* override the php default max execution time limit */
ini_set("max_execution_time","0");

/* limit for password failues */
$limitFail = 4;

/* pipe file to read from */
$pipeFifo = "/var/run/seclog";

/* blocked hosts stay blocked for this many seconds */
$stayBlockedFor = 600; // in seconds

/* declare array to hold failed passwords for a user */
$passFails = array(); //

/* assumes logs of this kind

Sep 30 17:32:39 devone sshd[6601]: Failed password for jijutm from 192.168.0.140 port 1044 ssh2
Sep 30 17:33:02 devone sshd[6601]: Accepted password for jijutm from 192.168.0.140 port 1044 ssh2
Sep 30 17:33:51 devone sshd[6678]: Illegal user meena from 192.168.0.140

*/


/* process functions */

function process($line){

    
/* do further processing of the log if you want here by passing the $line */    

    
list($server, $secure) = explode(': ',$line);
    
$splitup = explode(' ', $secure);

    switch(
$splitup[0]){
        case
'Illegal':
            
dropRoute($splitup[4]);
            break;
        case
'Didnot':
            
dropRoute($splitup[5]);
            break;
        case
'Failed':
            if(!
checkUser($splitup[3])) dropRoute($splitup[5]);
            break;
        case
'Accepted':
            
resetPassFails($splitup[3]);
            break;
    }
}


/* check the failed password count */
function checkUser($user){
    global
$passFails, $limitFail;
    if(
$passFails[$user] > $limitFail){
         unset(
$passFails[$user]);
         return
false;
        }
    
$passFails[$user]++;
    return
true;
}

/* drop the route to this host */
function dropRoute($hostIP){
    global
$config, $blocked, $stayBlockedFor;
    
/*
        Drop route to $hostIP!
        use atd to release block after $stayBlockedFor seconds
    */
}

/* reset the failed password count for an accepted user */
function resetPassFails($user){
    global
$passFails;
    unset(
$passFails[$user]);
}


$kf = fopen($pipeFifo,"r");
while(!
feof($kf)){
    
$line = trim(fgets($kf));
    
process($line);
    }
fclose ($kf);

in place of ;
    
/*
        Drop route to $hostIP!
        use atd to release block after $stayBlockedFor seconds
    */
write your own drop and release methods.

The php file can be started as a service from the local services typically rc.local

Jiju Thomas Mathew,
siltech @ RAC

Back to Index on Notes
© 2003 Saturn InfoLab http://www.saturn.in