cancel
Showing results for 
Search instead for 
Did you mean: 

Detecting false login attempts and blocking the attackers

You may be familiar with the security concept of a 'honeypot' - a sandboxed, sacrificial computer system that sits safely away from the primary systems.  Any attempts to access that computer are a strong indicator that an attacker is at work, probing for weak points in a network.

 

A recent Slashdot article raised an interesting idea... 'honeywords' are fake accounts in a password database that don't correspond to real users.  Any attempts to log in with one of these accounts is a strong indicator that the password database has been stolen.

 

In a similar vein, attempts to log in with common, predictable admin accounts are a strong indicator that an attacker is scanning your system and looking for weaknesses.  This article describes how you can detect these attacks with ease, and then considers different methods you could use to block the attacker.

 

Detecting Attack Attempts

 

Screen Shot 2013-05-12 at 16.39.29.pngAttackers look for common account names and passwords (see [1], [2] and [3])

 

Stingray Traffic Manager is in an ideal position to detect attack attempts.  It can inspect the username and password in each login attempt, and flag an alert if a user appears to be scanning for familiar usernames.

 

Step 1: Determine how the login process functions

 

Credentials are usually presented to the server as HTTP form parameters, typically in an HTTP POST to an SSL-protected endpoint:

Screen Shot 2013-05-13 at 09.29.08.png

Screen Shot 2013-05-13 at 13.09.15.png

Web Inspection tools such as the Chrome Developer tools (illustrated above)

help you understand how the authentication credentials are presented to the login service.

 

You can use the TrafficScript function http.getFormParam() to look up the submitted HTTP form parameters - this function extracts parameters from both the query string (GET and POST requests) and HTTP request body (POST requests), handles any unusual body transfer encoding, and %-decodes the values:

 

$userid = http.getFormParam( "Email" ); 
$pass   = http.getFormParam( "Password" ); 

 

 

Step 2: Does this constitute an attack?

 

You'll need to make a judgement as to what constitutes an attack attempt against your service.  A single attempt to log-in with 'admin:admin' is probably sufficient to block a user, but multiple attempts in a short period of time certainly indicate a concerted attack.

 

An easy way to count user/password combinations is to use a rate shaping class to count events.  Stingray's rate classes are usually used to implement queues (delaying requests that exceed the per-second or per-minute queue), but you can also use the rate.use.noQueue() function to determine if an event has exceeded the rate limit or not, without queuing it.

 

Let's construct a policy that detects if a particular source IP address is trying to log in to one of our false 'admin' accounts too frequently:

 

$path = http.getPath(); 
if( $path != "/cgi-bin/login.cgi" ) break; 
$ip = request.getRemoteIP(); 
$user = http.getFormParam( "user" ); 
if( string.regexmatch( 
$user,     "^(admin|root|phpadmin|test|guest|user|administrator|mysql|adm|oracle)$" ) ) {   
if( rate.use.noQueue( "5 per minute", $ip ) == 0 ) {      
# User has exceeded the limits      
....   
} 
} 

 

An aside: If you would like to maintain a large list of honeyword names (making sure that none of them correspond to real accounts), then you may find it easier to store them in an external table using libTable.rts: Interrogating tables of data in TrafficScript.

 

 

 

Responding to Attack Attempts

 

If you determine that a particular IP address is generating attack attempts and you want to block it, there are a number of ways that you can do so.  They vary in complexity, accuracy and the ability to 'time out' the period that an IP address is blocked out for:

 

Method Sophistication
Store data locally in the global data segment Straightforward to code, timeouts possible, not cluster-aware
Store data in the resource directory Straightforward to code, timeouts possible, is cluster-aware
Update configuration in service protection policy Straightforward to code, difficult to avoid race conditions, not possible to timeout the configuration, is cluster aware
Provision iptables rules from an event Complex to code accurately but very effective, not possible to timeout, is cluster aware

 

Updating the configuration in a service protection policy could be achieved by calling the REST API from TrafficScript - perform a GET on the configuration (/api/tm/1.0/config/active/protection/name), update the banned array, and PUT the configuration back again.  However, there is no natural way to remove (timeout) a block on an IP address after a period of inactivity.

 

Provisioning iptables rules would be possible with a specific event handler that responded to the TrafficScript function event.emit( "block", $ip ), but once again, there's no easy way to time a block rule out.

 

Storing data locally in the resource directory is a good approach, and is described in detail in the article Slowing down busy users - driving the REST API from TrafficScript.  The basic premise is that you can use the REST API to 'touch' a file (named after an IP address) in the resource directory, and you block a user if their IP address corresponds to a file in the resource directory that is not too old.  However, if the user does not return, you will build up a large number of files in the resource directory that should be manually pruned.

 

Storing data in the global data segment (How is memory managed in TrafficScript?) is perhaps the best solution.  The following code sample illustrates the basic premise:

 

 

$prefix = "blocked-ip-address:";

# Record that an IP address is blocked data.set( $prefix.$ip, 1 ); 

# Check if an IP address is blocked
if( data.get( $prefix.$ip ) ) {
connection.discard();#sthash.YB8cEYo7.dpuf
}

# Delete all records data.reset( $prefix );

 

You could implement timeouts in a simple fashion, for example, by calling data.reset() on the first transaction after the top of every hour:

 

$hour = sys.time.hour();

$last = data.get( $prefix."hour" );

if( $last != $hour ) {

data.reset( $prefix );

data.set( $prefix."hour", $hour );

 

An aside: There is a very slight risk of a race condition here (if two cores run the rule simultaneously) but the effects are not significant.

 

This approach gives a simple and effective solution to the problem of detecting logins to fake admin accounts, and then blocking the IP address for up to an hour.

 

What if I want to block IP addresses for longer?

 

One weakness of the approach above is that if an IP address is added to the block table at 59 minutes past the hour, it will be removed a minute later.  This may not be a serious fault; if the user is continuing to try to force admin accounts, the rule will detect this and block the IP address shortly after.

 

An alternative solution is to store two tables - one for odd-numbered hours, and one for even-numbered hours:

 

  • When you add an IP address, place it in the odd or even table according to the current hour
  • When you test for the presence of an IP address, check both tables
  • When the hour rolls over and you switch to the even-numbered table (for example), delete all of the entries (using data.reset) before proceeding - they will be between one and two hours old

 

$prefix = "blocked-ip-address:";

# Check if an IP address is blocked

if( data.get( $prefix."0:".$ip ) || data.get( $prefix."1:".$ip ) ) {

connection.discard();

}

# Add an IP address (this is an infrequent operation we hope!)

$hour = sys.time.hour();

$pp = ( $hour % 2 ) . ":"; # pp is either 0: or 1:

$last = data.get( $prefix.$pp."hour" );

if( $last != $hour ) {

data.reset( $prefix.$pp );

data.set( $prefix.$pp."hour", $hour );

}

data.set( $prefix.$pp.$ip, 1 );

 

This extension to the rule could further be extended to any number of tables, and to any time interval, though this is almost certainly overkill for this solution.

 

Read More

 

Version history
Revision #:
1 of 1
Last update:
‎05-09-2013 10:20:AM
Updated by:
 
Labels (1)