Sunday 24 October 2010

DOS, DDOS, BOTNET -- IT ARENT A PROBLEM

The follow are aim at hackers that aren't very smart (average hacker), the smarter hacker normally don't play around with you.

A friend of mine own a money making on line forum, and a group of hackers want to blackmail him for benefits. At first they start with a single ip to DOS the server. The server went down and my friend ask me for help.

I install a simple iptable to block attack from a single source and swap apache for light httpd. The server stay up for another few days until they start using DDOS and BOTNET.

When they start using botnet I made the program below, since the introduction of the program the server never crash again. At the peak of the attack there was 8000 ips and 26000 connection and the cpu was using 1-2%.


The basic of DDOS is any one of the following.
Resource Starvation: CPU 100%, RAM 100% or BandWidth 100%.

What to do when your under attack?
  1. Go in and turn off your web service.
    You can't do anything if your server hang :D.
  2. Run the following command, it change the priority of the process being attack, so your server wont hang.
    echo "
    www-data hard priority 10" >>
    /etc/security/limits.conf
    For more detail vist here 
    http://kissconcept.blogspot.com/2010/09/set-default-priority-for-all-processes.html
  3. Install the little script below, name Anti-Dos :D.


What do anti-dos do?
It take access data from your web service, analysis the data and determine the bad guys.
It use iptables to block people, pretty sweet. The best part is that it unbad the bad guys if they stop attacking after x duration.

What web service can it run with?
  • lighttpd
  • for nginx and apache it require a bit of change.

Iptables:
The following script create 100 bucket in iptables, and each bucket can hold 100 ips.
If some one can extend the amount of ips in the bucket give me call :D

Put the code in a php file
<?php 
 
`iptables -F`; 
 
for($index=0; $index <100; $index++){ 
    `iptables -A INPUT -p tcp --dport 80 -m recent --update --seconds 1800 --name ROBOT$index -j DROP`;
}
 
`chmod 777 /proc/net/ipt_recent/*` 
 


Configure Lighttpd:
  •  Find a place to put the script and logs,
    • antidos : /home/www-data/antidos.php
    • log: /home/www-data/log/
    • error: home/www-data/error
    • Secret:^Z --> single letter
  • Edit your antidos.php
    define ( 'LOGGING', '/home/www-data/log/');
  • Edit /etc/lighttpd/lighttpd.conf
    • accesslog.filename = "| /usr/bin/php /home/www-data/antidos.php 2> /home/www-data/error"
    • accesslog.format = "%h^Z%t^Z%H^Z%m^Z%U^Z%X^Z%b^Z%{Referer}i^Z%{User-Agent}i"
      in vim ^Z is for 26 character code, you can type it by using ctrl+z in vim.
That's it :D, restart your lighttpd and wait for 2-5 min.
Your cpu usage should drop to 0%.

If it don't check for activity in the log folder and the error file.
you can use tail -f /home/www-data/log/access to follow the log

<?php

define ( 'SEPARATOR', 26 );
define ( 'ACCESSED_EXPIRE', 600);
define ( 'ACCESSED_POINT', 600); // Point to start banning people
define ( 'IPTABLES_NAME', 'ROBOT');
define ( 'IPTABLES_BUCKET', 100); // Including 0
define ( 'IPTABLES_PATH', '/proc/net/ipt_recent/');
define ( 'LOGGING', '/home/www-data/log/');

class AntiDos {
    /**
     * Contain, time and point for the ip
     */
    private $_logPoint          = array();
    private $_logTime           = array();
    private $_logIpNumber       = array();

    private $_firstLogPoint     = null;
    private $_firstLogTime      = null;
    private $_firstLogIpNumber  = null;

    private $_banned = array();

    /**
     * Contain the point use to ban people
     */
    private $_point = array();

    /**
     * Time Of last Accessed
     */
    private $_lastAccessed = array();

    private $_extension = array();
    private $_extensionPattern;
    private $_time  = null;
    private $_count = 0;
    private $_ban_path = 'ban';
    private $_points_path = 'points';
    private $_access_path = 'access';

    public function __construct(){
        $this->_extension = array(
            'Image' => "(?(JPG|GIF|BMP|JPEG|PNG))?",
            'Css' => "(?CSS)?",
            'Javascript' => "(?JS)?",
            'Other' => "(?.)?",
        );

        $this->_extensionPattern = implode('',$this->_extension);
        $this->_lastAccessed = array(
            'Image'=>array(),
            'Css'=>array(),
            'Javascript'=>array(),
            'Other'=>array(),
        );
        $this->_ban_path = LOGGING.'ban';
        $this->_points_path = LOGGING.'points';
        $this->_access_path = LOGGING.'access';
    }

    /**
     * Convert the string time from lighttpd to Unix time.
     * @param string $time
     */
    private function _strtotime($time){
        $time = explode('[',$time);
        $time = explode(' ',$time[1]);
        list($date,$hour,$minute,$second) = explode(':',$time[0]);
        list($day,$month,$year) = explode('/',$date);
        return strtotime("$day $month $year $hour:$minute:$second");
    }

    private function _nextLog(){
        if(!isset($this->_firstLogTime)){
            if(!empty($this->_logTime)){
                $this->_firstLogTime = array_shift($this->_logTime);
                $this->_firstLogPoint = array_shift($this->_logPoint);
                $this->_firstLogIpNumber = array_shift($this->_logIpNumber);
            } else {
                return false;
            }
        }
        return $this->_firstLogTime < $this->_time;
    }

    private function _cleanUp(){
        while($this->_nextLog()){
            $this->_point[$this->_firstLogIpNumber] -= $this->_firstLogPoint;

            if($this->_point[$this->_firstLogIpNumber] <= 0){
                unset($this->_point[$this->_firstLogIpNumber]);
                unset($this->_lastAccessed['Image'][$this->_firstLogIpNumber]);
                unset($this->_lastAccessed['Css'][$this->_firstLogIpNumber]);
                unset($this->_lastAccessed['Javascript'][$this->_firstLogIpNumber]);
                unset($this->_lastAccessed['Other'][$this->_firstLogIpNumber]);
            }

            unset($this->_firstLogTime);
            unset($this->_firstLogPoint);
            unset($this->_firstLogIpNumber);
        }
    }

    private function _getHumanFactor($ipNumber){
        $human = 0.0000000001;
        $robot = 1;

        if($this->_lastAccessed['Image'][$ipNumber] > $this->_time){
            $human += 0.4;
        } else {
            $robot *= 2;
        }

        if($this->_lastAccessed['Css'][$ipNumber] > $this->_time){
            $human += 0.4;
        } else {
            $robot *= 2;
        }

        if($this->_lastAccessed['Javascript'][$ipNumber] > $this->_time){
            $human += 0.4;
        } else {
            $robot *= 2;
        }

        if($this->_lastAccessed['Other'][$ipNumber] > $this->_time){
            $human += 0.2;
        } else {
            $robot *= 2;
        }

        return $human / $robot;
    }

    private function _checkAgent($agent){
        if(stristr($agent,'.NET CLR')){
            $agent = 4;
        } else {
            $agent = 1;
        }
        return $agent;
    }

    private function _stringToIpNumber($string){
        return ip2long(array_pop(explode(':',$string)));
    }

    private function _checkReferred($referred){
        if($referred != '-'){
            $referred = 0.5;
        } else {
            $referred = 2;
        }
        return $referred;
    }

    private function _parse($log){
        if(!$log){
            return;
        }
        $this->_cleanUp();

        list ( $ipAddress, $time, $http, $method, $url, $status, $byte, $referred, $agent ) = (explode ( chr ( SEPARATOR ), $log ));
        // accesslog.format = "%h %t %H %m %U %X %b %{Referer}i\" \"%{User-Agent}i\""
        // accesslog.format = "%h^Z%t^Z%H^Z%m^Z%U^Z%X^Z%b^Z%{Referer}i\"^Z\"%{User-Agent}i\""
        // ^Z is ctrl+z in vim
        $this->_time = $time = $this->_strtotime($time);
        $ipNumber = $this->_stringToIpNumber($ipAddress);

        if(!array_key_exists($ipNumber, $this->_point)){
            $this->_point[$ipNumber] = 0;
            $this->_lastAccessed['Image'][$ipNumber] = 0;
            $this->_lastAccessed['Css'][$ipNumber] = 0;
            $this->_lastAccessed['Javascript'][$ipNumber] = 0;
            $this->_lastAccessed['Other'][$ipNumber] = 0;
        }

        preg_match ( "/{$this->_extensionPattern}$/i", $url, $extensionMatch );
        foreach($extensionMatch as $key => $value){
            if($value && !is_numeric($key)){
                $this->_lastAccessed[$key][$ipNumber] = $time + ACCESSED_EXPIRE;
            }
        }


        $referred = $this->_checkReferred($referred);
        $agent = $this->_checkAgent($agent);
        $human = $this->_getHumanFactor($ipNumber);


        $point = 1 / $human * $referred * $agent;
        $this->_logTime[] = $time + ACCESSED_POINT;
        $this->_logPoint[] = $point;
        $this->_logIpNumber[] = $ipNumber;

        $this->_point[$ipNumber] += $point;
        $this->ban($ipNumber);
    }

    public function ban($ipNumber){
        $ipAddress = long2ip($ipNumber);
        if($this->_point[$ipNumber] > ACCESSED_POINT){
            $this->_count++;
            $index = $this->_count % IPTABLES_BUCKET;
            $iptables = IPTABLES_PATH.IPTABLES_NAME.$index;
            `echo '+$ipAddress' > $iptables 2> /dev/null`;
            `echo '$ipAddress {$this->_point[$ipNumber]}' >> {$this->_ban_path}`;
        }
        `echo '$ipAddress {$this->_point[$ipNumber]}' >> {$this->_points_path}`;
    }

    public function run(){
        do{
            $log = trim(fgets(STDIN));
            `echo '{$log}' >> {$this->_access_path}`;
            $this->_parse($log);
        } while($log);
    }
}
// Check for memory leaks
$first = memory_get_usage();
//echo "Memory :" . memory_get_usage() . PHP_EOL;
$antiDos = new AntiDos();
//echo "Memory :" . (memory_get_usage() - $first) . PHP_EOL;
$antiDos->run();
//echo "Memory :" . (memory_get_usage() - $first) . PHP_EOL;



No comments:

Post a Comment