ScriptVsftpdExcluder

A script to ban VSFTPD brute-force attackers

I have noticed recently a LOT of people (usually coming from China) trying to connect to one of my vsftpd server with logins and password that almost made sense.

For instance, if I have VeryImportantClient as someone who has access to FTP on the server, I would find entries such as this one in /var/log/vsftpd.log:

Sun Aug 26 12:42:52 2012 [pid 1] [VeryImportantClient] FAIL LOGIN: Client "61.160.211.4"
Sun Aug 26 12:43:07 2012 [pid 1] [VeryImportant] FAIL LOGIN: Client "61.160.211.4"
Sun Aug 26 12:43:23 2012 [pid 1] [Client] FAIL LOGIN: Client "61.160.211.4"
Sun Aug 26 12:43:38 2012 [pid 1] [VeryImportantClientVeryImportantClient] FAIL LOGIN: Client "61.160.211.4"

... Etc... The messages shown above were sometimes repeated hundreds of times! And no, the IP address has not been modified to protect the guilty... ;-)

I got tired of adding IP addresses manually, and the following script is the result. I hope it helps someone out there...

WARNING USE THIS SCRIPT AT YOUR OWN RISKS!!

This script may be updated from time to time, make sure you come here to get the latest version...

#!/bin/bash

# ##############################################################
# #
# # vsftp_exclude : black-lists brute-force VSFTP script kitties 
# # ;-)
# #
# ##############################################################
# #
# # HISTORY:
# #
# # v0.0 Mon Aug 27 10:19:18 CEST 2012 Gil ANDRE
# # v1.0 Mon Aug 27 11:01:07 CEST 2012 Gil ANDRE
# # v1.1 Mon Aug 27 12:09:54 CEST 2012 Gil ANDRE
# # v1.2 Wed Aug 29 10:22:11 CEST 2012 Gil ANDRE
# # v1.3 Wed Aug 29 11:47:29 CEST 2012 Gil ANDRE
# # v1.4 Thu Aug 30 19:12:16 CEST 2012 Ruben ALVES
# # v1.5 Fri Aug 31 09:47:11 CEST 2012 Gil ANDRE
# # v1.6 Wed Nov 14 15:35:54 CET 2012  Gil ANDRE
# # v1.7 Wed Nov 14 16:29:40 CET 2012  Gil ANDRE
# #
# ##############################################################

# today's date
today=$(date +%F-%H%M)

# log file name
log_file=/tmp/vsftpd_excluder_$(hostname | awk -F\. '{print $1}')_${today}.LOG

# remove previous log files
rm /tmp/vsftpd_excluder*.LOG

# create log file
touch ${log_file}

# log starting time
echo "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%">> ${log_file}
echo "${0} check for $(hostname | awk -F\. '{print $1}') started on $(date)">> ${log_file}

# get the list of offending IP addresses (with FAIL LOGIN):
# for a more aggressive version use /var/log/vsftpd.log* to
# check ALL VSFTPD logs instead of just the latest one.
cracker=$(grep -i fail.login /var/log/vsftpd.log | awk -F\" '{print $2}' | sort | uniq)

if [[ ! -z ${cracker} ]]; then
	# found something
	echo "Someone has been trying to get into the VSFTPD server!" >> ${log_file}
	echo "Here is the list:">> ${log_file}
	echo ${cracker}>> ${log_file}
elif [[ -z ${cracker} ]] ; then
	# ooops - nothing, let's stop right here.
	echo "Nothing detected. Stopping." >> ${log_file}
	exit 0
fi

# loops through the list of addresses:

for ip_addr in ${cracker}; do
	# check if address already black listed (should not happen):
	echo "Checking ${ip_addr}...">> ${log_file}
	present=$(/sbin/iptables -L -n | grep -i -c ${ip_addr})
	if [[ $(echo ${ip_addr} | awk -F\. '{print $1}') -eq "10" ]]; then
		present="255"
	fi
	case ${present} in
		"0")
			# not present - add ip address in iptables
			# see script: quick_iptables_add.sh for more details.
			echo "Adding ${ip_addr} to local iptables...">> ${log_file}
			/sbin/iptables -I INPUT -s ${ip_addr} -j DROP -m comment --comment "auto-added by ${0} on $(date)"
			oktest=$?
			case ${oktest} in
				"0")
					echo "Address ${ip_addr} has been succesfully added to iptables">> ${log_file}
					echo "Saving iptables configuration now...">> ${log_file}
					/sbin/service iptables save
					echo "Restarting iptables...">> ${log_file}
					/etc/init.d/iptables restart
					echo "Listing new iptables rules...">> ${log_file}
					/sbin/iptables -L -n | grep -i ${ip_addr}>> ${log_file}
					modified=1
					;;
				*)
					echo "%%% ERROR: IP address ${ip_addr} not saved">> ${log_file}
					echo "%%% ERROR: Something is wrong with ${0}! Please fix it!">> ${log_file}
					exit 1
					;;
			esac

			;;	
		*)
			# uh oh... address already exist - stop here
			echo "%%% WARNING: ${ip_addr} is on an admin network or already black-listed in iptables:">> ${log_file}
			/sbin/iptables -L -n | grep -i ${ip_addr}>> ${log_file}
			echo "Iptables not modified.">> ${log_file}
			;;
	esac
done

# log ending time
echo "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%">> ${log_file}
echo "${0} check ended on $(date)">> ${log_file}

# mail the log file to the usual suspects
case ${modified} in
	"1")
		# something has been changed in iptables: send report.
		cat ${log_file} | mailx -s "VSFTPD Excluder report" sysadmin@your-site.com
		;;
	*)
		# nothing has been changed: log file only.
		echo "%%% NO CHANGES">>${log_file}
		;;
esac
exit 0

# end of script.

Stick this into the crontab of root and you should be in business!

Please note that, in version 1.7, all networks starting by "10." are now automatically excluded from the exclusion (does that sentence makes any sense??)... ;-)

Improvements and debugging, suggestions and gripes can be sent to my email address.

See Also: