Compare commits

..

No commits in common. "419ae9e1c9701a23070f80412cf02ee2b7de226c" and "5cdb469c6e6367d7c42c144ed25f46dead69bd46" have entirely different histories.

3 changed files with 83 additions and 7 deletions

Binary file not shown.

View File

@ -1,5 +1,5 @@
# Updates
- 7th July 2025: Added a count for the number of table entries - pf sets a maximum and it would be handy to know if I'm bumping along that number. Starting removing the SSH Brute force code - SSHGuard is in OpenBSD packages and already blocks SSH bruteforcers but can be configured for other services.
- 7th July 2025: Added a count for the number of table entries - pf sets a maximum and it would be handy to know if I'm bumping along that number.
- 1st July 2025: Renamed "HAIL_MARY" to "CLOUD_BRUTEFORCE_MITIGATION". Read [Catechism of the Catholic Church](https://www.vatican.va/archive/ENG0015/_INDEX.HTM) before complaining.
- 19th June 2025: Updated the _AGENT to the latest Windows Firefox as some sites were rejecting download attempts based on an outdated user agent.
@ -40,6 +40,8 @@ permit root
permit nopass _pfbadhost cmd /sbin/pfctl args -nf /etc/pf.conf
permit nopass _pfbadhost cmd /sbin/pfctl args -t pfbadhost -T replace -f /etc/pf-badhost.txt
permit nopass _pfbadhost cmd /sbin/pfctl args -t pfbadhost -T show | wc -l
# Optional rule for authlog scanning
permit nopass _pfbadhost cmd /usr/bin/zcat args -f /var/log/authlog /var/log/authlog.0.gz
...
```

View File

@ -87,6 +87,17 @@ _BOGON_4=0
# Enable IPv6 Bogon Filter (Blocks unassigned/reserved/martian addresses)
_BOGON_6=0
###################################################################
# Cloud Bruteforcer Mitigation (SSH authlog analysis)
# Searches SSH authlog for bruteforcers
#
# Set to '1' to enable
_CLOUD_BRUTEFORCE_MITIGATION=0
#
# Set failed log-in limit for bans
_LOGIN_LIMIT=25
###################################################################
###################################################################
# Country GeoIP Blacklist
# Enter any ISO-3166 Country Codes you want to block (1 per line)
@ -170,7 +181,7 @@ https://sslbl.abuse.ch/blacklist/sslipblacklist.txt
### SSH attackers
# https://lists.blocklist.de/lists/22.txt
# https://lists.blocklist.de/lists/ssh.txt:
# https://lists.blocklist.de/lists/ssh.txt
# https://lists.blocklist.de/lists/bruteforcelogin.txt
### FTP attackers
@ -423,6 +434,31 @@ mysort() {
fi
}
# ------------------------------------------------------------------------------
# Authlog Analysis Functions
# ------------------------------------------------------------------------------
# CLOUD_BRUTEFORCE_MITIGATION preproccessor
AUTHLOG_PROC() {
myawk -- '{if ($6 !~ "Disconnected|Accepted" && $7 !~ "disconnect") printf("%s\n%s\n%s\n%s\n%s\n%s\n", $9, $10, $11, $12, $13, $14)}'
}
CLOUD_BRUTEFORCE_MITIGATION_MITIGATE() {
# Check OSTYPE
if [ "${_OS_TYPE}" != 'macos' ]; then
# IPv4 Authlog List Gen
if [ "${_IPV4}" -eq 1 ]; then
"${getroot}" -- "${authlog_unzip}" -f "${authlog_path1}" "${authlog_path2}" | AUTHLOG_PROC | PARSE_V4 | WHITELIST_FILTER | myawk -- '{ a[$0]++ }END{ for(i in a) print a[i],i }' | myawk -v LOGIN_LIMIT="${_LOGIN_LIMIT}" -- '$1>LOGIN_LIMIT {print $2}' | mysort -uV
fi
# IPv6 Authlog List Gen
if [ "${_IPV6}" -eq 1 ]; then
"${getroot}" -- "${authlog_unzip}" -f "${authlog_path1}" "${authlog_path2}" | AUTHLOG_PROC | PARSE_V6 | WHITELIST_FILTER | myawk '{ a[$0]++ }END{ for(i in a) print a[i],i }' | myawk -v LOGIN_LIMIT="${_LOGIN_LIMIT}" -- '$1>LOGIN_LIMIT {print $2}' | mysort -uV
fi
else
echo 'MacOS does not support authlog analysis :(' 1>&2
fi > "${authlog}"
}
# ------------------------------------------------------------------------------
# Geoblock Functions
# ------------------------------------------------------------------------------
@ -606,7 +642,11 @@ PRINT_LIST() {
printf '\n# User Defined Rules:\n\n'
cat -- < "${user_rules}"
fi
# Authlog Analysis
if [ "${_CLOUD_BRUTEFORCE_MITIGATION}" -eq 1 ]; then
printf '\n# Rules Generated from %s:\n\n' "$authlog_path1"
cat -- < "${authlog}"
fi
# Tor Filtering
if [ -s "${tor_whitelist}" ]; then
printf '\n# Tor Whitelist:\n\n'
@ -726,7 +766,14 @@ LOGGER() {
PRINT_STATS() {
# Print number of addresses in table (expand CIDR ranges)
typeset v4_num v4_total v6_num v6_total
typeset authlog_num v4_num v4_total v6_num v6_total
authlog_num="$(wc -l -- < "${authlog}" | tr -cd '[:digit:]')"
if [ "${_CLOUD_BRUTEFORCE_MITIGATION}" -eq 1 ]; then
printf '\nBruteforcers found in "%s": %s\n' "${authlog_path1}" "${authlog_num}"
else
printf '\n'
fi
if [ "${_IPV4}" -eq 1 ]; then
v4_num="$(mygrep -cv -- "/[[:digit:]]{1,2}$" < "${v4list}")"
@ -938,7 +985,9 @@ PRE_EXEC_TESTS() {
if [ "${_NO_UID_CHECK}" -ne 1 ]; then
CHECK_PRIVILEGE
fi
if [ "${_CLOUD_BRUTEFORCE_MITIGATION}" -eq 1 ]; then
CHECK_CMD "${authlog_unzip}" > /dev/null
fi
if [ "${_PRINT_ONLY}" -ne 1 ]; then
pfctl="$(CHECK_CMD pfctl)"
CHECK_DRIVE
@ -957,9 +1006,11 @@ VAR_SANITY_CHECK() {
IS_INT "${_BOGON_6}" || ERR 'User defined variable "$_BOGON_6" contains a non-integer value - Unable to proceed!'
IS_INT "${_CHECK_ONLY}" || ERR 'User defined variable "$_CHECK_ONLY" contains a non-integer value - Unable to proceed!'
IS_INT "${_GEOBLOCK}" || ERR 'User defined variable "$_GEOBLOCK" contains a non-integer value - Unable to proceed!'
IS_INT "${_CLOUD_BRUTEFORCE_MITIGATION}" || 'User defined variable "$_CLOUD_BRUTEFORCE_MITIGATION" contains a non-integer value - Unable to proceed!'
IS_INT "${_IPV4}" || 'User defined variable "$_IPV4" contains a non-integer value - Unable to proceed!'
IS_INT "${_IPV6}" || ERR 'User defined variable "$_IPV6" contains a non-integer value - Unable to proceed!'
IS_INT "${_LOG}" || ERR 'User defined variable "$_LOG" contains a non-integer value - Unable to proceed!'
IS_INT "${_LOGIN_LIMIT}" || ERR 'User defined variable "$_LOGIN_LIMIT" contains a non-integer value - Unable to proceed!'
IS_INT "${_NO_UID_CHECK}" || ERR 'User defined variable "$_NO_UID_CHECK" contains a non-integer value - Unable to proceed!'
IS_INT "${_PRINT_ONLY}" || ERR 'User defined variable "$_PRINT_ONLY" contains a non-integer value - Unable to proceed!'
IS_INT "${_RETRY}" || ERR 'User defined variable "$_RETRY" contains a non-integer value - Unable to proceed!'
@ -975,6 +1026,11 @@ VAR_SANITY_CHECK() {
ERR 'No address family enabled! Please enable IPv4 and/or IPv6 in your pf-badhost config!'
fi
# Make sure $_LOGIN_LIMIT is greater than 0
if [ "${_LOGIN_LIMIT}" -lt 1 ]; then
_LOGIN_LIMIT=1
fi
# Make sure $RETRY is greater than 0
if [ "${_RETRY}" -lt 1 ]; then
_RETRY=1
@ -1162,6 +1218,7 @@ main() {
E) authlog_unzip="${OPTARG}" ;; # set tool to unzip authlog
F) netget="${OPTARG}" ;; # set curl/fetch/ftp/wget preference
G) _GEOBLOCK=1 ;; # Enable Geoblocking
H) _CLOUD_BRUTEFORCE_MITIGATION=1 ; _LOGIN_LIMIT="${OPTARG}" ;; # Enable SSH authlog analysis
J) authlog_path1="${OPTARG}" ;;
K) authlog_path2="${OPTARG}" ;;
O) typeset -l -r _OS_TYPE="${OPTARG}" ;;
@ -1265,7 +1322,7 @@ main() {
# Mark user-defined booleans as read-only
readonly _AGENT _LOG _STRICT _IPV4 _IPV6 _AGGREGATE \
_GEOBLOCK _BOGON_4 _BOGON_6 \
_GEOBLOCK _BOGON_4 _BOGON_6 _CLOUD_BRUTEFORCE_MITIGATION \
_TOR_WHITELIST _TOR_BLOCK_ALL _TOR_BLOCK_EXIT \
_RFC3330 _RFC5156 _WHITELIST
@ -1305,6 +1362,18 @@ main() {
openbsd)
test -n "${getroot}" || getroot="$(CHECK_CMD doas)"
test -n "${netget}" || netget='ftp'
test -n "${authlog_path1}" || authlog_path1='/var/log/authlog'
test -n "${authlog_path2}" || authlog_path2='/var/log/authlog.0.gz'
test -n "${authlog_unzip}" || authlog_unzip="$(CHECK_CMD zcat)"
;;
custom)
test -n "${getroot}" || ERR "Custom OS type specified - please set doas/sudo preference with '-Z' option"
test -n "${netget}" || ERR "Custom OS type specified - please set curl/fetch/ftp/wget preference with '-F' option"
if [ "${_CLOUD_BRUTEFORCE_MITIGATION}" -eq 1 ]; then
test -n "${authlog_path1}" || ERR "Custom OS type specified - please specifiy path to SSH authlog with '-J' option"
test -n "${authlog_path2}" || ERR "Custom OS type specified - please specifiy path to secondary SSH authlog with '-K' option"
test -n "${authlog_unzip}" || ERR "Custom OS type specified - please specifiy zcat/bzcat for SSH authlog analysis with '-E' option"
fi
;;
*)
printf '\n\nUnknown Operating System Specified. Available Options Are:\n * -OpenBSD\n * -FreeBSD\n * -NetBSD\n * -DragonflyBSD\n * -MacOS\n\nQuitting Without Making Changes...\n\n'
@ -1313,7 +1382,7 @@ main() {
esac
# Mark operating system specific variables as read-only
readonly getroot netget
readonly getroot netget authlog_path1 authlog_path2 authlog_unzip
# Config test / dry run
if [ "${_CHECK_ONLY}" -eq 1 ]; then
@ -1468,6 +1537,11 @@ main() {
printf '%s\n' "${_i}"
done > "${user_rules}"
# CLOUD_BRUTEFORCE_MITIGATION
if [ "${_CLOUD_BRUTEFORCE_MITIGATION}" -eq 1 ]; then
CLOUD_BRUTEFORCE_MITIGATION
fi
# Generate lists to load into PF
LIST_GEN