*nix
16 October 2011

Активная защита FreeBSD на основе логов, sh и cron

From Sandbox
Приветствую всех администраторов FreeBSD!

Настроив свой второй сервер на FreeBSD и перенеся туда важную корпоративную информацию, я задумался о защите. Не буду повторяться про антивирусы, брандмауэры и дополнительные полезные комплексы — ни один из этих инструментов не решал мою задачу.

Задача возникла сама собой, при просмотре логов:
/var/log/exim/rejectlog
/var/log/auth.log
/var/log/apache22/httpd-error.log


в них постоянно попадала информация о неудачных попытках подобрать пароль к exim, к серверу и к веб-почте соответственно. Рано или поздно злоумышленники могут пароль подобрать, поэтому их нужно как-то остановить, например, добавив их IP-адрес в правила ipfw. А на веб-сервере еще и пытались найти несуществующие каталоги и файлы, относящиеся к администрированию, типа phpmyadmin, очевидно, чтобы проверить их на существующие уязвимости.

Google и статья lissyara о некоторых аспектах безопасности подсказали направление.

Решение в итоге выглядит просто: cron раз в минуту запускает sh-скрипт, который сканирует указанные логи и добавляет незадачливых нарушителей в правила ipfw в виде:
deny ip from 123.123.123.123 to me

Cкрипты от lissyara мне удалось доработать напильником:
  • в правила не добавляются пользователи доверенных подсетей
  • в правила не добавляются одни и те же IP
  • скрипт может обрабатывать один и тот-же лог файл без его копирования (основываясь на дате событий)
  • скрипт пишет свои логи
  • скрипт исправляет некоторые ошибки в логах


Для каждого лог-файла я сделал соответственно свой sh-скрипт:
mailsec.sh
nixsec.sh
websec.sh

т.к. у них немного разный подход к решению задачи, но общая концепция аналогична.

Скрипты стабильно отрабатывают свою электроэнергию, делая практически невозможным брутфорс и защищая от излишне любопытных. По накопленной статистике, за минуту у самых быстрых получалось сделать не более 100 попыток подбора. До ввода в эксплуатацию скриптов, попытки были круглосуточными и бесчисленными.

Думаю, что подобные скрипты должны стоять на каждом сервере, т.к. защиты никогда не бывает много.
Надеюсь, мои наработки помогут Вам в этом. Привожу код. Готов ответить не вопросы.



mailsec.sh

#!/bin/sh

# Настройки
trusted_net='192.168.'
debug_log="/var/log/mailsec.log"
cur_log_file="rejectlog"
cur_log_dir="/var/log/exim/"

#echo "Hi!" > /dev/ttyv0
# если два часа четыре минуты удаляем предыдущие правила
if [ `date +%H` -eq 02 ] && [ `date +%M` -eq 04 ]
    then {
    # пишем статистику использования накопленных правил
    echo "-= RULES STATS =-" >> ${debug_log}
    /sbin/ipfw show 3 >> ${debug_log}
    /sbin/ipfw delete 3 >/dev/null 2>&1
    echo "-= RESET RULES =-" >> ${debug_log}
    date >> ${debug_log}
    }
fi

# блокируем тех кто пробует логиниться на несуществующих пользователей
cat ${cur_log_dir}${cur_log_file} | grep "auth_login authenticator failed for" | sed 's/ (/--/' | sed 's/failed for--/failed for (/' | sort | awk '{print $1,$8}' | sed 's/\[/ /' | sed 's/\]:/  /' | uniq -c | sort |
{
 while read count_IP
 do
    count_deny=`echo ${count_IP} | awk '{print $1}'`
    logday=`echo ${count_IP} | awk '{print $2}' | cut -d - -f 3`
    logmonth=`echo ${count_IP} | awk '{print $2}' | cut -d - -f 2`
    logyear=`echo ${count_IP} | awk '{print $2}' | cut -d - -f 1`
    IP=`echo ${count_IP} | awk '{print $3}'`
    day=`date +%d`
    mon=`date +%m`
    yea=`date +%Y`
    ippresent=`/sbin/ipfw show | grep ${IP} >/dev/null 2>&1`
    if [ ${day} -eq ${logday} ] && [ ${mon} -eq ${logmonth} ] && [ ${yea} -eq ${logyear} ] && [ ! ${ippresent} ]
    then {
        if [ ${count_deny} -ge 6 ] && echo $IP | grep ${trusted_net} >/dev/null 2>&1
        then echo "MAILSEC: (bruteforce) IP address  = ${IP}        attempts count = ${count_deny}. Правило не применяется (доверенный IP)" >> ${debug_log}
        else {
           if [ ${count_deny} -ge 6 ]
               then {
               echo "MAILSEC: (bruteforce) IP address  = ${IP}        attempts count = ${count_deny}" >> ${debug_log}
               /sbin/ipfw add 3 deny ip from ${IP} to me >/dev/null 2>&1
               }
           fi
        }
        fi
    }
    fi
 done
}




nixsec.sh

#!/bin/sh

#echo "Hi!" > /dev/ttyv0
# в два часа ночи удаляем предыдущие правила
if [ `date +%H` -eq 02 ] && [ `date +%M` -eq 00 ]
then {
     /sbin/ipfw delete 1 >/dev/null 2>&1
     echo "-= RESET RULES =-" >> /var/log/nixsec.log
     date >> /var/log/nixsec.log
}
fi


#
day=`date +%d`
month=`date +%m`
year=`date +%Y`
log_dir="/var/old_log/${year}/${month}"
# создаём папку для логов
mkdir -p ${log_dir}
log_file="${log_dir}/${day}_auth.log"

# переносим логи
cat /var/log/auth.log > /tmp/auth.log
cat /dev/null > /var/log/auth.log
cat /tmp/auth.log >> ${log_file}



# Вначале отлавливаем IP с которых пытаются залогинится
# под несуществующими пользователями
cat /tmp/auth.log | grep "Invalid user" | awk '{print $10}' | sort | uniq -c  | sort |
{
     while read count_IP
     do
     count_deny=`echo ${count_IP} | awk '{print $1}'`
     IP=`echo ${count_IP} | awk '{print $2}'`
     if [ ${count_deny} -ge 2 ]
     then {
          echo "NIXSEC: (invalid user) IP address  = ${IP}        deny count = ${count_deny}" >> /var/log/nixsec.log
          /sbin/ipfw add 1 deny ip from ${IP} to me #>/dev/null 2>&1
     }
fi
done
}



# Вылавливаем пытающихся ломать сервант
cat /tmp/auth.log | grep "Did not receive identification string" | awk '{print $12}' | sort | uniq -c  | sort |
{
 while read count_IP
do
count_deny=`echo ${count_IP} | awk '{print $1}'`
IP=`echo ${count_IP} | awk '{print $2}'`
if [ ${count_deny} -ge 2 ]
then {
echo "NIXSEC: (not ident) IP address  = ${IP}        deny count = ${count_deny}" >> /var/log/nixsec.log
/sbin/ipfw add 1 deny ip from ${IP} to me >/dev/null 2>&1
}
fi
done
}


# Вылавливаем пытающихся ломать сервант c несуществующих адресов
cat /tmp/auth.log | grep "but this does not map back to the address - POSSIBLE BREAK-IN ATTEMPT" | awk '{print $7}' | sort | uniq -c | sort |
{
while read count_IP
do
count_deny=`echo ${count_IP} | awk '{print $1}'`
IP=`echo ${count_IP} | awk '{print $2}'`
if [ ${count_deny} -ge 2 ]
then {
echo "NIXSEC: (break) IP address  = ${IP}        deny count = ${count_deny}" >> /var/log/nixsec.log
/sbin/ipfw add 1 deny ip from ${IP} to me >/dev/null 2>&1
}
fi
done
}


# Вылавливаем тех, кто лезет в root
cat /tmp/auth.log | grep "User root from" | awk '{print $9}' | sort | uniq -c  | sort |
{
while read count_IP
do
count_deny=`echo ${count_IP} | awk '{print $1}'`
IP=`echo ${count_IP} | awk '{print $2}'`
if [ ${count_deny} -ge 2 ]
then {
echo "NIXSEC: (root attempt) IP address  = ${IP}        deny count = ${count_deny}" >> /var/log/nixsec.log
/sbin/ipfw add 1 deny ip from ${IP} to me >/dev/null 2>&1
}
fi
done
}




websec.sh

#!/bin/sh

# Настройки
trusted_nets='192.168. 127.0. 10.0.'
debug_log="/var/log/websec.log"
cur_log_file="httpd-error.log"
cur_log_dir="/var/log/apache22"
old_log_dir="/var/log/apache22/old"


#echo "Hi!" > /dev/ttyv0
# в два часа две минуты удаляем предыдущие правила
if [ `date +%H` -eq 02 ] && [ `date +%M` -eq 02 ]
    then {
    echo "-= RULES STATS =-" >> ${debug_log}
    /sbin/ipfw show 2 >> ${debug_log}
    /sbin/ipfw delete 2 >/dev/null 2>&1
    echo "-= RESET RULES =-" >> ${debug_log}
    date >> ${debug_log}
    }
fi

# создаем папку для архивных логов
day=`date +%d`
month=`date +%m`
year=`date +%Y`
log_dir="${old_log_dir}/${year}/${month}"
# создаЈм папку для логов
mkdir -p ${log_dir}
log_file="${log_dir}/${day}_${cur_log_file}"

# копируем логи
cat ${cur_log_dir}/${cur_log_file} > /tmp/${cur_log_file}
# очищаем исходный логфайл
cat /dev/null > ${cur_log_dir}/${cur_log_file}
# обновляем архивный логфайл
cat /tmp/${cur_log_file} >> ${log_file}



# блокируем тех кто ищет несуществующие файлы
cat /tmp/${cur_log_file} | grep "File does not exist" | sed 'y/]/ /' | awk '{print $8}' | sort | uniq -c | sort |
{
 while read count_IP
 do
    count_deny=`echo ${count_IP} | awk '{print $1}'`
    IP=`echo ${count_IP} | awk '{print $2}'`

    if [ ${count_deny} -ge 5 ] && echo $IP | grep 192.168. >/dev/null 2>&1
    then echo "WEBSEC: (files) IP address  = ${IP}        attempts count = ${count_deny}. Правило не применяется (доверенный IP)" >> ${debug_log}
    else {

        if [ ${count_deny} -ge 5 ]
            then {
            echo "WEBSEC: (files) IP address  = ${IP}        attempts count = ${count_deny}" >> ${debug_log}
            /sbin/ipfw add 2 deny ip from ${IP} to me >/dev/null 2>&1
            }
        fi
    }
    fi
 done
}



# Вылавливаем спецов по составлению неправильных URI
cat /tmp/${cur_log_file} | grep "Invalid URI in request" | sed 'y/]/ /' | awk '{print $8}' | sort | uniq -c | sort |
{
 while read count_IP
 do
    count_deny=`echo ${count_IP} | awk '{print $1}'`
    IP=`echo ${count_IP} | awk '{print $2}'`
    if [ ${count_deny} -ge 2 ]
        then {
        echo "WEBSEC: (URI) IP address  = ${IP}        attempts count = ${count_deny}" >> ${debug_log}
        /sbin/ipfw add 2 deny ip from ${IP} to me >/dev/null 2>&1
        }
    fi
 done
}



# Вылавливаем спецов по нарушению RFC
cat /tmp/${cur_log_file} | grep "request without hostname" | sed 'y/]/ /' | awk '{print $8}' | sort | uniq -c | sort |
{
 while read count_IP
 do
    count_deny=`echo ${count_IP} | awk '{print $1}'`
    IP=`echo ${count_IP} | awk '{print $2}'`
    if [ ${count_deny} -ge 2 ]
        then {
        echo "WEBSEC: (RFC) IP address  = ${IP}        attempts count = ${count_deny}" >> ${debug_log}
        /sbin/ipfw add 2 deny ip from ${IP} to me >/dev/null 2>&1
        }
    fi
 done
}


Возможно специалисты по sh-скриптам смогут помочь оптимизировать код.

+29
8.6k 120
Comments 54