-
Notifications
You must be signed in to change notification settings - Fork 3
/
antiflood.php
168 lines (117 loc) · 4.56 KB
/
antiflood.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
#!/usr/bin/php -c /etc/php-cmd.ini
<?php
/***************************************************
* Anti HTTP Flood (Apache)
*
* @author John Cuppi
* @code http://github.com/jcink/antiflood
* @license http://unlicense.org/UNLICENSE
* @version 1.0
* @updated 11:36 PM Friday, April 29, 2011
* @description Protects Apache from HTTP floods.
*
****************************************************/
set_time_limit(0);
ini_set('default_socket_timeout', 1);
$apache_connections_limit = "67"; // apache connections limit
$connections_limit = "650"; // open connections in general
$SYNRECV_connections_limit = "150"; // max SYN_RECs to allow in limbo
$ESTABLISHED_connections_limit = "60"; // max ESTABLISHEDs to allow in limbo
echo "----------------------------------- \n";
echo "Anti HTTP flood script \n";
echo "-----------------------------------\n\n";
system("uname -a");
while(1) {
$raw_nstat = `nice --adjustment=19 netstat -ntu`;
$data = fopen("netstat.txt","w");
fwrite($data, $raw_nstat);
fclose($data);
# ------------------------------------------------------------
# Basic floods with a complete/incomplete handshake
# Doesn't really matter much which
# ------------------------------------------------------------
$netstat_query_1 = `cat netstat.txt | grep ':' | awk '{print \$5}' | awk '{sub("::ffff:","");print}' | cut -f1 -d ':' | sort | uniq -c | sort -nr`;
$sorted_data = explode("\n", $netstat_query_1);
foreach ($sorted_data as $k=>$v) {
$ip_info = explode(" ", trim($v));
if($ip_info[0] > $connections_limit) {
firewall($ip_info[1], "{$ip_info[0]} total open tcp connections detected.");
}
}
# ------------------------------------------------------------
# SYN_RECVs. This is to detect/help with a SYN flood.
# These will jam up the http server pretty quickly.
# ------------------------------------------------------------
$netstat_query_2 = `cat netstat.txt | grep 'SYN_RECV' | grep ':' | awk '{print \$5}' | awk '{sub("::ffff:","");print}' | cut -f1 -d ':' | sort | uniq -c | sort -nr`;
$sorted_data = explode("\n", $netstat_query_2);
foreach ($sorted_data as $k=>$v) {
$ip_info = explode(" ", trim($v));
if($ip_info[0] > $SYNRECV_connections_limit) {
firewall($ip_info[1], "{$ip_info[0]} SYN_RECV connections detected.");
}
}
# ------------------------------------------------------------
# ESTABLISHEDs. This will block off slow POST floods
# ala wikileaks, slowloris type attacks.
# ------------------------------------------------------------
$netstat_query_3 = `cat netstat.txt | grep 'ESTABLISHED' | grep ':80' | awk '{print \$5}' | awk '{sub("::ffff:","");print}' | cut -f1 -d ':' | sort | uniq -c | sort -nr`;
$sorted_data = explode("\n", $netstat_query_3);
foreach ($sorted_data as $k=>$v) {
$ip_info = explode(" ", trim($v));
if($ip_info[0] > $ESTABLISHED_connections_limit) {
firewall($ip_info[1], "{$ip_info[0]} ESTABLISHED connections detected.");
}
}
# ------------------------------------------------------------
# Query apache's server_status page and remove offending IPs
# http://localhost/server-status or otherwise, needs mod_status
# ------------------------------------------------------------
// the request for server status
$server_status = file_get_contents("http://localhost/server-status");
if ( $server_status ) {
// Our great regex
preg_match_all("@((?:\d{1,3}\.){3}\d{1,3})@",$server_status,$matches);
// Gather up connection and IP info
foreach ($matches[1] as $k=>$v) {
$iparray[$v]++;
}
// Sort it all out
foreach ($iparray as $k=>$v) {
if($v > $apache_connections_limit) {
$snap_log=1;
firewall($k, "$v httpd connections detected.");
}
}
unset($iparray);
// Did IPs get banned? Take an HTML based snapshot of the
// page so we can see what it looked like.
if($snap_log) {
$server_status_log=@fopen("./flood_logs/server-status-".time().".txt","a");
@fwrite($server_status_log, $server_status);
@fclose($server_status_log);
}
unset($snap_log);
}
sleep(4);
echo "Checking...\n";
}
// Firewall function, this
// does all of the bannage.
function firewall($ip,$reason) {
global $session_banned;
if($ip != "127.0.0.1" AND $ip != "::1" AND !$session_banned[$ip]) {
// Tell us about a blockage.
print "[ $ip ] blocked: $reason \n";
// Block in IPtables
system("iptables -I INPUT -s {$ip} -j DROP");
$deny = fopen("deny_hosts.rules","a");
$today = @date("F j, Y, g:i a");
fwrite($deny, "# Banned on {$today} with reason: $reason \n");
fwrite($deny, "$ip");
fwrite($deny, "\n");
fclose($deny);
// we banned this IP already for this run
$session_banned[$ip]=1;
}
}
?>