''' This module count the occurences of dodgy terms present in a file ''' import os import logging logging.basicConfig(level=logging.DEBUG) import scanmodule def main(): return GrepCount() class GrepCount(scanmodule.ScanModule): name = 'grep count' # ranked from 1 to 10, 10 being EVIL # Also, 100 is awarded to MEGA-DUH-OBVIOUS things. dodgy_terms = { '$GLOBALS': 6, 'WWW-Authenticate': 7, 'ZipArchive': 6, 'apache_get_modules': 8, 'assert': 5, 'base64_decode': 7, 'bzdecompress': 7, 'chmod': 8, 'curl_init("file://': 100, # safe mode bypass exploit #'dl': 10, #'exec': 10, 'eval(': 10, 'eval(base64_decode': 100, 'eval($_GET': 100, 'eval($_POST': 100, 'eval($_REQUEST': 100, 'eval(base64_decode': 100, 'eval(gzinflate': 100, 'file_get_contents': 6, 'fpassthru': 100, 'fsockopen': 7, 'ftp_connect': 7, 'ftp_exec': 7, 'ftp_login': 7, 'function_exists': 4, 'get_current_user': 10, 'getcwd': 8, 'getenv': 9, 'getmxrr': 5, 'getmygid': 10, 'getmygid': 10, 'getmyinode': 10, 'getmypid': 10, 'getmyuid': 10, 'gzinflate': 7, 'gzinflate(base64_decode(': 100, 'gzuncompress': 7, 'ini_get': 6, 'ini_set': 6, 'is_readable': 10, 'mysql_get_client_info': 7, 'open_basedir': 9, 'passthru': 10, 'passthru($_GET': 100, 'passthru($_POST': 100, 'passthru($_REQUEST': 100, 'pclose': 9, 'pcntl_fork': 10, 'php_logo_guid': 10, 'php_uname': 8, 'phpcredits': 10, 'phpinfo': 10, 'phpversion': 5, 'pnctl_exec': 10, 'pnctl_fork': 10, 'popen': 10, 'posix_getegid': 10, 'posix_geteuid': 10, 'posix_getgetgruid': 10, 'posix_getpwuid': 10, 'posix_kill': 10, 'posix_mkfifo': 10, 'posix_setgid': 10, 'posix_setpgid': 10, 'posix_setsid': 10, 'posix_setuid': 10, 'posix_uname': 10, 'php://input': 7, 'proc_close': 10, 'proc_get_status': 10, 'proc_nice': 10, 'proc_open': 10, 'proc_terminate': 10, 'putenv': 10, 'putenv("PHP': 100, # Shellshock exploit 'putenv(\'PHP': 100, # Shellshock exploit 'safe_mode': 10, 'shell_exec': 10, 'show_source': 10, 'socket_create(AF_INET, SOCK_STREAM, SOL_TCP)': 10, # Used for SYN flood 'symlink': 8, 'system(': 9, 'system($_GET': 100, 'system($_POST': 100, 'system($_REQUEST': 100, 'win_shell_execute': 10, 'win_create_service': 100, 'wscript': 8, 'zend_logo_guid': 10, 'zend_thread_id': 9, 'zend_version': 9, } dodgy_terms.update({ '/bin/bash ': 100, '/bin/sh ': 100, '/etc/hosts': 100, '/etc/passwd': 100, '/etc/resolv.conf ': 100, '/etc/shadow': 100, '/etc/syslog.conf': 100, '/proc/cpuinfo': 10, '/tmp': 8, '/var/cpanel/accounting.log': 100, 'IRC server': 100, 'LD_PRELOAD': 100, 'PRIVMSG': 100, 'Safe Mod Bypass': 100, 'Shell ': 9, '\\x': 3, # Shellcodes '\x00/../': 100, # safe mode bypass 'backdoor': 10, 'bypass': 8, 'chkrootkit': 100, 'chmod 777': 7, 'cmd.exe': 100, 'dir /OG /X': 100, 'find . -type f': 100, 'gcc ': 8, 'id_rsa': 100, 'ipconfig /all': 100, 'jschl_vc': 100, # Cloudflare bypass 'jschl_answer': 100, # Cloudflare bypass 'kernel32.dll': 100, 'ls -la': 100, 'milw0rm': 100, 'my.cnf': 100, 'my.conf': 100, 'nc -l': 100, 'netstat ': 100, 'file:file://': 100, # basedir bypass 'portsentry': 100, 'proftpd.conf': 100, 'ps -aux': 100, 'rkhunter': 100, 'shellcode': 100, 'slowloris': 100, 'snort': 100, 'system32': 9, 'tripwire': 100, 'uname -a': 100, 'wget': 8, 'WinExec': 10, }) dodgy_terms.update({ '/cdn-cgi/l/chk_jschl': 100, # Cloudflare bypass for DDoS'ing 'Antichat Shell': 100, 'Cr@zy_King': 100, 'KAdot@ngs.ru': 100, 'Kacak': 100, 'KingDefacer': 100, 'SimAttacker': 100, 'SoldiersOfAllah': 100, 'ak74-team.net': 100, 'alturks.com': 100, 'egy_spider' : 100, 'egyspider.eu' : 100, 'exploit-db.com': 100, 'forever5pi': 100, 'grayhatz.org': 100, 'kacaq.blogspot.com': 100, 'locus7s.com': 100, 'michaeldaw.org': 100, 'milw0rm.com': 100, 'pentestmonkey': 100, 'r57.biz': 100, 'r57shell.net': 100, 'rootshell-team.info': 100, 'simorgh': 100, 'thecrowsrew.org': 100, 'vnhacker.org': 100, 'xdevil.org': 100, 'zehirhacker': 100, '~z0mbie': 100, }) def populate(self, path): ''' Does nothing :< ''' pass def evaluate(self, path): ''' Check the given file against a list of know dodgy strings. The calculation formulae is empirical. @ret A sorted list of the form [name, match_in_percent_superior_to_zero] ''' fsize = os.path.getsize(path) if not fsize: return None content = '' with open(path, 'r') as f: content = f.read() score = 0 for key,data in self.dodgy_terms.iteritems(): nb = content.find(key) * data if nb > 0: score += nb score /= fsize logging.info('Grep score for ' + path + ' : ' + str(score)) if score > 75: return [['MALWARE', min(score, 100)],] return None def load(self, path): pass def save(self, path): pass def is_malware(self, path): return self.evaluate(path) is not None