#!/usr/bin/php <?php /* Prevent ban evasion and proxy usage */ /***** How it works ******/ /* This script reads a file named blacklist.txt and prevents names and ips in there from entering the server*/ /* It will adapt to new IP's and aliases; if a new IP is used by a blacklisted alias or visa-versa, it will add to the blacklist*/ /* For example, you can blacklist a clan tag and all members who join will be auto-banned no matter which IP */ /* And if they change their clan tag or name/alias, the bot will remember every IP assosiated with any name they ever used */ /* If ban evaders try and change both alias and IP, the bot will then check for proxies and ban them */ /* Sometimes there are some false positives that can't be avoided, add good ips to the blacklist: "WHITELIST 123.123.123.123"*/ /* You cannot whitelist aliases yet because then ban evaders can just use that alias. */ //Made by dukevin (dukevinjduke@gmail.com) Questions/Comments welcome. PUBLIC DOMAIN 2014 $directory = "df"; //YOUR DIRECTORY NAME $path = "/home/duke/aa/servers/$directory/var/ladderlog.txt"; //path to ladderlog (no need to touch if using Rx Hosting) //Put IPs or aliases you want to blacklist in this file. Partial aliases and ips will work: $file = "/home/duke/aa/servers/$directory/var/customize/blacklist.txt"; //Location of the blacklist file //If the blacklist file is empty, only proxy checking will be avalable $ban_msg = "You were auto-banned for ban evasion"; //message displayed to the person we caught ban evading $proxy_msg = "You were auto-banned for using a proxy"; //message displayed to person using a proxy $ban_time = 99999; //time each ban is worth in minutes $check_proxies = true; //this enables a proxy checker; there can be some false positives so some may want it off /*********** End editable settings *************/ while(1) { $line = rtrim(fgets(STDIN, 1024)); $split = explode(" ", $line); $banned = file($file); if($split[0] == "PLAYER_ENTERED") //PLAYER_ENTERED rebirth 199.255.211.12 Rebirth { if(in_banned_list($split[1], $banned) ||is_numeric($split[1])) //if name matches { p("Name match: {$split[1]}"); if(in_banned_list($split[2], $banned) == false) //check if IP is banned { //looks like they have a new proxy, let's add it file_put_contents($file, $split[2]."\n", FILE_APPEND); p("New IP found from name {$split[2]}. Logged IP"); } cout("BOT: {$split[1]} has triggered ban filter #A."); $banned[] = $split[1]; //add this name to current banlist ban($split[1], $split[2]); } else if(in_banned_list($split[2], $banned)) //if ip matches { p("New alias found from IP {$split[2]}..."); if(strlen($split[1]) >= 3) //don't log names 2 or fewer chars { //call checkuser to see if this name is an impersonation check_alias($split[1]); } //else the ip matches and the name matches, so no need to do anything with ip cout("BOT: {$split[1]} has triggered ban filter #P."); $banned[] = $split[1]; //add this name to current banlist ban($split[1], $split[2]); } else if(is_proxy($split[2]) && $check_proxies) { check_alias($split[1]); //add the new alias if it is valid p("PROXY ".$split[1]." entered the game."); ban($split[1], $split[2], true); $banned[] = $split[1]; //add this name to current banlist } } if($split[0] == "PLAYER_RENAMED") //PLAYER_RENAMED pike pike@ct 85.202.52.21 1 pike { if(in_banned_list($split[2], $banned) || in_banned_list($split[3], $banned) ) //if rename matches name in blacklist or IP still matches { p("Alias/IP match from rename {$split[2]}. Checking prev name {$split[1]}..."); if(in_banned_list($split[1], $banned) == false) //log old alias if it's not already logged {//switched to a banned name check_alias($split[1]); } if(in_banned_list($split[3], $banned) == false) //log ip if it's not already logged { p("Logging new IP using evidence from the rename {$split[1]} -> {$split[2]}"); file_put_contents($file, $split[3]."\n", FILE_APPEND); } cout("BOT: After review, {$split[1]} has been determined to trigger ban filter #R"); $banned[] = $split[2]; //add this new name to current banlist ban($split[2], $split[3]); } else if(in_banned_list($split[1], $banned)) //a banned name switches to unbanned name (this case shouldn't really { //happen but we'll log the new names if we can) $banned[] = $split[2]; //add this new name to current banlist file_put_contents($file, $split[2]."\n", FILE_APPEND); p("A banned name switched to an unbanned name: {$split[1]} -> {$split[2]} Haven't checked for alias usage though, just banning it"); ban($split[2], $split[3]); } } /*if($split[0] == "CYCLE_CREATED") //commented out because it interferes with whitelist but it also checks for banned players during cycle spawning { //not really necessary but will help if there is no whitelist and you just loaded scripts. Otherwise not useful. if(in_banned_list($split[1], $banned)) { p("BLACKLISTED ".$split[1]." cycle_created event. Banned"); echo "ban {$split[1]} 99999 You were auto-banned for being either swag or zulu.\n"; cout("0xff8080{$split[1]} has been auto-banned for being either swag or zulu."); } }*/ if($split[0] == "ROUND_COMMENCING") { if(!file_exists($file)) { cout("BOT: The blacklist file cannot be found at $file"); cout("Only proxy checking is enabled, but alias/ip saving is disabled."); } unset($banned); $banned = file($file); } } //checks if a string is in the banned list. Will match partial strings function in_banned_list($str, $list) { $str = strtolower(trim($str)); if(empty($list)) { cout("BOT: Cannot read file or empty ban list"); return false; } foreach($list as $i) { $i = strtolower(trim($i)); if(stripos($str, $i) !== false) { p("Match: $str -> $i"); return true; } } return false; } //checks to see if an alias should be added to the blacklist based on usage. For example, Player 1 is overused and will not be added, or if a name is being impostered function check_alias($alias) { global $file; if(strlen($alias) >= 3) //don't log names 2 or fewer chars { //call checkuser to see if this name is an impersonation $matches = file_get_contents("http://rxtron.com/rxcommand/checkuser-for-script.php?search=".$alias); if($matches > 4) //the alias they are using has been used by another ip 5+ times, so this might be an impersonation { p("...but didn't log since the alias ".$alias." is probably an impersonation"); return false; } else //the alias they are using has been used less than 5 times so probably isn't an impersonation target { //therefore it is a new name used by this troll p("...the alias ".$alias." was used only $matches times so it is probably bad"); file_put_contents($file, $alias."\n", FILE_APPEND); p("New alias logged (".$alias.") using IP as evidence."); return true; } } p("...the alias is 3 or fewer characters so didn't log check it."); return false; } function is_proxy($ip, $tries = 0) { $result = json_decode(file_get_contents("http://ipinfo.io/".$ip)); if($result->{"country"} == "A1") { p("IP $ip is a known proxy with country code A1"); return true; //if country code is A1 it is a registered proxy } else //need more extensive tests for unregistered proxies { $url = 'http://www.checkingtools.com/ip_check'; $vars = 'ip=' . $ip . '&GO!=GO!'; $ch = curl_init( $url ); curl_setopt( $ch, CURLOPT_POST, 1); curl_setopt( $ch, CURLOPT_POSTFIELDS, $vars); curl_setopt( $ch, CURLOPT_FOLLOWLOCATION, 1); curl_setopt( $ch, CURLOPT_HEADER, 0); curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1); $response = curl_exec( $ch ); $triggers = substr_count($response, 'a class="red"'); if($triggers > 0) { if(!empty($response)) { p("IP $ip has triggered $triggers proxy filters."); return falsepcheck($ip); } } else if(empty($response) && $tries < 1) return is_proxy($ip, ++$tries); //in case of time-out else if(empty($response) || strlen($response) < 50) cout("BOT: Proxy check failure: Timeout"); else return false; } return false; } function ban($name, $ip, $wasproxy = false) { global $file, $ban_msg, $proxy_msg, $ban_time; $lines = file($file); foreach($lines as $l) { $t = explode(" ", $l); if(strtoupper($t[0]) == "WHITELIST" && strpos($t[1], $ip) !== false ) { p("Would have banned $name but ".trim($t[1])." is on the whitelist."); return; } } if(is_numeric($name)) { echo "ban_ip $ip $ban_time $ban_msg \n"; echo "delay_command 3 ban_ip $ip $ban_time $ban_msg\n"; } else { if(!$wasproxy) echo "ban $name $ban_time $ban_msg \n"; else echo "ban $name $ban_time $proxy_msg \n"; } if(!$wasproxy) cout("0xff8080$name has been auto-banned for ban evasion"); else cout("0xff8080$name has been auto-banned for using a proxy."); } function falsepcheck($ip) { global $path, $directory; $seg = explode(".",$ip); $seg = $seg[0].".".$seg[1]; //partial ip for dynamic ips $matches = file_get_contents("http://rxtron.com/rxcommand/checkuser-for-script.php?search=".$seg); if($directory != "df") //let's also check your logs $matches += sizeof(explode(PHP_EOL, `grep $seg $path`)); if($matches >= 5) //similar IPs have also played in this server { p("There are $matches entries/renames with similar ips of $seg, so probably not a proxy"); return false; } //another test for name usage for this particular ip $grep = `curl -s -o - http://rxtron.com/rxcommand/ips.txt | grep $ip`; if($directory != "df") $grep .= file_get_contents($path); $ra = explode(PHP_EOL, $grep); foreach($ra as $a) { $raa = explode(" ", $a); if($raa[0] == 'PLAYER_ENTERED') $names[] = normal_chars($raa[1]); else if($raa[0] == 'PLAYER_RENAMED') $names[] = normal_chars($raa[2]); } $size = sizeof($names); if($size > 14) { p("There are $size entries which is > 14 so probably not a proxy"); return false; //lots of names for 1 ip so most likely not proxy } else if($size <= 2) { p("There are $size entries which is < 3 so it's probably a proxy"); return true; //only 2 or fewer so it probably is } $names = array_count_values($names); sort($names, SORT_NUMERIC); $names = array_reverse($names); if(empty($names[1])) $names[1] = 1; if($names[0] + max(1,$names[1]) > 3) { p("The most used two aliases were used {$names[0]} + {$names[1]}/1 > 4 so not proxy"); return false; } else { p("The most used two aliases were used ".($names[0] + $names[1])." which is <= 3 so it's probably a proxy"); return true; } } function normal_chars($string) //purpose is to combine aliases which use weird ascii with non ascii counterparts { $string = str_replace('\\', '', $string); $string = htmlentities($string, ENT_QUOTES, 'UTF-8'); $string = preg_replace('~&([a-z]{1,2})(acute|cedil|circ|grave|lig|orn|ring|slash|th|tilde|uml);~i', '$1', $string); $string = html_entity_decode($string, ENT_QUOTES, 'UTF-8'); $string = preg_replace(array('~[^0-9a-z]~i', '~[ -]+~'), ' ', $string); return str_replace(' ', '', $string); } function cout($str) { echo "console_message {$str}\n"; } function p($str) { echo $str."\n"; } ?>