Код отслеживания Google Analytics.

Feb 28, 2010

protection from brute force on ssh

When I've tired from brute force attacks on my computer at 22 port I create simple protection.



From the beginning I've written script which collect all data about failed attempts of penetration:
#!/bin/bash
function getDate() {
    echo "$@" | grep -o "^[[:alpha:]]\+[[:space:]]\+[[:digit:]]\+[[:space:]]\+[[:digit:]]\+:[[:digit:]]\+:[[:digit:]]\+"
}

invalidUsers=$(
    for filename in $(ls -t1 /var/log/auth.log*); do
        if [[ "${filename}" =~ gz$ ]]; then
            gzip -dc "${filename}"
        else
            cat "${filename}"
        fi
    done | grep "Failed password for invalid user")
ipList=$(echo "${invalidUsers}" | grep -o "\([[:digit:]]*\.\)\{3\}[[:digit:]]*" | sort --unique)
for ipAddress in $(echo "${invalidUsers}" | grep -o "\([[:digit:]]*\.\)\{3\}[[:digit:]]*" | sort -u); do
    currentViolation=$(echo "${invalidUsers}" | grep "${ipAddress}")
    currentNumber=$(echo "${currentViolation}" | wc -l)
    if [[ "${currentNumber}" -eq 1 ]]; then
        whenDate="at "$(getDate "${currentViolation}")
    else
        fromDate=$(getDate $(echo "${currentViolation}" | head -n 1))
        toDate=$(getDate $(echo "${currentViolation}" | tail -n 1))
        whenDate="from ${fromDate} to ${toDate}"
    fi
    userNames=$(echo "${currentViolation}" | sed "s|^.*user \([^ ]*\) from.*$|\1|g" | sort -u | paste -s -d ,)
    echo "${ipAddress}: ${currentNumber} time(s) ${whenDate}. User names: ${userNames}"
done | sort --key=2 --general-numeric-sort --reverse
after it I learned about TCP Wrappers and use it for limit number of attempts. I want to press your attention on the way how can be determined if your services compiled with supporting of TCP Wrappers:
$ strings -f /usr/sbin/* | grep hosts_access
/usr/sbin/in.tftpd: hosts_access
/usr/sbin/sshd: hosts_access
/usr/sbin/stunnel: hosts_access
/usr/sbin/stunnel: See hosts_access(5) manual for details
/usr/sbin/tcpd: hosts_access_verbose
/usr/sbin/vsftpd: hosts_access
/usr/sbin/xinetd: hosts_access
Well the main idea is to check every attempt to use ssh and block it if IP is included in black list. This list should be appended every time by data from auth.log analyse and include all addresses which have 5 or more failed attempts. Some notes should be listed here to be unforgotten:
  • we have to use both hosts.allow and hosts.deny. Information about aclexec can be found in hosts_options(5)
  • hosts.allow:
    sshd : ALL : aclexec <some checking script> %a
    
    hosts.deny:
    sshd : ALL
  • <some checking script> have to be executable - I forget to change it :)
  • <some checking script> have to explicit define exit status. The return status is the exit status of the last command executed and in my following script I forget last "exit" command - as result it was always return false status
#!/bin/bash

function getTempFilename(){
    prefix="$1"
    #generate new name
    tempFile="${tmpPlace}/${prefix}${RANDOM}"
    while [ -f "${tempFile}" ] ; do
        tempFile="${tmpPlace}/${prefix}${RANDOM}"
    done
    echo "${tempFile}"
}
    
declare -r NUMBER_TRIES=5
BLOCK_FILE_NAME="/var/blackList"
tmpPlace=/tmp

grep Failed /var/log/auth.log /var/log/auth.log.0 2>/dev/null | grep -o "\([[:digit:]]*\.\)\{3\}[[:digit:]]*" | sort | uniq -c | while read count ipAddress ; do
    if [[ "${count}" -gt ${NUMBER_TRIES} ]] ; then
        echo "${ipAddress}" >> "${BLOCK_FILE_NAME}"
    fi
done

tmpFileName=$(getTempFilename balckList)
sort -g "${BLOCK_FILE_NAME}" | uniq > "${tmpFileName}"
mv "${tmpFileName}" "${BLOCK_FILE_NAME}"

grep "$1" "${BLOCK_FILE_NAME}" && exit 1
exit 0

2 comments:

alex said...

Good work Beggy! But it seems to me that zcat "${filename}" would be shorter for gzip -dc "${filename}" though both acts the same...

Beggy said...

Sure - you are right. I just forget it as "one more command", but it standard command so you are right - it is better to use zcat instead gzip. Thank you :)