├── test.php ├── runddosfirewall.sh ├── init.php ├── config.php ├── README.md ├── logparser.php └── analyser.php /test.php: -------------------------------------------------------------------------------- 1 | 127.0.0.1 29 | // [1] => 1.53.194.96 30 | // ) 31 | -------------------------------------------------------------------------------- /runddosfirewall.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | tmux new-session -n 'xnohatDDoSFirewall' -d 'echo -e "Press Ctrl-b LEFT to move Cursor to this panel\nRun Command: php analyser.php"; bash -i' #create window with 1st panel 3 | tmux split-window -h -p 50 'nload; bash -i' #split window horizon to 50% 4 | tmux split-window -v -p 70 'top; bash -i' #split 'new created previous panel' to 2 panel with new create panel is 60% of previous panel 5 | tmux split-window -v -p 40 'php logparser.php; bash -i' #split previous created panel to 2 panel with new create panel is 30% of previous panel 6 | #tmux select-pane -t +1 #move focus to 1st panel 7 | #tmux send-keys 'top; bash -i' Enter #run some command in 1st panel 8 | tmux -2 attach-session -d 9 | -------------------------------------------------------------------------------- /init.php: -------------------------------------------------------------------------------- 1 | 8 | * @link https://junookyo.blogspot.com/ 9 | * @param string $name Function to check for 10 | * @return bool 11 | */ 12 | function function_usable($name) { 13 | $disable_funcs = get_cfg_var('disable_functions'); 14 | $disable_funcs = ($disable_funcs === FALSE OR empty($disable_funcs)) ? ini_get('disable_functions') : $disable_funcs; 15 | 16 | if ($disable_funcs === FALSE OR empty($disable_funcs)) { 17 | return TRUE; 18 | } else { 19 | if (strpos($disable_funcs, ',') === FALSE) { 20 | return strtolower($disable_funcs) !== $name; 21 | } 22 | 23 | $disable_funcs = str_replace(' ', '', $disable_funcs); 24 | $disable_funcs = explode(',', strtolower($disable_funcs)); 25 | return ! in_array($name, $disable_funcs); 26 | } 27 | } 28 | 29 | // Test exec() 30 | function_usable('exec') OR exit('Error: The firewall cannot run on this server. Please remove "exec" from "disable_functions" in php.ini'); 31 | -------------------------------------------------------------------------------- /config.php: -------------------------------------------------------------------------------- 1 | -w 45 | + Open .pcap file in Wireshark: 46 | + go to Edit -> Preference set: 47 | * "Show burst count for item rather than rate" set Enabled (check mark in the box) 48 | * set Burst rate resolution = Burst rate window size = 60000 miliseconds (1 min) 49 | + Go Statistic -> ipv4 -> all addresses: burst rate is number of packets per minute, use average numbers (just guess!) to Threshold 50 | 51 | --------- 52 | 53 | $ chmod +x ./xnohatddosfirewall/runddosfirewall.sh 54 | 55 | $ chmod 777 ./xnohatddosfirewall 56 | 57 | Run script with sudo or root privilege: sudo ./runddosfirewall.sh 58 | follow guide on screen 59 | 60 | Press "Ctrl-b d" : to detach tmux 61 | 62 | Re-attach tmux by command $ tmux attach -t 0 63 | 64 | Start all your services again 65 | 66 | 67 | 68 | CHANGE LOG: 69 | * v1.1 : first release 70 | * v1.2 : Implement new algorithm -------------------------------------------------------------------------------- /logparser.php: -------------------------------------------------------------------------------- 1 | query('PRAGMA synchronous = OFF'); 16 | $db->query('PRAGMA journal_mode = MEMORY'); 17 | $db->query('PRAGMA busy_timeout = 300000'); 18 | 19 | //Check log table exist or not 20 | $res_check_table_exist = $db->query("SELECT * FROM sqlite_master WHERE name = 'accesslog' and type='table' "); 21 | if (!$res_check_table_exist->fetchArray()) { // Not have table accesslog 22 | //echo "Table Not exist"; 23 | //Create Table accesslog 24 | $db->exec('CREATE TABLE accesslog (remote_ip varchar(255), request_time NUMERIC)'); 25 | $db->exec("CREATE INDEX accesslog_index ON accesslog(remote_ip,request_time)"); 26 | echo "Table accesslog has been created \r\n"; 27 | } 28 | 29 | //Follow Log 30 | $size = filesize(LOG_FILE); //set to current file size to move disk read cursor to end of file 31 | 32 | while (true) { 33 | 34 | clearstatcache(); 35 | $currentSize = filesize(LOG_FILE); 36 | if ($size === $currentSize) { 37 | usleep(100); 38 | continue; 39 | } 40 | 41 | $fh = fopen(LOG_FILE, 'r'); 42 | fseek($fh, $size); 43 | 44 | while ($line = fgets($fh)) { 45 | 46 | // process the line read. 47 | if ( ! empty($line)) { 48 | //-----Clear wasted character----- 49 | $clear_char = array('[', ']'); 50 | $line = str_replace($clear_char, '', $line); //strip special chars 51 | 52 | //-----Parse Log Line----- 53 | $arr_log_line = explode(' ', $line); 54 | //var_dump($arr_log_line);continue; 55 | $remote_ip = $arr_log_line[0]; 56 | $request_time = @date("Y-m-d H:i:s", @strtotime(str_replace('/', '-', substr_replace($arr_log_line[3], ' ', -9, 1)))); //original nginx time look like 05/Nov/2016:01:35:24 , remember change for apache , SQLite format must look like 2016-11-05 01:35:24 57 | $db->exec('INSERT INTO accesslog (remote_ip, request_time) VALUES ("' . $remote_ip . '","' . $request_time . '")'); //insert request to DB 58 | //echo $remote_ip.' - '.$request_time."\r\n"; 59 | echo $line; 60 | } 61 | 62 | } 63 | 64 | fclose($fh); 65 | $size = $currentSize; 66 | } 67 | 68 | $db->close(); 69 | } 70 | 71 | //function for fatal error case 72 | function fatal_handler() { 73 | $errfile = 'unknown file'; 74 | $errstr = 'shutdown'; 75 | $errno = E_CORE_ERROR; 76 | $errline = 0; 77 | 78 | $error = error_get_last(); 79 | 80 | if (!is_null($error)) { 81 | $errno = $error['type']; 82 | $errfile = $error['file']; 83 | $errline = $error['line']; 84 | $errstr = $error['message']; 85 | 86 | main(); 87 | } 88 | } 89 | 90 | ?> -------------------------------------------------------------------------------- /analyser.php: -------------------------------------------------------------------------------- 1 | query('PRAGMA synchronous = OFF'); 37 | $db->query('PRAGMA journal_mode = MEMORY'); 38 | $db->query('PRAGMA busy_timeout = 300000'); 39 | 40 | //$res_count_request = $db->query('SELECT remote_ip, count(remote_ip) AS request_num FROM accesslog GROUP BY remote_ip ORDER BY request_num DESC'); //load all request in database is VERY SLOW 41 | $res_count_request = $db->query('SELECT remote_ip, count(remote_ip) AS request_num FROM accesslog WHERE request_time BETWEEN "' . @date("Y-m-d H:i:s", time() - TIME_WINDOW) . '" AND "' . @date("Y-m-d H:i:s", time()) . '" GROUP BY remote_ip ORDER BY request_num DESC'); 42 | while ($row = $res_count_request->fetchArray()) { 43 | //print_r($row); 44 | if ($row['request_num'] >= THRESHOLD AND !in_array($row['remote_ip'], $exclude_ips)) { 45 | 46 | exec('iptables -A INPUT -s ' . $row['remote_ip'] . ' -j BLOCKEDIP'); 47 | 48 | echo 'BLOCKED IP: ' . $row['remote_ip'] . ' (REQ_NUM: ' . $row['request_num'] . '/' . TIME_WINDOW . " seconds)\n"; 49 | file_put_contents('blockedip.log', $row['remote_ip'] . '-' . $row['request_num'] . "\n", FILE_APPEND); 50 | file_put_contents('manualblockip.sh', 'iptables -A INPUT -s ' . $row['remote_ip'] . ' -j BLOCKEDIP' . "\n", FILE_APPEND); 51 | 52 | $ipblocklist[] = $row['remote_ip']; 53 | } 54 | } 55 | 56 | removeDuplicateIptablesRules(); 57 | 58 | $db->close(); 59 | 60 | echo 'SLEEP IN ' . SLEEP_TIME . "seconds\n"; 61 | sleep(SLEEP_TIME); 62 | 63 | } 64 | catch (Exception $error) { 65 | //do nothing 66 | } 67 | 68 | } 69 | 70 | } 71 | 72 | function removeDuplicateIptablesRules() { 73 | exec('iptables-save', $arr_iptables_save_output); 74 | foreach ($arr_iptables_save_output as $k => $v) { 75 | if (($kt = array_search($v, $arr_iptables_save_output)) !== FALSE AND $k != $kt AND strpos($v, '-A') !== FALSE) { 76 | unset($arr_iptables_save_output[$kt]); 77 | } 78 | } 79 | 80 | $removed_duplicate_iptables_rules = implode("\n", $arr_iptables_save_output); 81 | file_put_contents('removed_duplicate_iptables_rules.txt', $removed_duplicate_iptables_rules); 82 | exec('iptables -F'); // Clear all rules 83 | exec('iptables-restore < removed_duplicate_iptables_rules.txt'); 84 | echo "Removed Duplicate Rules from IPTables\n"; 85 | } 86 | 87 | //function for fatal error case 88 | function fatal_handler() { 89 | $errfile = 'unknown file'; 90 | $errstr = 'shutdown'; 91 | $errno = E_CORE_ERROR; 92 | $errline = 0; 93 | 94 | $error = error_get_last(); 95 | 96 | if (!is_null($error)) { 97 | $errno = $error['type']; 98 | $errfile = $error['file']; 99 | $errline = $error['line']; 100 | $errstr = $error['message']; 101 | 102 | main(); 103 | } 104 | } 105 | 106 | ?> --------------------------------------------------------------------------------