YANZ UPLOAD ANJG

#! /usr/bin/python3 ''' frob: fix something ''' # see also maintenance/protect import sys import os import glob import pwd import stat import re import random import time import socket import syslog ipcs_remove_apache_cmd=""" for i in `ipcs -s | grep nobody | awk '{print $2}'` ; do ipcrm sem $i; done """ def frob(): r = "/usr/local/afribin/frob" if os.path.exists(r): return r import inspect return inspect.getfile(frob) class CpanelData: def __init__(self,user=None, verbose=True): '''Open user file''' self.verbose=verbose if not user: user=self.userfromcwd() self.setuser(user) def setuser(self,user): '''Make the user blah''' self.user=user self.getcpaneldata() self.makeapacheconfdir() def userfromcwd(self): '''Choose user from current working directory''' user=os.getcwd().split("/home/",1)[1].split("/",1)[0] if not user: print(os.getcwd(),"does not seem to indicate a user") sys.exit() return user def has_site(self,site): # FIXME: this does not handle site aliases in the userdata files for key in [ "DNS", "DNS1", "DNS2", "DNS3" ] : if self.cpanel.get(key,'')==site: return True if "www."+self.cpanel.get(key,'')==site: return True return False def getcpaneldata(self): '''Read the cpanel data file into self.cpanel[]''' cpaneldatafile=open("/var/cpanel/users/"+self.user,"r"); self.cpanel={} for line in cpaneldatafile: bits=line.split("=",1) if len(bits)>1: self.cpanel[bits[0].strip()]=bits[1].strip() cpaneldatafile.close() return self.cpanel def setitem(self,item,value): cpaneldatafile=open("/var/cpanel/users/"+self.user,"r"); out=[] for line in cpaneldatafile: if line.startswith(item+"="): continue out.append(line) if len(out)>0 and not out[-1].endswith('\n'): out.append("\n") cpaneldatafile.close() out.append(item+'='+value+"\n") cpaneldatafile=open("/var/cpanel/users/"+self.user,"w"); cpaneldatafile.writelines(out) cpaneldatafile.close() return value def mkdir_p(self,dir): bits=dir.split("/") for a in range(1,len(bits)+1): try: os.mkdir("/".join(bits[:a])) except: pass def makeapacheconfdir(self): '''Get the apache dir for configs''' if not self.cpanel: self.getcpaneldata() apacheconfbase="/usr/local/apache/conf/userdata/std/2/"+self.user; apachesslbase ="/usr/local/apache/conf/userdata/ssl/2/"+self.user; self.mkdir_p(apacheconfbase) self.mkdir_p(apachesslbase) apacheconfdir=apacheconfbase+"/"+self.cpanel['DNS'] self.mkdir_p(apacheconfdir) for dns in ("DNS","DNS1","DNS2","DNS3","DNS4"): if dns not in self.cpanel: continue for linkdir in (apacheconfbase,apachesslbase): linkname = linkdir+"/"+self.cpanel[dns] if os.path.exists(linkname): continue os.symlink(apacheconfdir,linkname) self.apacheconfdir=apacheconfdir return apacheconfdir def __update(self): '''Get ''' self.getcpaneldata() def get_apachecustomfile(self,filename): '''Figure out where to put the modsecurity stuff''' self.makeapacheconfdir() return self.apacheconfdir+"/"+filename def get_modsecinifile(self): '''Figure out where to put the modsecurity stuff''' self.modsecinifile=self.get_apachecustomfile("modsec.conf") return self.modsecinifile def read_modsecinifile(self): self.get_modsecinifile() self.modsec={} self.modsec['SecRuleRemoveById']=[] if os.path.exists(self.modsecinifile): f = open(self.modsecinifile,"r") self.modseclines=f.readlines() f.close() else: self.modseclines=[] for line in self.modseclines: line=line.strip(); match=re.search(r'^\s*#',line) if match: continue match=re.search(r'SecRuleEngine +(\S+)',line) if match: self.modsec['SecRuleEngine']=match.group(1) match=re.search(r'SecRuleEngine _(\S+)',line) match=re.search(r'SecRuleRemoveById +(.*)',line) if match: self.modsec['SecRuleRemoveById'].extend(match.group(1).split()) def write_modsecinifile(self,reason=''): self.get_modsecinifile() nomodsec= \ """ # %(reason)s SecRuleEngine %(SecRuleEngine)s """ template= \ """ # Turn off specific rules SecRuleEngine On SecRuleRemoveById %(SecRuleRemoveById)s """ self.modsec['reason']=reason if self.modsec.get('SecRuleEngine','')=="Off" \ or len(self.modsec.get("SecRuleRemoveById",[]))==0: out=nomodsec % dict( SecRuleEngine=self.modsec.get("SecRuleEngine","On"), reason=reason, ) else: out=template % dict( SecRuleRemoveById=" ".join(self.modsec.get("SecRuleRemoveById",[])), reason=reason, ) f = open(self.modsecinifile,"w") f.write(out) f.close() if self.verbose: print("Wrote %s -- frob rebuild to rebuild and reload" % self.modsecinifile) def do_frob(i): print(r""" If you've got the job, we've got the frob. ___ ______ /__/\ ___/_____/\ \ \ \ / /\\ \ \ \_/__ / \ _\ \ \ /\_____/___ \ // \__\/ / \ /\ \ _______//_______/ \ / _\/______ / / \ \ / / / /\ __/ / \ \ / / / / _\__ / / / \_______\/ / / / / /\ /_/______/___________________/ /________/ /___/ \ \ \ \ ___________ \ \ \ \ \ / \_\ \ / /\ \ \ \ \___\/ \ \/ / \ \ \ \ / \_____/ / \ \ \________\/ /__________/ \ \ / \ _____ \ /_____\/ \ / /\ \ / \ \ \ /____/ \ \ / \ \ \ \ \ /___\/ \ \ \ \____\/ \__\/ """) def fix_ipcs(): # FIXME: this does not handle site aliases in the userdata files global ipcs_remove_apache_cmd os.system(ipcs_remove_apache_cmd) def do_strace(usernames): "Install (and remove) strace wrapper" phpexec = "/usr/local/cpanel/cgi-sys/php5" phpexec_o = phpexec+".orig" lines = open(phpexec,'r').readlines() if lines[0].find("#!")!=0: print("It doesn't look like",phpexec,"is a wrapper file") return open(phpexec_o,'w').writelines(lines) out = [] droplevel=0 for line in lines: if line.find('exec ')==0 and len(usernames): userlist='|'.join(usernames) out.append('USER=`whoami`\n') out.append('case $USER in\n') out.append(' %s) mkdir -p /tmp/$USER; chmod 700 /tmp/$USER; exec /usr/bin/strace -f -s 256 -t -o /tmp/$USER/strace-$$ /usr/bin/php "$@" ;;\n' % userlist) out.append('esac\n') if line.find("USER=")==0: droplevel+=1 if droplevel==0: out.append(line) if line.find("esac")==0: droplevel=0 open(phpexec,'w').writelines(out) def pwtest(username,password): 'submit test via smtp' import base64 params = dict( u64 = base64.encodestring(username).strip(" \n\r"), p64 = base64.encodestring(password).strip(" \n\r"), helo = 'i.r.testa', mailfrom = username, rcptto = 'joe@hostmail.com', ) test=os.popen("exim -dd -bhc 160.124.1.1",'w') smtp = """EHLO %(helo)s AUTH LOGIN %(u64)s %(p64)s MAIL FROM: <%(mailfrom)s> RCPT TO: <%(rcptto)s> DATA From: <%(mailfrom)s> To: <%(rcptto)s> Subject: not a real mail test . QUIT """ % params print(smtp) test.write(smtp) test.close() def has_fcgi(): # grep '^AddHandler fcgid-script .*\.php' /usr/local/apache/conf/php.conf >& /dev/null || exit # grep '^Include "/usr/local/apache/conf/php.conf"' /etc/httpd/conf/httpd.conf >& /dev/null || exit httpdconf=open("/usr/local/apache/conf/httpd.conf",'r') httpdconf_re = re.compile(r'^\s*Include\s+"/usr/local/apache/conf/php.conf"') Found=False for line in httpdconf: if httpdconf_re.search(line): Found=True break if not Found: return False phpconf=open("/usr/local/apache/conf/php.conf",'r') phpconf_re = re.compile(r'^\s*AddHandler\s+fcgid-script\s.*\.php') Found=False for line in phpconf: if phpconf_re.search(line): Found=True break if not Found: return False # We got it ... return True # Functions that do something related to files owned by 'nobody' def uid_nobody(): 'return the uid for nobody' return pwd.getpwnam("nobody").pw_uid def nobodyfile(file): 'Is the named file a regular file owned by nobody' nobody=uid_nobody() try: fileinfo = os.stat(file) if fileinfo.st_uid == nobody and fileinfo.S_ISDIR(stat.st_mode): return True except: pass return False def hasnobodyfile(dir): 'Check if there are any nobody files in the directory' for file in glob.glob(dir+'/*'): if nobodyfile(file): return True return False def hasphpfiles(dir): 'Check if there are any legitimate php files in the directory' for file in glob.glob(dir+'/*.php'): return True return False def htaccess_patch(htaccessfile): 'Patch a .htaccess file and return directives required for the php.ini' fd = open(htaccessfile,'r') lines=[] phpinidirectives=[] phpvalue = re.compile(r"""^\s*php_(admin_value|value|flag)\s+['"]?([^"']*)""", re.IGNORECASE) drupal_disabled_php = re.compile(r"""^\s*SetHandler\s""", re.IGNORECASE) optionsymlink = re.compile(r"^\s*Option.*(FollowSymLinks)", re.IGNORECASE) ifmodule = re.compile(r"", re.IGNORECASE) endifmodule = re.compile(r"", re.IGNORECASE) modified = False adding_ifmodule=False ifmodule_depth=0 isdrupal=False for line in fd: if line[-1:] != "\n": line += "\n" phpvalue_m = phpvalue.search(line.strip()) ifmodule_m = ifmodule.search(line.strip()) endifmodule_m = endifmodule.search(line.strip()) if ifmodule_m: ifmodule_depth+=1 if endifmodule_m: ifmodule_depth=max(ifmodule_depth-1,0) # print ifmodule_depth,adding_ifmodule,line.strip() if phpvalue_m: value=phpvalue_m.group(2) value=value.strip('=\r\n\t \'"') phpinidirectives.append(value) if ( not adding_ifmodule ) and ( ifmodule_depth==0): lines.append("\n") ifmodule_depth+=1 adding_ifmodule= True modified = True lines.append(line) continue else: if adding_ifmodule and len(line.strip())>0: lines.append("\n") ifmodule_depth -= 1 adding_ifmodule=False; if drupal_disabled_php.search(line): isdrupal = True optionsymlink_m = optionsymlink.search(line.strip()) if optionsymlink_m and not isdrupal: lines.append("# FollowSymLinks conflicts with mod_fcgid:\n") # but if we're not doing fcgi anymore then hey lines.append("## "+line) modified = True continue lines.append(line) if adding_ifmodule: lines.append("\n") adding_ifmodule=False; fd.close() if modified: fd = open(htaccessfile,'w') fd.writelines(lines) fd.close() return phpinidirectives class phpini: def __init__(self): self.cpanel=CpanelData() self.php_ini = "/home/"+self.cpanel.user+"/php.ini" self.load() def load(self): readfile = self.php_ini if not os.path.exists(self.php_ini): readfile="/usr/lib/php.ini" self.lines = open(readfile,'r').readlines() def save(self): open(self.php_ini,'w').writelines(self.lines) return # Get a php value def __getitem__(self,key): for line in range(0,len(self.lines)): bits = self.lines[line].split("=",1) if len(bits)!=2: continue if bits[0].strip()==key: return bits[1].strip() def __setitem__(self,key,value): 'Set a value' found=None for line in range(0,len(self.lines)): bits = self.lines[line].split("=",1) if len(bits)!=2: continue if bits[0].strip()==key: found=line if found==None: found=len(self.lines) self.lines.append('') before = self.lines[found] self.lines[found]=key + '='+ value+'\n'; self.save() def htaccess_fixup(): cpanel = CpanelData() php_ini = "/home/"+cpanel.user+"/php.ini" print(php_ini) phpinidirectives=[] if not os.path.exists(php_ini): print(php_ini,"does not exist") customphpini(rebuild=False) maxdepth = ' ' if options.number: maxdepth=' -maxdepth %d ' % options.number for line in os.popen("find . %s -name .htaccess" % maxdepth,'r').readlines(): htaccess=line.strip(); if not os.path.exists(htaccess): continue phpinidirectives.extend( htaccess_patch(htaccess) ) directives={} for m in phpinidirectives: try: name,value=m.split(" ",1) except ValueError: print("Ignoring directive \"%s\"" % m) continue directives[name.strip().lower()]="%s = %s\n" % (name,value) if len(phpinidirectives)>0: print("\n".join(phpinidirectives)) ini = open(php_ini,'r') inilines = ini.readlines() ini.close() output=[] for line in inilines: if line[-1:] != "\n": line += "\n" bits=line.split("=",1) if len(bits)!=2: output.append(line) continue directive=bits[0].strip().lower() if directive in list(directives.keys()): output.append("# "+line) output.append(directives[directive]) directives.pop(directive) else: output.append(line) # put unmatched required directives at the head of php.ini for k,m in list(directives.items()): output.insert(0,m); ini = open(php_ini,'w') ini.writelines(output) ini.close() def mysqllimit(args,max=30): users=args if max==0: max=30 if max>100: print("Too many connections") sys.exit(1) for user in users: cpanel = CpanelData(user=user) settings=dict(user=cpanel.user, max=max) if not cpanel.user: print("Huh? What user %s" % user) continue mysqlcmd="""mysql mysql -e "update user set max_user_connections=%(max)d where User like '%(user)s%%'; flush privileges;" """ \ % settings print(mysqlcmd) os.system(mysqlcmd) def subscribe(): print("Subscribing everyone on the box to the INBOX.spam folder") cmd = """ find /home/*/mail/* -maxdepth 2 -mindepth 2 -name courierimapsubscribed -type f | while read f; do grep INBOX.spam "$f" >/dev/null || echo >> "$f" INBOX.spam ; done """ print(cmd) os.system(cmd) # Show phpinfo or something like it: def show_phpinfo(): home = droppriv() if os.path.exists(home+"/php.ini"): os.system("php -c $HOME -r 'phpinfo();' &1") else: os.system("php -r 'phpinfo();' &1") # Allow custom php.ini def customphpini(rebuild=False,disable_functions=False, open_basedir=False, setupfw=''): cpanel = CpanelData() data=dict(homedir="/home/"+cpanel.user) if not os.path.exists("/home/"+cpanel.user+"/php.ini"): print("Copying /usr/lib/php.ini to homedir") os.system("cp /usr/lib/php.ini %(homedir)s" % data ) else: print("/home/"+cpanel.user+"/php.ini already exists.") if open("/etc/httpd/conf/php.conf",'r').read().find("runphp")>=0: print("This system uses runphp, so we don't change the apache config") rebuild = False else: apacheconfdir=cpanel.makeapacheconfdir() fcgiphpconf=apacheconfdir+"/phpini.conf" if os.path.exists(fcgiphpconf): print("# There is already a file "+fcgiphpconf+" with the contents below:") print(open(fcgiphpconf,'r').read()) print("# There is already a file "+fcgiphpconf+" with the contents above") return try: f = open(fcgiphpconf,'w') print("Writing",fcgiphpconf) template = """ # Allow custom php.ini FCGIWrapper "/usr/local/cpanel/cgi-sys/php5 -c %(homedir)s" .php5 FCGIWrapper "/usr/local/cpanel/cgi-sys/php5 -c %(homedir)s" .php """ f.write(template % data ) f.close() except IOError: print("(Not writing", fcgiphpconf,")") rebuild=False php = phpini() if disable_functions: badfunctions=[ 'shell_exec', 'apache_get_modules', 'apache_get_version', 'apache_getenv', 'apache_note', 'apache_setenv', 'dl', 'escapeshellarg', 'escapeshellcmd', 'exec', 'get_current_user', 'getmyi', 'ini_alter', 'link', 'mysql_list_dbs', 'openlog', 'pass_thru', 'passthru', 'pclose', 'popen', 'pcntl_exec', 'pcntl_fork', 'pcntl_signal', 'pcntl_waitpid', 'pcntl_wexitstatus', 'pcntl_wifexited', 'pcntl_wifsignaled', 'pcntl_wifstopped', 'pcntl_wstopsig', 'pcntl_wtermsig', 'pfsockopen', 'posix_kill', 'posix_mkfifo', 'proc_close', 'proc_get_status', 'proc_nice', 'proc_open', 'proc_terminate', 'show_source', 'socket_bind', 'socket_create', 'socket_create_listen', 'socket_create_pair', 'symlink', 'syslog', 'system', 'virtual', ] php['disable_functions'] = ','.join(badfunctions) php['enable_dl'] = 'Off' php['expose_php'] = 'Off' if setupfw: php['auto_prepend_file'] = '/usr/local/afribin/apache/php/'+setupfw; print("expose_php =", php['expose_php']) print("disable_functions =", php['disable_functions']) print("enable_dl =", php['enable_dl']) print("auto_prepend_file =", php['auto_prepend_file']) if open_basedir: php['open_basedir'] = '/home/'+cpanel.user+'/public_html:/tmp:/usr/lib/php:/usr/local/lib/php:/usr/local/afribin/apache' print("open_basedir =", php['open_basedir']) if rebuild: print("Running /scripts/rebuildhttpdconf") os.system("/scripts/rebuildhttpdconf") return # File frobbers ... def redirect(title,url): stiff = dict(title=title,url=url) template=""" %(title)s <body> <h1>%(title)s</h1> <p><a href="%(url)s">Click here ...</a></p> </body> """ print("Writing a frame redirect to %(url)s with page title %(title)s" % stiff) index=open("index.html","w") index.write(template % stiff); index.close() os.system("chown --reference=. index.html; chmod 644 index.html") def mysql_sessiontable_repair(): list=glob.glob("/var/lib/mysql/*/*session*.MYI") for tablefile in list: bits=tablefile.split("/") table=bits[-1] database=bits[-2] table=table[:table.find(".MYI")]; print("mysqlcheck -r %s %s" % (database,table)) os.system( "mysqlcheck -r %s %s" % (database,table)) def safe(dir): print("Removing write acess for directories in %s" % dir) ocwd=os.getcwd() os.chdir(dir) os.system("find . -type d -print0 | xargs -0 chmod go-wr") print("Removing write acess for files in %s" % dir) os.system("find . -type f -print0 | xargs -0 chmod go-w") if has_fcgi(): print("Removing read acess for php files in %s" % dir) os.system(r"find . -type f -name \*.php\* -print0 | xargs -0 chmod go-r") os.chdir(ocwd) def do_images(dirname,no_act=False): ocwd=os.getcwd() os.chdir(dirname) if os.path.exists(".htaccess"): fd=open(".htaccess",'r') htaccess = fd.read() fd.close() if re.search('# Added ',htaccess): print("%s already protected" % dirname) os.chdir(ocwd) return False # already added if no_act: print("%s looks unprotected (permits script execution)" % dirname) os.chdir(ocwd) return False print("setting %s as restrictive images directory" % dirname) cmd = r""" touch .htaccess DATE=`date +%Y%m%d` if ! grep '^# Added' .htaccess ; then cat >> .htaccess < php_flag engine off Options -ExecCGI Order deny,allow Deny from all EOF fi chown root .htaccess 2>/dev/null chmod 644 .htaccess """ os.system(cmd) os.chdir(ocwd); return True def do_script(scriptname,params): "Pass parameters through to a script" paramstring = ' '.join(map(shellquote,params)) os.system('%s %s' % (os.path.dirname(frob())+'/'+scriptname, paramstring)) def do_fcgiauthheaders(params): "Pass FCGI auth paramters for basic auth" cpanel = CpanelData() fcgiauthfile = cpanel.get_apachecustomfile('fcgiauth.conf'); fd=open(fcgiauthfile,'w') fd.write("""# Pass Authorization header so Basic Auth can work # Date: %(date)s FcgidPassHeader Authorization """ % dict( date=time.strftime("%Y-%m-%d"),) ) print("# Wrote", fcgiauthfile) print("frob rebuild # will get this loaded up -- run it") def do_generosity(reason, processes=4): "Be generous" if not reason: print("No reason to be generous. Give me a reason.") return cpanel = CpanelData() generosityfile = cpanel.get_apachecustomfile('generosity.conf'); fd=open(generosityfile,'w') fd.write("""# More generous process limits # Date: %(date)s Reason: %(reason)s DefaultMaxClassProcessCount %(processes)d MaxRequestLen 90485760 FcgidMaxRequestLen 90485760 """ % dict( date=time.strftime("%Y-%m-%d"), reason=reason, processes=processes, ) ) print("# Wrote", generosityfile) print("frob rebuild # will get this loaded up") def modsecurity_onoff(status,reason=''): "Turn mod_security on or off" cpanel = CpanelData() cpanel.read_modsecinifile() if status: cpanel.modsec['SecRuleEngine'] = 'On' else: cpanel.modsec['SecRuleEngine'] = 'Off' cpanel.write_modsecinifile(reason=reason) def modsecurity_addrules(rules): "Add a list of bypass rules to mod_security" cpanel = CpanelData() cpanel.read_modsecinifile() changed = False for r in rules: try: cpanel.modsec['SecRuleRemoveById'].find(r) except: cpanel.modsec['SecRuleRemoveById'].append(r) changed = True if changed: cpanel.write_modsecinifile() return changed def modsecurity_autorule(): "Figure out what broke, and allow it" cpanel = CpanelData() cpanel.read_modsecinifile() error_log=open("/usr/local/apache/logs/error_log","r") violation={} violationline='' after_reload=False for line in error_log: if line.find("Graceful restart requested")>=0 or \ line.find("module security2_module is already loaded")>=0 or \ line.find("resuming normal operations")>=0: after_reload=False continue if not line.find(" ModSecurity: ")>=0: continue # [Tue Jul 07 06:55:25 2009] [error] [client 196.210.168.36] # ModSecurity: Access denied with code 500 (phase 2). Pattern match # "\\.php(3|4|5)?(\\?|&).*=(ht|f)tps?:/" at REQUEST_URI. [file # "/usr/local/afribin/apache/mod_security/apache2/rules.conf"] [line # "158"] [id "300040"] [rev "1"] [msg "Generic PHPcode injection # protection in URI"] [severity "CRITICAL"] [hostname "chns.co.za"] # [uri "/Home/index.php"] [unique_id "SlLVPcQh44YAADidNL4AAAAM"] bits=line.strip().split("] ["); tags=dict(hostname='',uri='',msg='') for bit in bits[1:]: try: (tag,value)=bit.split(" ",1) except: tag=bit value=bit tags[tag]=value.strip('"[] \t\r\n') after_reload=True if not cpanel.has_site(tags.get("hostname")): continue if "id" not in tags: continue tags['timestamp']=bit[0][1:] violation=tags violationline=line print(violation) if after_reload: print("This hit was after the latest reload:") print("Last violation: %(id)s (%(msg)s) at http://%(hostname)s%(uri)s" % tags) #for r in rules: # cpanel.modsec['SecRuleRemoveById'].append(tags['id']) #cpanel.write_modsecinifile() return modsecurity_addrules([tags['id']]) def cronfix(): '''Frob all the crontabls to have a random thingy''' for file in glob.glob("/var/spool/cron/*"): user=file.split("/")[:-1] if user == "root" : continue cronfile=open(file,"r"); crondata=[] modified=0 for line in cronfile: if line[:2]=='* ': line=('%d-59/15 '+line[2:]) % (random.randrange(14)+1 ) modified=1 crondata.append(line) cronfile.close() if modified: print("Wrote modified",file) cronfile=open(file,"w"); for line in crondata: cronfile.write(line) cronfile.close() def dcpumonview(): cmd = "/usr/local/cpanel/bin//dcpumonview | sed 's/<[^>]*>/ /g'" os.system(cmd) def domlogs(): sys.stderr.write("files modified in last 10 minutes\n") cmd = "find /usr/local/apache/domlogs -maxdepth 1 ! -name '*_*' -mmin -10" os.system(cmd) def killall(): global ipcs_remove_apache_cmd cmd=""" echo "# We are killing everything because we are panicky"; echo "# Killing httpd and php processes"; killall -9 httpd php php5 ; echo "# Making rdiff-backup, dovecot and exim nice" renice 19 -p `pidof -x rdiff-backup rrdtool` renice 15 -p `pidof -x dovecot imap courier-tcpd pop3 ` renice 12 -p `pidof -x exim` echo "# Restarting mysqld (to stop all processes)" service mysql restart; echo "# Killing apache IPC entries" %(ipcs_remove_apache_cmd)s echo "# Cleaning up /usr/local/apache/logs/fcgidsock" "rm" -r /usr/local/apache/logs/fcgidsock mkdir /usr/local/apache/logs/fcgidsock chown nobody /usr/local/apache/logs/fcgidsock chmod 700 /usr/local/apache/logs/fcgidsock echo "# Starting httpd again" service httpd start; echo "# Running DDOS termination" if pidof -x ddos.py | grep . ; then echo "Already running" else /usr/local/afribin/ddos.py -v fi """ % dict(ipcs_remove_apache_cmd=ipcs_remove_apache_cmd) os.system(cmd) def apachectlgraceful(build=True): if build: print("Rebuilding httpd.conf and reloading config in apache") cmd="/scripts/rebuildhttpdconf && /usr/local/apache/bin/apachectl graceful" else: print("Reloading config in apache") cmd="/usr/local/apache/bin/apachectl graceful" print(cmd) os.system(cmd) def takedown(target, taken_down='taken-down'): 'Password protect the directory' os.chdir(target) target=os.getcwd() if os.path.exists(taken_down): print(target+" taken down already") return n = target.find("/public_html") if n==-1: print(target+" does not include '/public_html'") return file_list=os.listdir('.') os.mkdir(taken_down,0o700) for file in file_list: os.rename(file,taken_down+'/'+file) open('.htaccess','w').write(""" RewriteEngine On RewriteRule ^ index.php """) open('index.php','w').write(""" Site unavailable

Site unvailable

This site is not available. """); os.system('chmod 711 . ; chown --reference=%(taken_down)s .' % {'taken_down': taken_down } ); def hacking(target): 'Password protect the directory' droppriv(target=target) os.chdir(target) target=os.getcwd() n = target.find("/public_html") if n==-1: print("Cannot find '/public_html' in "+target) return htpasswd_dir= target[:n]+"/.htpasswd"+target[n:]; params = { 'password': easypassword(), 'htaccess': target+"/.htaccess", 'htpasswd_dir': htpasswd_dir, 'htpasswd_file': htpasswd_dir+"/passwd", 'target': target, } cmd=""" if grep -i '^Require valid-user' '%(htaccess)s' > /dev/null 2>&1; then echo "Basic auth already set up in %(htaccess)s." else #echo n | cp -i '%(htaccess)s' '%(htaccess)s.orig' > /dev/null 2>&1 cat >> '%(htaccess)s' < /dev/null 2>&1 echo 'The password for %(target)s is user "user", password "%(password)s"' fi """ % params os.system(cmd) def remove_evil_lines(evils,filename): ''' Exclude evil lines ''' fc = open(filename,'r') filecontent=[] fixes=0 for line in fc: for evil in evils: if evil.search(line): line='' fixes+=1 break # ooh, efficiency filecontent.append(line) fc.close() if fixes == 0: return [] fc = open(filename,'w'); fc.writelines(filecontent) fc.close() return [ filename, ] def cleanup_htaccess(args): "Cleanup a htaccess injection" evils = ( re.compile(r'^(\s+$|\s{10}).*\n'), ) fixes=[] for filename in args: if filename.find('htaccess')>=0: fixes.extend( remove_evil_lines(evils,filename) ) return fixes def remove_evils(evils,filename): ''' Removing matching part of evil regex ''' fixes=0 fc = open(filename,'r') filecontent = fc.read() fc.close() for evil in evils: newcontent=evil.sub('',filecontent) if newcontent!=filecontent: fixes+=1 filecontent=newcontent if fixes: fc = open(filename,'w'); fc.write(filecontent); fc.close() return [ filename, ] return [] def cleanup_eval(args): "Cleanup an eval injection" evils = ( re.compile(r'<\?php\s+.*strto.*\[\d\].*\?>'), re.compile(r'<\?php\s+(eval|preg_replace)\([^;]*\);\s*\?>'), # php segment consisting entirely of eval or equivalent re.compile(r'(eval|echo)\(base64.*?;'), re.compile(r'<\?php\s*\?>\s*|/\*\*/ *',re.I), ); fixes=[] for filename in args: fixes.extend(remove_evils(evils,filename)) return fixes def cleanup_iframe(args): evils = ( re.compile(r'echo "";\s*',re.I), re.compile(r'',re.I), re.compile(r']*style=[^>]*?(display:\s*none|visibility:\s*hidden|left:\s*-\d\d\d\d).*?href="?http://.*?',re.I | re.S), re.compile(r']*style=[^>]*?(display:\s*none|visibility:\s*hidden|left:\s*-\d\d\d\d|(width|height):\s*0|overflow:\s*hidden).*?href="?http://.*?', re.I | re.S), re.compile(r']*style=[^>]*?(display:\s*none|visibility:\s*hidden|left:\s*-\d\d\d\d).*?href="?http://.*?', re.I | re.S), re.compile(r'', re.I | re.S), re.compile(r'', re.I | re.S), re.compile(r'', re.I | re.S), re.compile(r'', re.I | re.S), re.compile(r'\s*', re.I), re.compile(r'\s*',re.S), re.compile(r'\s*', re.I | re.S), re.compile(r'.*?(\%[0-9a-f][0-9a-f]){40}.*?\s*', re.I | re.S), re.compile(r'(/\*.*?\*/ )?try.*?document.body.appendChild.*?\s*', re.I), re.compile(r'.*?', re.I | re.S), re.compile(r'', re.I | re.S), ) fixes=[] for filename in args: fixes.extend(remove_evils(evils,filename)) return fixes def notify_ftp(args): cpanel = CpanelData() cmd=""" echo "%(domain)s security :" echo "" echo "Please notify the owner of %(domain)s that we have detected suspicious activity" echo "on their account %(user)s on `hostname`." echo "" echo "The client should check their desktop systems for spyware, and change their FTP" echo "password." echo "" echo "Here are the log messages:" echo "" cat /var/log/messages* | grep '%(vargs)s' echo """ settings = dict( vargs= "' '".join(args), user=cpanel.user, domain=cpanel.cpanel['DNS']) os.system( cmd % settings ) def dbaccess_openx(path): res={} res['file']= path res['app']= 'openx' f=open(path,'r') heading_re = re.compile(r"""^\[(.*)\]""") define_re = re.compile(r"""^\s*(\S+)\s*=(.+)""") part='' for line in f: h= heading_re.search(line) if h: part=h.group(1) if part=='database': d= define_re.search(line) if d: key=d.group(1).lower() value=d.group(2) if key=='host': res['host']=value.strip('"\'') if key=='username': res['user']=value.strip('"\'') if key=='password': res['pass']=value.strip('"\'') if key=='name': res['db']=value.strip('"\'') if 'db' in res: return res def dbaccess_wordpress(path): res={} res['prefix']= '' res['file']= path res['app']= 'wordpress' f=open(path,'r') # define('DB_NAME', 'idols4ci_wrdp1'); // The name of the database # define('DB_USER', 'idols4ci_wrdp1'); // Your MySQL username # define('DB_PASSWORD', 'rmcd'); // ...and password # define('DB_HOST', 'localhost'); // 99% chance you won't need to change this value define_re = re.compile(r"""define\s*\(\s*['"](.*?)['"]\s*,\s*['"](.*?)['"]\s*\)""") for line in f: # if line.find("define") >=0: sys.stdout.write( line) definebits=define_re.search(line.strip()) if definebits: res[definebits.group(1)]=definebits.group(2) f.close() if 'DB_NAME' not in res: return None res['db']= res.get('DB_NAME') res['user']= res.get('DB_USER') res['pass']= res.get('DB_PASSWORD') res['host']= res.get('DB_HOST') return res def dbaccess_moodle(path): res={} res['prefix']='' res['file']= path res['app']= 'moodle' f=open(path,'r') # $CFG->dbhost = 'localhost' ; # $CFG->dbname = 'learnnkv_mdle1' ; # $CFG->dbuser = 'learnnkv_mdle1' ; # $CFG->dbpass = '{eB9'; define_re = re.compile(r"""->\s*([a-z]+)\s*=\s*['"](.*?)['"]""") for line in f: # if line.find("define") >=0: sys.stdout.write( line) definebits=define_re.search(line.strip()) if definebits: res[definebits.group(1)]=definebits.group(2) f.close() if 'dbname' not in res: return None res['db']= res.get('dbname') res['user']= res.get('dbuser') res['pass']= res.get('dbpass') res['host']= res.get('dbhost') return res def dbaccess_phpbb(path): res={} res['prefix']='' res['file']= path res['app']= 'phpbb' #$dbhost = 'localhost'; #$dbname = 'sapanhnr_dtbase1'; #$dbuser = 'sapanhnr_devon'; #$dbpasswd = 'HWD5'; #$table_prefix = 'phpbb_'; f=open(path,'r') define_re = re.compile(r"""\$(db[a-z]+)\s*=\s*['"](.*?)['"]\s*;""") for line in f: # if line.find("define") >=0: sys.stdout.write( line) definebits=define_re.search(line) if definebits: res[definebits.group(1)]=definebits.group(2) f.close() if 'dbname' not in res: return None res['db']= res.get('dbname') res['user']= res.get('dbuser') res['pass']= res.get('dbpasswd') res['host']= res.get('dbhost') # print res return res def randompassword(length=9): 'Generate a 9 character password of sorts' import string import random return ''.join( random.Random().sample(string.ascii_letters+string.digits, length) ) def easypassword(length=8): 'Generate an easy 8 character password of sorts' import string import random word = ''.join( random.Random().sample(string.ascii_letters, length*4) ) word=word.lower() word=re.sub("([^aeoiu]{2})[^aeiou]+","\\1",word) word=re.sub("([aeoiu]{2})[aeiou]+","\\1",word) word=re.sub("([^aeoiu]{2})[^aeiou]+","\\1",word) word=word[:length] return word def joomlacrackformat(line): 'Format joomla passwords for john' u,p=tuple ( line.strip('\n').split('\t') ) i = p.find(':')<0 if i<0: return u+':$dynamic_0$'+p return u+':md5_gen(1)'+p.replace(":","$"); def do_crack(res): # show passwords for application for cracking found=0 if res['app'].startswith('joomla'): # Joomla 2.5: for sql in (""" # joomla 2.x SELECT users.username,users.password FROM jos_usergroups groups, jos_user_usergroup_map map, jos_users users WHERE groups.title LIKE 'Super%' AND map.group_id=groups.id and users.id=map.user_id """, """ # joomla 1.5 SELECT users.username,users.password FROM jos_core_acl_groups_aro_map map, jos_core_acl_aro aro, jos_users users WHERE map.aro_id=aro.id and aro.value=users.id and map.group_id=25; """,""" SELECT users.username,users.password FROM jos_users users order by id limit 5; """) : res['sql'] = sql.replace('jos_',res['prefix']) fd = os.popen('''%(mysql)s -N -B -e "%(sql)s" 2>/dev/null ''' % res , 'r'); for line in fd: print(joomlacrackformat(line)) found+=1 if found: break; if res['app'].startswith('wordpress'): cmd=r'''%(mysql)s -N -B -e "select concat(u.user_login,':',u.user_pass) from %(prefix)susers u left join %(prefix)susermeta m on u.ID=m.user_id and m.meta_key='%(prefix)scapabilities' where m.meta_value like '%%admin%%' " ''' % res if options.verbose: sys.stderr.write("%s\n" % cmd); os.system(cmd); def do_upgrade_wordpress(res): """ Upgrade wordpress by clobbering files in the current dir with the distribution version Does it work?? """ if res['app']!='wordpress': return # if not os.path.exists("wp-content"): return print("Running wordpress upgrade") droppriv(); res['url']='http://wordpress.org/latest.tar.gz'; # res['url']='http://answers.afrihost.com/updates/wordpress.php'; #res['url']='http://answers.afrihost.com/updates/wordpress.php'; os.system(""" umask 022 echo Downloading and extracting wordpress update ... 1>&2 wget -O - %(url)s | tar -zvx --strip-components=1 2>&1 | tail """ % res) do_wp(['core','update-db']); sys.stderr.write("Done\n") def do_certs(): os.system(r""" curl http://curl.haxx.se/ca/cacert.pem -o /etc/pki/tls/certs/ca-bundle.crt """) def runphpinstall(): os.system(r""" make -C /usr/local/afribin/src ; find /usr/local/apache/conf/userdata/std/2/ -name phpini.conf | while read x; do mv $x $x.DEPRECATED; done echo ' # Fastcgi configuration for PHP5 LoadModule fcgid_module modules/mod_fcgid.so MaxRequestsPerProcess 500 # 10Mb upload limit MaxRequestLen 90485760 AddHandler fcgid-script .php5 .php4 .php .php3 .php2 .phtml #FCGIWrapper /usr/local/cpanel/cgi-sys/php5 .php5 #FCGIWrapper /usr/local/cpanel/cgi-sys/php5 .php4 #FCGIWrapper /usr/local/cpanel/cgi-sys/php5 .php #FCGIWrapper /usr/local/cpanel/cgi-sys/php5 .php3 #FCGIWrapper /usr/local/cpanel/cgi-sys/php5 .php2 #FCGIWrapper /usr/local/cpanel/cgi-sys/php5 .phtml # End of autogenerated PHP configuration. FCGIWrapper "/usr/local/cpanel/cgi-sys/runphp /usr/local/cpanel/cgi-sys/php5" .php5 FCGIWrapper "/usr/local/cpanel/cgi-sys/runphp /usr/local/cpanel/cgi-sys/php5" .php4 FCGIWrapper "/usr/local/cpanel/cgi-sys/runphp /usr/local/cpanel/cgi-sys/php5" .php FCGIWrapper "/usr/local/cpanel/cgi-sys/runphp /usr/local/cpanel/cgi-sys/php5" .php3 FCGIWrapper "/usr/local/cpanel/cgi-sys/runphp /usr/local/cpanel/cgi-sys/php5" .php2 FCGIWrapper "/usr/local/cpanel/cgi-sys/runphp /usr/local/cpanel/cgi-sys/php5" .phtml IdleTimeout 60 ProcessLifeTime 600 MaxProcessCount 400 DefaultMinClassProcessCount 0 DefaultMaxClassProcessCount 3 IPCConnectTimeout 8 IPCCommTimeout 600 BusyTimeout 600 ' > /etc/httpd/conf/php.conf; # Fixup wrapper if it it php without parameters egrep 'exec /usr/bin/php ?("\$@")? *$' /usr/local/cpanel/cgi-sys/php5 && { perl -p -i -e 's{^exec /usr/bin/php *("\$\@")?}{exec /usr/bin/php "\$\@"}' /usr/local/cpanel/cgi-sys/php5 mkdir -p /var/cpanel/conf/apache/wrappers cp -a /usr/local/cpanel/cgi-sys/php5 /var/cpanel/conf/apache/wrappers/php5 } /scripts/rebuildhttpdconf && /usr/local/apache/bin/apachectl graceful ; """) def do_upgrade(): # Various upgrades res=dbaccess(show=False) if res and 'app' in res: do_upgrade_drupal(res) do_upgrade_mybb(res) do_upgrade_joomla(res,do_upgrade=True,do_password=False) do_upgrade_joomla16(res,do_upgrade=True); do_upgrade_wordpress(res) else: print("No application") def do_upgrade_drupal(res): if res['app']!='drupal': return do_drush(['up']) def do_upgrade_mybb(res): if res['version'] > '1.6' or res['app']!='MyBB': # return "%(app)s version %(version)s not supported by this MyBB upgrade" % res return droppriv() print("Upgrading to MyBB some other version") os.system(""" mkdir mybb_upgrade_files cd mybb_upgrade_files || exit curl -O http://www.mybb.com/download/139 unzip 139 [ -f ../images/logo.gif ] && rm Upload/images/logo.gif tar -C Upload -c . | tar -C .. -x cd .. rm -r mybb_upgrade_files """) def do_upgrade_joomla16(res,do_upgrade=True): # Joomla 1.6 to 2.2 upgrade to current or something 'upgrade joomla installation in the current dir' if res['app'] != 'joomla-2.x-series' or res['version'][:1] > '2': # print "%(app)s version %(version)s not supported by joomla 1.6+ upgrade" % res return configuration=res['file'] print("# application=%(app)s configfile=%(file)s prefix=%(prefix)s" % res) update_joomla_config_file(configuration, 'secret', easypassword()); res['url']="http://joomlacode.org/gf/download/frsrelease/17410/76012/Joomla_2.5.7-Stable-Full_Package.zip" res['url']="http://joomlacode.org/gf/download/frsrelease/17715/77262/Joomla_2.5.8-Stable-Full_Package.zip"; res['url']="http://joomlacode.org/gf/download/frsrelease/17968/78430/Joomla_2.5.9-Stable-Full_Package.zip"; res['url']="http://joomlacode.org/gf/download/frsrelease/18322/80354/Joomla_2.5.11-Stable-Full_Package.zip" res['url']="http://joomlacode.org/gf/download/frsrelease/18624/83477/Joomla_2.5.14-Stable-Full_Package.zip" res['url']="http://joomlacode.org/gf/download/frsrelease/18858/91472/Joomla_2.5.16-Stable-Full_Package.zip" res['url']="http://joomlacode.org/gf/download/frsrelease/19145/157516/Joomla_2.5.18-Stable-Full_Package.zip" res['url']="http://joomlacode.org/gf/download/frsrelease/19754/161033/Joomla_2.5.25-Stable-Full_Package.zip" res['url']="http://answers.afrihost.com/updates/Joomla_2.5.14-Stable-Full_Package.zip" res['url']="http://answers.afrihost.com/updates/Joomla_2.5.18-Stable-Full_Package.zip" res['url']="http://answers.afrihost.com/updates/Joomla_2.5.22-Stable-Full_Package.zip" res['url']="http://answers.afrihost.com/updates/Joomla_2.5.24-Stable-Full_Package.zip" res['url']="http://answers.afrihost.com/updates/Joomla_2.5.25-Stable-Full_Package.zip" res['url']="http://answers.afrihost.com/updates/Joomla_2.5.27-Stable-Full_Package.zip" res['url']="http://answers.afrihost.com/updates/Joomla_2.5.28-Stable-Full_Package.zip" # res['version']="2.5.11" if do_upgrade: res['version']=res['url'].split('_')[1].split('-')[0]; print("Downloading Joomla %(version)s" % res) res['downloadfilecache']=os.environ['HOME']+'/'+os.path.basename(res['url']); res['downloadfile']='./'+os.path.basename(res['url']); os.system(""" find %(downloadfilecache)s -mtime -1 | grep -q . || ( rm -f %(downloadfilecache)s wget -O %(downloadfilecache)s %(url)s touch %(downloadfilecache)s ) cp %(downloadfilecache)s %(downloadfile)s """ % res ); # Set non-runnable properties all over do_secure(res) print("Making autologin script") url = autologin_joomla16(show=False,timeout=7200) print("Dropping privileges") droppriv() print("Moving templates to templates.preserve in case you hacked the default template..." % res) os.system("[ -d templates.preserve ] || mv templates templates.preserve" % res) print("Extracting Joomla %(version)s" % res) os.system("unzip -o %(downloadfile)s 2>&1 | tail" % res) print("Removing installation directory") os.system("rm -rf installation") os.system("if [ -d templates -a -d templates.preserve ] ; then rm -rf templates ; mv templates.preserve templates; fi" % res) print("Random 1.6 to 2.x patches") os.system(r""" find templates -name \*.php -print0| xargs -0 egrep -lZ 'mainframe-|_component->user' | xargs -0 sed -i ' s/\$mainframe-/JFactory::getApplication()-/g; s/\$this->_component->user->/JFactory::getUser()->/g; ' 2>/dev/null FILE=plugins/system/jat3/jat3/core/joomla/modulehelper.php if [ -f $FILE ] ; then echo "jat3 update" perl -p -i -e 's/new JDatabaseQuery/\$db->getQuery(true)/g' $FILE fi """) print("") url_dbfix=url.split('/administrator/')[0]+'/administrator/?option=com_installer&view=database&task=database.fix'; print("If the automatic thingy doesn't work, you must run the extension manager database update MANUALLY: Extension Manager | Database | Fix now:") print("\x1b[31m"+url_dbfix+"\x1b[0m") print("Trying it:") os.system("""curl -s -L -c $HOME/.cj '%(url)s' | wc -l ; curl -s -L -b $HOME/.cj -c $HOME/.cj '%(url_dbfix)s' | wc -l ; rm $HOME/.cj """ % {'url':url,'url_dbfix':url_dbfix} ); # def php_str_escape(value): return value.replace('\\','\\\\').replace('\'','\\\''); #define('AUTH_KEY', 'f7sXx[2wbB@LBwlxS{A56y>+[DL9b3FJbvVZXz iq\\Ms}?(;_a#ypoR5\'3wFL~1X'); #define('SECURE_AUTH_KEY', '-\"r642fed3E1d>}MdGFlh@tR!QPU%/!S_6;CjO0}^>6T[DL9b3FJbvVZ+ OG2,,J{?w}G*b $LEds-JWc;0:&0!<=|=IB6BT2M);_'); # define('NONCE_KEY', 'D2/S-d>4UI&{Vw9P+<-/:s]$;E1( ~0FL{BR+-&/&AmjXL'); alphabet = '`^~<=>|_-,;:!?/.()[]{}@$*&#%+0123456789aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqrRsStTuUvVwWxXyYzZ' return ''.join(random.Random().sample(alphabet, 64)) def refresh_wordpress_keys(configuration_file): newvalues = { 'AUTH_KEY': generate_wordpress_key(), 'SECURE_AUTH_KEY': generate_wordpress_key(), 'LOGGED_IN_KEY': generate_wordpress_key(), 'NONCE_KEY': generate_wordpress_key(), } update_wordpress_configuration_file(configuration_file, newvalues) def update_wordpress_configuration_file(configuration_file, newvalues): "Write values to wordpress configuration" changed=[] fd=open(configuration_file,'r') lines=[] define_re = re.compile(r"define\('([A-Z_]+)',\s*'(.*)'\);\s*$"); for line in fd: m = define_re.search(line) if m and m.group(1) in list(newvalues.keys()): configuration_key=m.group(1) newvalue=newvalues[configuration_key]; line = "define('%s', '%s');\n" % (configuration_key, php_str_escape(newvalue)) changed.append(configuration_key) lines.append(line) fd.close() if len(changed): print("Updating "+configuration_file+" setting new "+(' '.join(changed))) st = os.stat(configuration_file); os.chmod(configuration_file,0o600); # unwriteable files are a pain ... fd=open(configuration_file,"w"); fd.writelines(lines) fd.close() os.chmod(configuration_file, st.st_mode & 0o777); # reset the mode as it was return changed # def update_joomla_config_file(configuration_file, configuration_key, newvalue): changed=0 fd=open(configuration_file,'r') lines=[] define_re = re.compile(r"""(var|public) \$([a-z]+)\s*=\s*['"](.*?)['"]\s*;""") for line in fd: m = define_re.search(line) if m and m.group(2)==configuration_key: replace = { 'vartype': m.group(1), 'key': m.group(2), 'value': newvalue, } line=" %(vartype)s $%(key)s = '%(value)s';\n" % replace; changed+=1 lines.append(line) fd.close() if changed: print("Setting new "+configuration_key+" in "+configuration_file) st = os.stat(configuration_file); os.chmod(configuration_file,0o600); # unwriteable files are a pain ... fd=open(configuration_file,"w"); fd.writelines(lines) fd.close() os.chmod(configuration_file, st.st_mode & 0o777); # reset the mode as it was return changed def do_upgrade_joomla(res,do_upgrade=True,do_password=True): 'upgrade joomla installation in the current dir' configuration=res['file'] if not res['app'] in ('joomla',): # print "%(app)s version %(version)s not supported by this joomla upgrade" % res return # Decide what we're putting in its place: res['newdbpassword'] = randompassword() res['adminpassword'] = randompassword() if do_password: os.system("""mysql --host=%(host)s --password='%(newdbpassword)s' --user=%(user)s %(db)s -e 'UPDATE %(prefix)susers SET password=md5("%(adminpassword)s") WHERE usertype LIKE "%%dministrator%%";' """ % res); print(("The new administrator password is %(adminpassword)s") % res) print("Old connection command: mysql --host=%(host)s --password='%(pass)s' --user=%(user)s %(db)s" % res) print("Setting new db password" % res) rc = os.system(""" mysql --host=%(host)s --password='%(pass)s' --user=%(user)s %(db)s -e 'SET password=PASSWORD("%(newdbpassword)s"); ' """ % res); if rc==0: print("Updating configuration to use new password, and setting new secret") update_joomla_config_file(configuration, 'password', res['newdbpassword']); update_joomla_config_file(configuration, 'secret', easypassword()); print(("The database password is now %(newdbpassword)s" ) % res) #res['com_fabrik']="http://answers.afrihost.com/updates/com_fabrik_2.1.1.zip" res['version']="1.5.26" res['url']="http://joomlacode.org/gf/download/frsrelease/12610/53421/Joomla_1.5.20-Stable-Full_Package.zip" res['url']="http://joomlacode.org/gf/download/frsrelease/12975/56086/Joomla_1.5.21-Stable-Full_Package.zip" res['url']="http://joomlacode.org/gf/download/frsrelease/13105/57240/Joomla_1.5.22-Stable-Full_Package.zip" res['url']="http://joomlacode.org/gf/download/frsrelease/14506/63460/Joomla_1.5.23-Stable-Full_Package.zip" res['url']="http://joomlacode.org/gf/download/frsrelease/15898/68914/Joomla_1.5.24-Stable-Full_Package.zip" res['url']="http://joomlacode.org/gf/download/frsrelease/16026/69665/Joomla_1.5.25-Stable-Full_Package.zip" res['url']="http://joomlacode.org/gf/download/frsrelease/16890/73390/Joomla_1.5.26-Stable-Full_Package.zip" res['url']="http://answers.afrihost.com/updates/Joomla_1.5.26-Stable-Full_Package.zip" if do_upgrade: if not res['version'].startswith('1.5.') or res['app']!='joomla': # print "%(app)s version %(version)s not supported by this joomla upgrade" % res return print("Downloading Joomla %(version)s" % res) res['downloadfilecache']=os.environ['HOME']+'/'+os.path.basename(res['url']); res['downloadfile']='./'+os.path.basename(res['url']); os.system(""" find %(downloadfilecache)s -mtime -1 | grep -q . || ( rm -f %(downloadfilecache)s wget -O %(downloadfilecache)s %(url)s touch %(downloadfilecache)s ) cp %(downloadfilecache)s %(downloadfile)s """ % res ); print("Dropping privileges") droppriv() print("Moving templates to templates.preserve in case you hacked the default template..." % res) os.system("[ -d templates.preserve ] || mv templates templates.preserve" % res) print("Extracting Joomla %(version)s" % res) os.system("unzip -o %(downloadfile)s 2>&1 | tail" % res) print("Removing installation directory and zip file") os.system("if [ -d templates -a -d templates.preserve ] ; then rm -rf templates ; mv templates.preserve templates; fi" % res) os.system("rm -rf installation %(downloadfile)s" % res) # Upgrade vulnerable stuff too # com_fabrik does not like being upgraded: #if os.path.exists('components/com_fabrik'): # print "Downloading com_fabrik %(version)s" % res # os.system("wget --continue %(com_fabrik)s -O com_fabrik.zip; unzip -o com_fabrik.zip" % res) ## We don't do this, because we dropped privileges #print "Changing ownership of extracted zip files (this won't work on BSD - please do it manually!)" #os.system("find . -user root -print0 | xargs -0 chown --reference=.") if do_upgrade: print("We have updated the Joomla installation to version %(version)s" % res) if do_password: print(("The new administrator password is %(adminpassword)s") % res) print(("The database access password is now %(newdbpassword)s for phpmyadmin" ) % res) def dbaccess_joomla(path): res={} res['prefix']= '' res['file']= path res['app']= 'joomla' #var $password = 'Scr'; #var $host = 'localhost'; #var $user = 'fairlkrj_gilan'; #var $db = 'fairlkrj_fairleads'; f=open(path,'r') define_re = re.compile(r"""var \$([a-z]+)\s*=\s*['"](.*?)['"]\s*;""") for line in f: # if line.find("define") >=0: sys.stdout.write( line) definebits=define_re.search(line) if definebits: res[definebits.group(1)]=definebits.group(2) f.close() if 'db' not in res: return None res['prefix']= res.get('dbprefix','') res['db']= res.get('db') res['user']= res.get('user') res['pass']= res.get('password') res['host']= res.get('host') res['version'] = version_of_joomla() return res def parsephpfile(filename,res,already={}): # print "filename =",filename if (not os.path.exists(filename)): return; f=open(filename,'r'); include_re = re.compile(r"""(include|require|include_once|require_once)\s*\(['"](.*?)['"]\s*\)""") assign_re = re.compile(r"""(\$\S+?)\s*=\s*['"](.*?)['"]""") connect_re = re.compile(r"""mysql_p?connect\s*\(\s*(\S+)\s*,\s*(\S+)\s*,\s*(\S+)\s*\)""") select_re = re.compile(r"""mysql_select_db\s*\(\s*(\S+?),""") for line in f: m=include_re.search(line) if m: include_file = m.group(2) if include_file not in already: already[include_file]=True parsephpfile(include_file,res,already=already) m=assign_re.search(line) if m: res[m.group(1)]=m.group(2) m=connect_re.search(line) if m: res['host']=m.group(1).strip('"\'') res['user']=m.group(2).strip('"\'') res['pass']=m.group(3).strip('"\'') if res['host'] in res: res['host']=res[res['host']] if res['user'] in res: res['user']=res[res['user']] if res['pass'] in res: res['pass']=res[res['pass']] res['file']= filename m=select_re.search(line) if m: res['db']=m.group(1).strip('"\''); if res['db'] in res: res['db']=res[res['db']] f.close() def dbaccess_guess(path): 'parse a php app to see what its database is' res={} res['prefix']= '' res['app']= 'some application' res['file']= path parsephpfile(path,res) # print "res.keys() =", ",".join(res.keys()) return res def urldecode(data): import urllib.request, urllib.parse, urllib.error data= urllib.parse.unquote_plus(data) return data def dbaccess_other(dummyfilename,verbose=False): applications = [ { 'app': 'drupal', 'configfile': 'sites/default/settings.php', 'assignments': [ re.compile(r"^\s*'([^']+)'\s*=>\s*'([^']*)'"), ], 'map': { 'db': 'database', 'user': 'username', 'pass': 'password', 'host': 'host', 'prefix': 'prefix', }, #'debug': True }, { 'app': 'drupal', 'configfile': 'sites/default/settings.php', 'regexlist': { re.compile(r'''^\s*\$.*= *'mysqli?://(\S+):(\S+)@(\S+)/(\S+)['"]'''): [ 'user','pass','host','db' ], re.compile(r"\$db_prefix\s*=\s*'(.*?)'") : ['prefix'], }, 'map': { 'db': 'db', 'user': 'user', 'pass': 'pass', 'host': 'host'}, 'pass_postprocess': urldecode, }, { 'app': 'joomla-2.x-series', 'configfile': 'configuration.php', 'assignments': [ re.compile(r"""public \$([a-z]+)\s*=\s*'(.*?)'\s*;""") ], 'map': { 'db': 'db', 'user': 'user', 'pass': 'password', 'host': 'host', 'prefix': 'dbprefix'}, }, { 'app': 'joomla-1.ancient', 'configfile': 'configuration.php', 'assignments': [re.compile(r"""mosConfig_(\S+)\s*=\s*["'](\S+?)["']""")], 'map': { 'db':'db', 'user':'user', 'pass':'password', 'host':'host', 'prefix': 'dbprefix', }, }, { 'app': 'vtiger', 'configfile': 'config.inc.php', 'assignments': [ re.compile(r'''\$dbconfig\['([a-z_]+)'\] *= *'(.*?)';'''), ] , 'map': { 'db': 'db_name', 'user': 'db_username', 'pass': 'db_password', 'host': 'db_server'}, }, { 'app': 'b2evolution', 'configfile': 'conf/_basic_config.php', 'assignments': [ re.compile(r"'([^']+)' *=> *'([^']+)'"), ], 'map': { 'db': 'name', 'user': 'user', 'pass': 'password', 'host': 'host'}, 'regexlist': { re.compile(r"\$tableprefix\s*=\s*'(.*?)'") : ['prefix'], }, }, { 'app': 'symfony', 'configfile': 'config/databases.yml', 'regexlist': { re.compile(r"'mysql:host=(.*?);dbname=(.*?)'") : ['host','db'], }, 'assignments': [ re.compile(r""" (\S+): (\S+)""") ], 'map': { 'user': 'username', 'pass': 'password' } , }, { 'app': 'cubecart', 'configfile': 'includes/global.inc.php', 'assignments': [ re.compile(r"\['([^']+)'\s*\] *= *'([^']*)'"), ], 'map': { 'db': 'dbdatabase', 'user': 'dbusername', 'pass': 'dbpassword', 'host': 'dbhost', 'prefix': 'dbprefix'}, #'debug': True, }, # $CONFIG->dbuser = 'whereduw_elgguse'; # $CONFIG->dbpass = 'bhhgbk13463'; # $CONFIG->dbname = 'whereduw_elggdb'; # $CONFIG->dbhost = 'localhost'; # $CONFIG->dbprefix = 'elgg_'; { 'app': 'elgg', 'configfile': [ 'engine/settings.php', ], 'assignments': [ re.compile(r"\$CONFIG->(\w+) = '(.*?)';"), ], 'map': { 'db': 'dbname', 'user': 'dbuser', 'pass': 'dbpass', 'host': 'dbhost', 'prefix': 'dbprefix'}, }, { 'app': 'indexhibit.org', 'configfile': [ 'ndxz-studio/config/config.php', 'config/config.php', ], 'assignments': [ re.compile(r"\['([^']+)'\] *= *'([^']*)'"), ], 'map': { 'db': 'db', 'user': 'user', 'pass': 'pass', 'host': 'host'}, }, { 'app': 'webSpell.org', 'configfile': '_mysql.php', 'assignments': [ re.compile(r'''\$([a-z]+)\s*=\s*['"]([^'"]+)['"]'''), ], 'map': { 'db': 'db', 'user': 'user', 'pass': 'pwd', 'host': 'host'}, }, { 'app': 'e107', #'debug': True, 'configfile': 'e107_config.php', 'assignments': [ re.compile(r'''\$(mySQL[a-z]+)\s*=\s*['"]([^'"]+)['"]'''), ], 'map': { 'db': 'mySQLdefaultdb', 'user': 'mySQLuser', 'pass': 'mySQLpassword', 'host': 'mySQLserver', 'prefix': 'mySQLprefix'}, }, { 'app': 'hesk helpdesk', 'configfile': 'hesk_settings.inc.php', 'assignments': [ re.compile(r"\['([^']+)'\] *= *'([^']+)'"), ], 'map': { 'db': 'db_name', 'user': 'db_user', 'pass': 'db_pass', 'host': 'db_host'}, }, { 'app': 'eticketsupport.com', 'configfile': 'settings.php', 'assignments': [re.compile(r"""\$(\S+)\s*=\s*["']?(\S+?)["']?;""")], 'map': { 'db': 'db_name', 'user': 'db_user', 'pass': 'db_pass', 'host': 'db_host', 'prefix': 'db_table_prefix', 'version': 'version'} }, { # #pixelpost_db_host = "localhost"; // database host, often "localhost" # #pixelpost_db_user = "gavinjwd_pp"; // database user # #pixelpost_db_pass = "A5S"; // database user password # #pixelpost_db_pixelpost = "gavinjwd_pixelpost"; // database 'app': 'pixelpost.org', 'configfile': 'includes/pixelpost.php', 'assignments': [re.compile(r"""\$(\S+)\s*=\s*["']?(\S+?)["']?;""")], 'map': { 'db': 'pixelpost_db_pixelpost', 'user': 'pixelpost_db_user', 'pass': 'pixelpost_db_pass', 'host': 'pixelpost_db_host', } }, { 'app': 'phplist', 'configfile': 'config/config.php', 'assignments': [ re.compile(r"\['([^']+)'\] *= *'([^']+)'"), ], 'map': { 'db': 'database_name', 'user': 'database_user', 'pass': 'database_password', 'host': 'database_host'}, }, { 'app': 'simbarsk', 'configfile': 'config.local.php', 'assignments': [ re.compile(r"\['([^']+)'\] *= *'([^']+)'"), ], 'map': { 'db': 'db_host', 'user': 'db_user', 'pass': 'db_password', 'host': 'db_host'}, }, { 'app': 'cake', 'configfile': 'config/database.php', 'assignments': [ re.compile(r"'([^']+)' *=> *'([^']+)'"), ], 'map': { 'db': 'database', 'user': 'login', 'pass': 'password', 'host': 'host'}, }, # DB["server"] = 'localhost'; # DB["user"] = 'propemkx_qafaq'; # DB["password"] = '!+X61$GI(xq'; # DB["db"] = 'propemkx_faq'; # DB["prefix"] = ''; # DB["type"] = 'mysql'; { 'app': 'phpmyfaq', 'configfile': 'config/database.php', 'assignments': [ re.compile(r'''\$DB\["([a-z]+)"\] *= *'(.*?)';'''), ] , 'map': { 'db': 'db', 'user': 'user', 'pass': 'password', 'host': 'server', 'prefix': 'prefix'}, }, { 'app': 'bidbutler', 'configfile': 'config/config.php', 'assignments': [ re.compile(r"'([^']+)' *=> *'([^']+)'"), ], 'map': { 'db': 'database', 'user': 'login', 'pass': 'password', 'host': 'host'}, }, { 'app': 'SMF forum', 'configfile': 'Settings.php', 'assignments': [re.compile(r"""\$(db_\S+)\s*=\s*["']?(\S+?)["']?;""")], 'map': { 'db':'db_name', 'user':'db_user', 'pass':'db_passwd', 'host':'db_server'}, }, { 'app': 'kayako', 'configfile': 'config/config.php', 'assignments': [re.compile(r'''^\$_DB\["([a-z]*)"\] *= *"(.*?)"''')], 'map': { 'db':'name', 'user':'username', 'pass':'password', 'host':'hostname'}, }, { 'app': 'expressionengine', 'configfile': 'siteadmin/expressionengine/config/database.php', # $db['expressionengine']['hostname'] = "localhost"; 'assignments': [re.compile(r'''^\$db\[['"a-z]*\]\[['"]([a-z]*)['"]\] *= *['"](.*?)['"]''')], 'map': { 'db':'database', 'user':'username', 'pass':'password', 'host':'hostname'}, }, { 'app': 'MODx', 'configfile': [ 'core/config/config.inc.php', 'manager/includes/config.inc.php', ], 'assignments': [re.compile(r'''\$([a-z_]+)\s+=\s+'`?(.*?)`?';''')], 'map': { 'db':'dbase', 'user':'database_user', 'pass':'database_password', 'host':'database_server', 'prefix': 'table_prefix', }, }, { 'app': 'lifetype', 'configfile': 'config/config.properties.php', # $config['db_host'] = 'localhost'; 'assignments': [re.compile(r"""\[['"]([^'"]+)['"]\s*\]\s*=\s*["']?(\S+?)["']?;""")], 'map': { 'db':'db_database', 'user':'db_username', 'pass':'db_password', 'host':'db_host'}, }, { 'app': 'Comdev One Admin', 'configfile': 'config.php', 'assignments': [re.compile(r"""\[['"]([^'"]+)['"]\s*\]\s*=\s*["']?(\S+?)["']?;""")], 'map': { 'db':'used', 'user':'user', 'pass':'pass', 'host':'host'}, }, { 'app': 'vbullettin', 'configfile': 'includes/config.php', 'assignments': [re.compile(r"""\[['"]([^'"]+)['"]\s*\]\s*=\s*["']?(\S+?)["']?;""")], 'map': { 'db':'dbname', 'user':'username', 'pass':'password', 'host':'servername'}, }, { 'app': 'sugarcrm', 'configfile': 'config.php', 'assignments': [ re.compile(r"'([^']+)' *=> *'([^']+)'"), ], 'map': { 'db':'db_name', 'user':'db_user_name', 'pass':'db_password', 'host':'db_host_name'}, }, { 'app': 'interspire email marketer', 'configfile': 'admin/includes/config.php', 'assignments': [re.compile(r"""define\s*\(\s*['"](.*?)['"]\s*,\s*['"](.*?)['"]\s*\)""")], 'map': { 'db':'SENDSTUDIO_DATABASE_NAME', 'user':'SENDSTUDIO_DATABASE_USER', 'pass':'SENDSTUDIO_DATABASE_PASS', 'host':'SENDSTUDIO_DATABASE_HOST', 'prefix': 'SENDSTUDIO_TABLEPREFIX'}, }, { 'app': 'interspire shopping cart', 'configfile': 'config/config.php', 'assignments': [re.compile(r"""\$GLOBALS\['ISC_CFG'\]\["(\S+)"\] *= *'(\S+)'""")], 'map': { 'db':'dbDatabase', 'user':'dbUser', 'pass':'dbPass', 'host':'dbServer', 'prefix': 'tablePrefix'}, # $GLOBALS['ISC_CFG']["dbUser"] = 'beechwor_jodieg'; # $GLOBALS['ISC_CFG']["dbPass"] = 'e2otjodf'; # $GLOBALS['ISC_CFG']["dbDatabase"] = 'beechwor_shop2'; # $GLOBALS['ISC_CFG']["tablePrefix"] = 'isc_'; }, { 'app': 'oxwall', 'configfile': 'ow_includes/config.php', 'assignments': [re.compile(r"""define\s*\(\s*['"](.*?)['"]\s*,\s*['"](.*?)['"]\s*\)""")], 'map': { 'db':'OW_DB_NAME', 'user':'OW_DB_USER', 'pass':'OW_DB_PASSWORD', 'host':'OW_DB_HOST', 'prefix': 'OW_DB_PREFIX'}, }, { # define('DB_HOST', 'localhost'); // database host # define('DB_USER', 'thewenwt_kenneth'); // username # define('DB_PASS', 'k3nn3th'); // password # define('DB_NAME', 'thewenwt_onlineshop'); // database name 'app': 'wordpress', 'configfile': 'wp-config.php', 'assignments': [re.compile(r"""^\s*define\s*\(\s*['"](.*?)['"]\s*,\s*['"](.*?)['"]\s*\)""")], 'map': { 'db':'DB_NAME', 'user':'DB_USER', 'pass':'DB_PASSWORD', 'host':'DB_HOST'}, 'regexlist': { re.compile(r"\$table_prefix\s*=\s*'(.*?)'") : ['prefix'], }, }, { 'app': 'CodeIgniter', 'configfile': 'config.php', 'assignments': [re.compile(r"""engine_config\['db'\]\[\'(.*?)'\]\s*=\s*'(.*?)';""")], 'map': { 'db':'database', 'user':'username', 'pass':'password', 'host':'hostname'}, }, { # define('DB_DRIVER', 'mysql'); # define('DB_HOSTNAME', 'localhost'); # define('DB_USERNAME', 'phenoelc_cube'); # define('DB_PASSWORD', 'ant1fr33z3'); # define('DB_DATABASE', 'phenoelc_cubecart'); # define('DB_PREFIX', 'phenoelc_'); 'app': 'OpenCart', 'configfile': 'config.php', 'assignments': [re.compile(r"""define\s*\(\s*['"](.*?)['"]\s*,\s*['"](.*?)['"]\s*\)""")], 'map': { 'db':'DB_DATABASE', 'user':'DB_USERNAME', 'pass':'DB_PASSWORD', 'host':'DB_HOSTNAME', 'prefix': 'DB_PREFIX'}, }, { # define('_DB_NAME_', 'pcgatlfy_pcg'); # define('_MYSQL_ENGINE_', 'InnoDB'); # define('_DB_SERVER_', 'localhost'); # define('_DB_USER_', 'pcgatlfy_pcg'); # define('_DB_PREFIX_', 'ps_'); # define('_DB_PASSWD_', 'nUTh'); 'app': 'prestashop', 'configfile': 'config/settings.inc.php', 'assignments': [re.compile(r"""define\s*\(\s*['"](.*?)['"]\s*,\s*['"](.*?)['"]\s*\)""")], 'map': { 'db':'_DB_NAME_', 'user':'_DB_USER_', 'pass':'_DB_PASSWD_', 'host':'_DB_SERVER_', 'prefix': '_DB_PREFIX_'}, }, { # define('DB_HOST', 'localhost'); // database host # define('DB_USER', 'thewenwt_kenneth'); // username # define('DB_PASS', 'k3nn3th'); // password # define('DB_NAME', 'thewenwt_onlineshop'); // database name 'app': 'shop-script free', 'configfile': 'cfg/connect.inc.php', 'assignments': [re.compile(r"""define\s*\(\s*['"](.*?)['"]\s*,\s*['"](.*?)['"]\s*\)""")], 'map': { 'db':'DB_NAME', 'user':'DB_USER', 'pass':'DB_PASS', 'host':'DB_HOST'}, }, { # define('DB_TYPE', 'mysql'); # define('DB_HOST', 'localhost'); # define('DB_USERNAME', 'ssidhvcc_admin'); # define('DB_PASSWORD', 'pw4admin'); # define('DB_NAME', 'ssidhvcc_ssinov2010'); 'app': 'bake', 'configfile': 'config.php', 'assignments': [re.compile(r"""define\s*\(\s*['"](.*?)['"]\s*,\s*['"](.*?)['"]\s*\)""")], 'map': { 'db':'DB_NAME', 'user':'DB_USERNAME', 'pass':'DB_PASSWORD', 'host':'DB_HOST'}, }, { 'app': 'magento', 'configfile': 'app/etc/local.xml', 'assignments': [re.compile(r"""<([a-z]+)>> /etc/sysctl.conf done echo '# Setting sysctls' sysctl -p echo '# Values after frob' sysctl -a | grep panic """) # kernel.modules_disabled was here ... it should probably be used in the firewall setup .. def droppriv(target=".", quiet=False): "Drop root privileges" import posix,pwd stat = os.stat(target) uid = stat.st_uid if os.getuid==uid: return # job's done already # gid = stat.st_gid gid = pwd.getpwuid(uid).pw_gid try: posix.setgroups([]) posix.setgid(gid) posix.setuid(uid) posix.seteuid(uid) except: if not quiet: sys.stderr.write("We are already unprivileged :)\n") if uid==0: raise Exception.create("Oops - we dropped privilege to root: that's probably wrong"); os.environ['HOME'] = pwd.getpwuid(uid).pw_dir os.environ['PS1'] = '\\u@\\h [\\w]\\$ ' return os.environ['HOME'] def list_myips(): if_re = re.compile(r'^([^ :]+)'); inet_re = re.compile(r'inet addr:(\d\S+)') ifconfig=os.popen("ifconfig",'r') d={'eth': 'eth0'} for line in ifconfig: m = if_re.search(line) if m: d['eth']=m.group(1) m = inet_re.search(line) if m: d['ip']=m.group(1) yield d def arping(): print("# Sending arping -U for each configured IP address shown by ifconfig:") for d in list_myips(): if d['ip'].startswith('127.') or d['eth']=='lo': continue cmd = "arping -U -I %(eth)s -c 2 %(ip)s" % d print(cmd) os.system(cmd) # root@foreverresortssouthafrica [~]# ifconfig | grep -o 'addr:[^ ]\+' | cut -f 2 -d : | while read x; do arping -U $x -I eth1 -c 3; done def mv(src,dst): params = dict(src=src,dst=dst,nice='ionice -c 3 nice -n 19') if not os.path.exists(src): print("# ERROR: source does not exist") return if os.path.exists(dst): print("# ERROR: destination already exists") return if src==dst: print("# ERROR: src==dst: crazy") return os.system(""" [ -d '%(src)s' ] || { echo "# ERROR: %(src)s is not a directory" exit } echo "# INFO: Making dest dir %(dst)s" mkdir -p %(dst)s echo "# INFO: Doing nice initial copy to %(dst)s" %(nice)s cp -au %(src)s/. %(dst)s/. echo "# INFO: Doing nice rsync --delete to %(dst)s" %(nice)s rsync -a --delete %(src)s/. %(dst)s/. echo "# INFO: Renaming source, and making link to dest" mv %(src)s %(src)s.orig ln -s %(dst)s %(src)s chown -h --reference=%(dst)s %(src)s echo "# INFO: Sleeping ..." sleep 15 echo "# INFO: Doing additional rsync without nice or --delete (possible duplicate mails)" rsync -a %(src)s.orig/. %(dst)s/. echo "# TODO: Check that everything is working, and remove the source like this:" echo "%(nice)s rm -r '%(src)s.orig'" """ % params) def do_dbaccess(operation,args): res=dbaccess() shellargs='' for arg in args: shellargs+= " '" + (arg.replace("'",r"'\''")) + "'" if not res: print("Unknown application") return if operation=='mysql': os.system(res['mysql']+shellargs) elif operation=='mysqldump': os.system(res['mysqldump']+shellargs) elif operation=='grant': fd=os.popen("mysql",'w') fd.write(res['grant']) fd.close() def do_dbpass(operation,args): res=dbaccess() res['newdbpassword']=randompassword(12) print("Rewriting db password from %(pass)s to %(newdbpassword)s" % res) # Change database password fd = os.popen(res['mysql'],'w') fd.write('SET PASSWORD=PASSWORD("%(newdbpassword)s");' % res) fd.close() # Rewrite the config file fd=open(res['file'],'r') configfile=fd.read() fd.close() configfile=configfile.replace(res['pass'],res['newdbpassword']) fd=open(res['file'],'w') fd.write(configfile) fd.close() def do_nocomment(): # Remove evil components res=dbaccess() cmd=''; if res['app'].startswith('joomla'): # cmd = """%(mysql)s -e 'update %(prefix)scomponents set enabled=0 where `option` in ("com_fabrik","com_remository");' """ % res cmd = """ echo ' truncate table %(prefix)srem_review; # delete all comments alter table %(prefix)srem_review modify id int(11); # change from autoincrement to int insert into %(prefix)srem_review (id) values (0); # add a bogus non-comment comment truncate table %(prefix)sk2_comments; # delete all comments alter table %(prefix)sk2_comments modify id int(11); # change from autoincrement to int insert into %(prefix)sk2_comments (id) values (0); # add a bogus non-comment comment drop table %(prefix)skunena_messages_text; # delete all comments and break the application: it cannot even index its primary key truncate table %(prefix)sudjacomments; # delete all comments alter table %(prefix)sudjacomments modify id int(11); # change from autoincrement to int insert into %(prefix)sudjacomments (id) values (0); # add a bogus non-comment comment truncate table %(prefix)skunena_messages; # delete all comments alter table %(prefix)skunena_messages modify id int(11); # change from autoincrement to int insert into %(prefix)skunena_messages (id) values (0); # add a bogus non-comment comment ' | %(mysql)s -f """ % res if res['app'].startswith('MODx'): # cmd = """%(mysql)s -e 'update %(prefix)scomponents set enabled=0 where `option` in ("com_fabrik","com_remository");' """ % res cmd = """ echo ' truncate table %(prefix)sjot_content ; alter table %(prefix)sjot_content change column id `id` int(10) NOT NULL; insert into %(prefix)sjot_content (id) values (0); # add a bogus non-comment comment ' | %(mysql)s -f """ % res if cmd: print(cmd) os.system(cmd); print("# Done") else: print("Eish, nocomment not available for this application") def php_version(): # Returns 0x050317 for php 5.3.17 fd = os.popen('php -v','r') for line in fd: m = re.search(r'(\d+)\.(\d+)\.(\d+)', line) if m: return int(m.group(1)) * 0x10000 + \ int(m.group(2)) * 0x00100 + \ int(m.group(3)) * 0x00001 ; m = re.search(r'(\d+)\.(\d+)', line) if m: return int(m.group(1)) * 0x10000 + \ int(m.group(2)) * 0x00100 ; return 0 def do_autocleanup(parameters): os.system(r""" pwd | grep -q public_html || exit 1 /usr/local/afribin/maintenance/linkfarmscan.py -N /dev/null --limit 999999 . -n php -l | /usr/local/afribin/quarantine """) def do_jce(): "Fix com_jce hack with do_secure and clean up common hacks" do_secure() os.system(r""" cd $( echo $PWD | sed 's/public_html.*/public_html/' ) find ../.htaccess . -name .htaccess -print0| xargs -0 perl -p -i -e 'if (m/^\s+$|^\s{10}/) { $_=""; }' find images -name \*.php\* -print0|xargs -0 %(frob)s disable find . -type l -print0|xargs -0 rm find . -name \*.old.php -print0|xargs -0 quarantine find . -name x.txt -print0| xargs -0 quarantine for FILENAME in \ components/com_user/views/reset/mod.php \ components/com_banners/models/mod.php \ components/com_remository_files \ media/sok.php \ media/mod.php \ _vti_pvt/service.pwd \ modules/mod.php \ images/stories/pageinfo.php \ images/stories/error_log \ images/stories/ll.php \ images/stories/semi.php \ images/stories/kabe.php ; do [ -f "$FILENAME" ] || continue quarantine "$FILENAME" done """ % dict(frob=frob())); class Slurp: def __init__(self): self.data=[] def write(self,line): self.data.append(line) def __repr__(self): return ''.join(self.data); def do_john(res, no_act=False, verbose=0,guesses=[]): path = os.path.dirname(frob())+'/andrew-scripts'; if not os.path.exists(path): path='/usr/local/afribin/andrew-scripts'; sys.path.append(path); import john slurp=Slurp() john.guesses.extend(guesses) john.find_vulnerable_installations(user=res['db'], stdout=slurp, res=res, verbose=verbose); for line in slurp.data: if verbose: sys.stdout.write(line) if not line.startswith("#"): continue print("Vulnerable password: "+line.strip()) if not no_act: fd=os.popen('/bin/bash -v','w') fd.writelines(slurp.data); fd.close() def check_jce(no_act=False): # Joomla: Remove jce plugin if not os.path.exists('plugins/editors/jce.xml'): return xml = open('plugins/editors/jce.xml','r').read() version=re.search(r'(.*)',xml) if not version: return version=version.group(1) if version > '2.2.': return if no_act: print("We found a vulnerable installation of JCE (joomla content editor), version %s" % version) return print("Removing jce joomla content editor version %s" % version) os.system("rm -r plugins/editors/jce{,.xml,.php} administrator/components/com_jce/jce.xml"); print("Installing files of another random version of jce") os.system("curl http://answers.afrihost.com/updates/jce-files.tar.gz | %s su -- tar -zx ." % frob()); # droppriv after fork def do_secure(res=None, no_act=False, quick=False): # Remove evil components if not res: res=dbaccess(show=False, unknown_application='an unknown application') if no_act: print("Checking application security for %(app)s %(version)s:" % res) else: print("Setting up application security for %(app)s %(version)s:" % res) if res['app'].startswith('joomla'): # Joomla: Evil / stupid components res['sql']='select distinct `option` from %(prefix)scomponents where enabled=1;' % res res['sql']=res['sql'].replace("'","'\\''") cmd = """%(mysql)s -Ne '%(sql)s' 2>/dev/null """ % res p = os.popen(cmd,'r') for bad_component in p: res['component']=bad_component.strip() if res['component'] not in ('com_fabrik','com_remository',): continue print("Vulnerable %(app)s component %(component)s found" % res) if no_act: continue print("Disabling %(app)s component %(component)s by setting 'enabled' to false" % res) res['sql']='update %(prefix)scomponents set enabled=0 where `option` = "%(component)s";' % res res['sql']=res['sql'].replace("'","'\\''") cmd = """%(mysql)s -Ne '%(sql)s' """ % res print(cmd) os.system(cmd); if os.path.exists('components/com_smartformer/smartformer.php'): code = open('components/com_smartformer/smartformer.php','r').read() m = re.search(r'@version (\d\S+)',code) if m and m.group(1) < '2.4.1': print("Upgrading com_smartformer version "+m.group(1)) upgrade = r'curl http://answers.afrihost.com/updates/com_smartformer-files.tar.gz | %s su -- tar -xz' % sys.argv[0]; os.system(upgrade); check_jce(no_act) # Protect image directories writeable_dirs = [ 'images','uploads','media','wp-content/uploads', 'upload','cache','tmp','logs','wp-content/flagallery', 'components/com_smartformer/files', 'sobi2_downloads','phocadownload', 'components/com_jnews/includes/openflashchart/tmp-upload-images', ] for imagedir in writeable_dirs: if os.path.exists(imagedir): do_images(imagedir,no_act) # Check for guessable passwords if 'db' in res: do_john(res, no_act=no_act) # Find # TODO: ./wp-content/themes/*/framework/_scripts/valums_uploader/php.php p=os.popen(r'find . -iname \*thumb\*.php -o -iname \*upload\*.php -o -iname \*process-form.php','r') for line in p: fn=line.strip('\n'); if not os.path.exists(fn): continue if not os.path.isfile(fn): continue basename=line.split('/')[-1].lower() files=[fn, ] if basename.find('thumb')>=0: do_patch_timthumb(files ,no_act=no_act); elif basename.find('upload')>=0 or basename.find('form')>=0: do_patch_upload(files,no_act=no_act); # done if not no_act and res['app'].startswith('wordpress') and php_version() >= 0x050300: print("%(app)s: setting new secrets" % res) refresh_wordpress_keys('wp-config.php'); do_wp(None) # Install print("%(app)s: Installing cookies for comments and force-strong-password" % res) cmd = """ echo "Deleting inactive plugins ..." $HOME/bin/wp plugin delete `$HOME/bin/wp plugin list | awk '($2 == "inactive"){print $1}' ` echo "Updating plugins ..." $HOME/bin/wp plugin update --all echo "Updating themes ..." $HOME/bin/wp theme update --all echo "Installing cookies-for-comments plugin" $HOME/bin/wp plugin install cookies-for-comments --activate echo "Installing force-strong-passwords plugin" $HOME/bin/wp plugin install force-strong-passwords --activate """ s = cmd.replace(r"'",r"'\''") droppriv(quiet=True) os.system(cmd) def dbaccess(show=True,unknown_application=None): try: if unknown_application: unknown_application={'app': unknown_application, 'version':''} res = None cmsconfigs=[ dict(getconfigfunc=dbaccess_joomla, files=["configuration.php"], ), dict(getconfigfunc=dbaccess_moodle, files=["config.php"], ), dict(getconfigfunc=dbaccess_phpbb, files=["config.php"], ), #dict(getconfigfunc=dbaccess_wordpress, files=["wp-config.php"], ), dict(getconfigfunc=dbaccess_openx, files=glob.glob('var/*.conf.php'), ), dict(getconfigfunc=dbaccess_other, files=[""], ), dict(getconfigfunc=dbaccess_guess, files=["index.php"], ), ] res=None for cmsconfig in cmsconfigs: for filename in cmsconfig['files']: if filename and not os.path.exists(filename): continue res = cmsconfig['getconfigfunc'](filename) if res: break if res: break if res: res.setdefault('prefix','') if 'version' not in res: res['version'] = version_of_application(res.get('app','None')).replace(",","\\,") try: res['mysql'] = "mysql --host=%(host)s --password='%(pass)s' --user=%(user)s %(db)s" % res res['mysqldump'] = "mysqldump --host=%(host)s --password='%(pass)s' --user=%(user)s %(db)s --skip-ext --skip-lock-tables" % res res['grant'] = "GRANT ALL on `%(db)s`.* to '%(user)s'@'localhost' IDENTIFIED BY '%(pass)s';" % res if show and sys.stderr.isatty() and sys.stdout.isatty() and sys.stdin.isatty(): sys.stderr.write( ( "# application=%(app)s version=%(version)s configfile=%(file)s prefix=%(prefix)s\n" "mysql --host=%(host)s --password='%(pass)s' --user=%(user)s %(db)s\n" "mysqldump --host=%(host)s --password='%(pass)s' --user=%(user)s %(db)s --skip-ext --skip-lock-tables|less\n" "# GRANT ALL on `%(db)s`.* to '%(user)s'@'localhost' IDENTIFIED BY '%(pass)s';\n" ) % res ) except KeyError: if show: print("Application unknown. Please report if you know what it is.") if 'prefix' in res: res.pop('prefix'); if 'version' in res: res.pop('version'); if len(res): if show: print(res.__repr__()[:80]) else: res['user']= pwd.getpwuid(os.stat(".").st_uid).pw_name if show: print("# egrep -1 \"%(user)s_[0-9A-Za-z]*\" {.,inc*,conf*}/*.php" % res) os.system("bash -c 'egrep -1 --color=ALWAYS \"%(user)s_[0-9A-Za-z]*\" {.,inc*,conf*}/*.php 2>/dev/null | head -n 20'" % res) #raise pass if res==None: unknown_application; return res res=unknown_application except AttributeError: return None return res def version_of_anything(filenames,regex, group=1, debug=0): if type(regex)==str: regex =re.compile(regex) if type(filenames)==str: filenames=[filenames,] for fn in filenames: if debug: print(fn) if not os.path.exists(fn): continue if debug: print(fn) fd = open(fn,'r') for line in fd: m = regex.search(line) if m: if debug: print(line) if debug: print('---'.join(m.groups())) return m.group(group) return '' def version_of_phplist(): return version_of_anything("admin/connect.php",r'define\("VERSION","(\S+)"\);') def version_of_application(app): if app=='joomla': return version_of_joomla() if app=='joomla-2.x-series': return version_of_anything("administrator/manifests/files/joomla.xml",r'(.*?)') if app=='joomla-1.ancient': return version_of_anything("includes/version.php",r"var \$RELEASE\s*=\s*'(.*)'") if app=='vtiger': return version_of_anything("vtigerversion.php",r"vtiger_current_version\s*=\s*'(.*?)'"); if app=='phplist': return version_of_anything("admin/connect.php",r'define\("VERSION","(\S+)"\);') if app=='wordpress': return version_of_anything("wp-includes/version.php",r'''\$wp_version\s*=\s*["'](.*?)["']'''); if app=='oxwall': return version_of_anything("ow_version.xml",r'''(.*?)'''); if app=='OpenCart': return version_of_anything("index.php",r'''define\('VERSION', '(.*?)'\);'''); if app=='MODx': if os.path.exists('core/docs/changelog.txt'): return version_of_anything("core/docs/changelog.txt",r"MODx Revolution\s+(.*?\(.*,.*\))"); if os.path.exists('changelog.txt'): return version_of_anything("changelog.txt",r"\d+/\d+/\d+: Version\s+([^\r\n]*)"); if app=='oscommerce': return version_of_anything("includes/application_top.php", r"define\('PROJECT_VERSION', *'(.*?)'\)") if app=='cubecart': return version_of_anything("ini.inc.php",r"""\$ini\['ver'\]\s*=\s*'(.*?)'""") if app=='elgg': return version_of_anything("CHANGES.txt",r"""Version (\d.*)""") if app=='Comdev One Admin': return version_of_anything("readme.txt",r"""Comdev One Admin (\d+\..*)""") if app=='drupal': v=version_of_anything("modules/system/system.module",r"""define\('VERSION', *'(.*?)'\)""") if not v: v=version_of_anything('./includes/bootstrap.inc',r"""define\('VERSION', *'(.*?)'\)""") return v if app=='e107': # e107_admin/ver.php:$e107info['e107_version'] = "0.7.25"; return version_of_anything("e107_admin/ver.php",r'''\['e107_version'\]\s*=\s*"(.*?)"''', group=1, debug=0); if app=='WebCalendar': return version_of_anything("includes/config.php",r'''PROGRAM_VERSION\s*=\s*['"]v?(.+?)["']''', group=1, debug=0); if app=='hesk helpdesk': return version_of_anything("hesk_settings.inc.php",r"""hesk_settings\['hesk_version'\]='(.*?)';""") if app=='magento': return version_of_anything("RELEASE_NOTES.txt",r"""==== ([^=]*?) ====""") if app=='coppermine photo gallery 1.x': return version_of_anything("admin.php",r"""Coppermine version:\s+(\d+\S*)""") if app=='MyBB': return version_of_anything("calendar.php",r""" \* MyBB\s+(\d+\.\S*)""") if app=='phpmyfaq': return version_of_anything("docs/CHANGELOG",r"""phpMyFAQ (\d+\.[0-9.]+)""") if app=='interspire shopping cart': return version_of_anything("lib/init.php",r"""define\('PRODUCT_VERSION', *'(.*?)'\)""") if app=='xoops': return version_of_anything("include/version.php",r"""define\('XOOPS_VERSION', *'[XOOPS ]*(.*?)'\)""") if app=='Gallery2.3': return version_of_anything("./modules/core/module.inc", r"""this->setGalleryVersion\('(.*?)'\);"""); return '' def version_of_joomla(): changelogfile="CHANGELOG.php" if not os.path.exists(changelogfile): return 'unknown'; cl = open(changelogfile,'r') version_re=re.compile(r"------ (\d+\.\d+\.\d+[^\[-]*) ") for line in cl: m = version_re.search(line) if m: return m.group(1) return 'unknown' def nocommentspam(): # FIXME show create table x; drop table x; create table x s/AUTOINCREMENT// jos_joomgallery_comments = """ drop table jos_joomgallery_comments; CREATE TABLE `jos_joomgallery_comments` ( `cmtid` int(11) NOT NULL, `cmtpic` int(11) NOT NULL default '0', `cmtip` varchar(15) NOT NULL default '', `userid` int(11) unsigned NOT NULL default '0', `cmtname` varchar(50) NOT NULL default '', `cmttext` text NOT NULL, `cmtdate` varchar(20) default NULL, `published` tinyint(1) NOT NULL default '0', `approved` tinyint(1) NOT NULL default '1', PRIMARY KEY (`cmtid`), KEY `idx_cmtpic` (`cmtpic`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1 """ def show_autologin(content,prefix='',show=True,url=None,base_url=None): filename=prefix+'autologin-%s.php' % easypassword(); fd=open(filename,'w') fd.write(content); fd.close() if base_url: if not base_url.endswith('/'): base_url+='/'; url=base_url+filename if not url: url = os.popen('url "%(filename)s"' % dict(filename=filename),'r').read().strip() meh = dict(filename=filename,url=url); os.system(r''' chmod 644 %(filename)s chown --reference=index.php %(filename)s''' % meh ) if show: os.system(r''' echo Autologin URL is: echo echo %(url)s echo read -t 120 -p "Press Enter to remove %(filename)s ..." || echo \rm -f %(filename)s ''' % meh) return url; def autologin_joomla(): autologin_php = """1200) { die('Expired'); } setcookie("RFC3514","1",time()+604800,'/'); // Set flag that this is a parent file define( '_JEXEC', 1 ); define('JPATH_BASE', dirname(__FILE__) ); define('DS', DIRECTORY_SEPARATOR); require_once( JPATH_BASE .DS.'includes'.DS.'defines.php' ); require_once( JPATH_BASE .DS.'includes'.DS.'framework.php' ); require_once( JPATH_BASE .DS.'includes'.DS.'helper.php' ); require_once( JPATH_BASE .DS.'includes'.DS.'toolbar.php' ); $mainframe =& JFactory::getApplication('administrator'); $mainframe->initialise(array( 'language' => $mainframe->getUserState( "application.lang", 'lang' ))); JPluginHelper::importPlugin('system'); // trigger the onAfterInitialise events $mainframe->triggerEvent('onAfterInitialise'); ########## BEGIN BACKDOOR ######### $db =& JFactory::getDBO(); $query = 'SELECT username,email from #__users WHERE usertype like "Super administrator%" LIMIT 1'; $db->setQuery($query); $rows = $db->loadObjectList(); foreach ($rows as $row) { $credentials=array('username'=> $row->username, 'password'=> rand()); break; } require_once( JPATH_BASE .DS.'..'.DS.'libraries'.DS.'joomla'.DS.'user'.DS.'authentication.php' ); #jimport( 'joomla.user.authentication'); $response = new JAuthenticationResponse(); $response->type='backdoor'; $response->username=$credentials['username']; $response->fullname=$credentials['username']; $response->password=$credentials['password']; #$session = &JFactory::getSession(); #$session->fork(); #$mainframe->_createSession($session->getId()); JPluginHelper::importPlugin('user'); #$results = $mainframe->triggerEvent('onLoginUser', array((array)$response, array())); $results = $mainframe->triggerEvent('onLoginUser', array($credentials, array())); ########## END BACKDOOR ######### $mainframe->route(); $mainframe->triggerEvent('onAfterRoute'); $option = JAdministratorHelper::findOption(); $mainframe->dispatch($option); $mainframe->triggerEvent('onAfterDispatch'); $mainframe->render(); $mainframe->triggerEvent( 'onAfterRender' ); echo JResponse::toString($mainframe->getCfg('gzip')); """ show_autologin(autologin_php,'administrator/') def autologin_joomla16(show=True,timeout=1200): autologin_php = """"""+str(timeout)+""") { die('Expired'); } setcookie("RFC3514","1",time()+604800,'/'); // Set flag that this is a parent file define( '_JEXEC', 1 ); define('JPATH_BASE', dirname(__FILE__) ); define('DS', DIRECTORY_SEPARATOR); require_once( JPATH_BASE .DS.'includes'.DS.'defines.php' ); require_once( JPATH_BASE .DS.'includes'.DS.'framework.php' ); require_once( JPATH_BASE .DS.'includes'.DS.'helper.php' ); require_once( JPATH_BASE .DS.'includes'.DS.'toolbar.php' ); $app =& JFactory::getApplication('administrator'); $app->initialise(array( 'language' => $app->getUserState('application.lang'))); JPluginHelper::importPlugin('system'); $app->triggerEvent('onAfterRoute'); $app->triggerEvent('onAfterDispatch'); // trigger the onAfterInitialise events $app->triggerEvent('onAfterInitialise'); ########## BEGIN BACKDOOR ######### $db =& JFactory::getDBO(); #$query = 'SELECT username,email from #__users WHERE usertype REGEXP "Super administrator|Super User" LIMIT 1'; $query = 'SELECT username,email from #__users ORDER by id LIMIT 1'; $db->setQuery($query); $rows = $db->loadObjectList(); foreach ($rows as $row) { $credentials=array('username'=> $row->username, 'password'=> rand()); break; } require_once( JPATH_BASE .DS.'..'.DS.'libraries'.DS.'joomla'.DS.'user'.DS.'authentication.php' ); #jimport( 'joomla.user.authentication'); $response = new JAuthenticationResponse(); $response->type='backdoor'; $response->username=$credentials['username']; $response->fullname=$credentials['username']; $response->password=$credentials['password']; # JAUTHENTICATE_STATUS_SUCCESS is defined by joomla 1.6 if (defined('JAUTHENTICATE_STATUS_SUCCESS')) $response->status=JAUTHENTICATE_STATUS_SUCCESS; else $response->status=JAuthentication::STATUS_SUCCESS; JPluginHelper::importPlugin('user'); $results = $app->triggerEvent('onUserLogin', array($credentials, array())); ########## END BACKDOOR ######### # Render something $app->route(); $app->triggerEvent('onAfterRoute'); $app->dispatch(); $app->triggerEvent('onAfterDispatch'); $app->render(); $app->triggerEvent( 'onAfterRender' ); echo $app; """ return show_autologin(autologin_php,'administrator/',show=show) def do_login(show=True): "Get login link for current application (e.g. wordpress)" res=dbaccess(show=False, unknown_application='Unknown application') sys.stderr.write("autologin %(app)s %(version)s...\n" % res); if res['app']=='joomla': return autologin_joomla() elif res['app']=='joomla-2.x-series': return autologin_joomla16(show=show) elif res['app']=='wordpress': return autologin_wordpress() else: print("Not implemented") def do_software_list(dirlist): if len(dirlist)==0: dirlist = glob.glob('/home/*/public_html') dirlist.sort() cwd = os.getcwd() for directory in dirlist: if not os.path.isdir(directory): continue os.chdir(cwd) os.chdir(directory) res=dbaccess(show=False) if not res or not res['app'] or res['app']=='some application': continue res['directory'] = directory dirname=sys.argv[0] dirname=dirname[ :dirname.rfind('/')+1 ] if dirname.startswith('.'): dirname=cwd+'/'+dirname res['url']=os.popen(dirname+'url','r').read().strip(); res['pad']=' '* (32 - len(res['app']) - len(res['version']) ) print("%(app)s %(version)s %(pad)s%(directory)-30s %(url)s" % res) sys.stdout.flush(); def wordpress_option(option,default=''): if os.getuid()==0: droppriv() php=r'''=0: return default; # probably some error return rc.strip() def autologin_wordpress(): autologin_php=r"""get_row("SELECT users.* FROM $wpdb->users users, $wpdb->usermeta users_meta where users.id=users_meta.user_id and users_meta.meta_key like '%capabilities' and users_meta.meta_value like '%administrator%' ORDER BY ID limit 1")) { $u=$wpdb->get_row("SELECT * FROM $wpdb->users ORDER BY ID limit 1"); } $user=$u->user_login; wp_setcookie($user,rand(),false,"","",""); do_action('wp_login',$user); wp_redirect('wp-admin/'); } """ show_autologin(autologin_php,base_url=wordpress_option('siteurl')); def do_wp(parameters): home = droppriv(quiet=True) res=dbaccess(show=False) if not res or res['app'] != 'wordpress': sys.stderr.write("ERROR: No wordpress here. (?!)... continuing anyway ...\n") do_download=1 try: wp_time = os.stat(home+'/bin/wp' ) if wp_time.st_mtime > time.time()-365*86400 and wp_time.st_size not in ( 817983, 5616279 ) : do_download=False except: pass if do_download: cmd=''' echo "Downloading wp-cli.phar to $HOME/bin/wp" mkdir -p $HOME/bin wget -O $HOME/bin/wp http://answers.afrihost.com/updates/wp-cli.phar chmod 755 $HOME/bin/wp ''' os.system(cmd); if parameters: cmd=''' $HOME/bin/wp %s ''' os.system(cmd % (' '.join(map(shellquote,parameters)))) return home def do_drush(parameters): droppriv() res=dbaccess(show=False) if res['app']!='drupal': print("No drupal here. (?)") return if not os.path.isdir("../drush"): os.system(r""" curl http://ftp.drupal.org/files/projects/drush-7.x-5.9.tar.gz | tar -C .. -xz """) if not os.path.exists("../drush/php.ini"): os.system(r""" sed 's/^memory_limit.*/memory_limit = 256M/' < $( php -r 'echo php_ini_loaded_file();' ) > ../drush/php.ini """) os.system("../drush/drush %s" % ' '.join(parameters)) def drupal(): res=dbaccess(show=False) if res['app']!='drupal': print("No drupal here. (?)") return mysql = "mysql -f --host=%(host)s --password='%(pass)s' --user=%(user)s %(db)s" % res print("Hacking %(app)s as configured in %(file)s" % res) fd = os.popen(mysql,'w') cmds=""" UPDATE system SET status='0' WHERE name='tellafriend'; UPDATE system SET status='0' WHERE name='forward'; UPDATE system SET status='0' WHERE name='syslog'; UPDATE system SET status='0' WHERE name='forum'; """ fd.write(cmds) fd.close(); def shellquote(name): return "'" + ( name.replace("'","'\\''") ) + "'" def do_patch_ereg_replace(files,no_act=False): match=re.compile(r"""\b(ereg_replace)\b"""); already_done=re.compile(r'deprecated_ereg_replace'); patch=r""""""; for filename in files: if not os.path.exists(filename): sys.stderr.write("'%s' does not exist.\n" % filename) continue fd=open(filename,'r'); content=fd.read(); if not match.search(content): continue; if already_done.search(content): sys.stderr.write("%s: ereg_replace already patched\n" % filename) continue; if no_act: sys.stderr.write("%s is an ereg_replace script that requires a patch\n" % filename) return content=content.replace('ereg_replace','deprecated_ereg_replace'); content=patch+content; fd=open(filename,'w'); fd.write(content) fd.close() sys.stderr.write("ereg_replace patched %s\n" % filename) def do_patch_upload(files,no_act=False): # match=re.compile(r"""""", re.DOTALL ); patch=r""""""; for filename in files: if not os.path.exists(filename): sys.stderr.write("'%s' does not exist.\n" % filename) continue fd=open(filename,'r'); content=fd.read(); # if not match.search(content): continue; # this takes too long on bigger files ... i = content.find('"""; match=r"""preg_match('/\.php/i',$_REQUEST['src'])"""; check="""TimThumb""" for filename in files: if not os.path.exists(filename): sys.stderr.write("'%s' does not exist.\n" % filename) continue fd=open(filename,'r'); content=fd.read(); if content.find(match)>=0: continue; if content.find(check)<0: continue; content=patch+content; if no_act: print("We found a vulnerable installation of timthumb, which requires a patch %s\n" % filename); else: st = os.stat(filename); fd=open(filename,'w'); fd.write(content) fd.close() os.utime(filename,(st.st_atime, st.st_mtime) ); # ctime changes instead sys.stderr.write("Vulnerable copy of tim thumb patched, %s\n" % filename) def remail(files): emailre=re.compile(r'[a-zA-Z0-9+_\-\.]+@[0-9a-zA-Z][.-0-9a-zA-Z]*.[a-zA-Z]+') for filename in files: recipients=[] fd=open(filename,'r') while True: line = fd.readline() if line.strip()=='': break if not line.startswith('Envelope-to: '): continue recipients = emailre.findall(line) line = fd.readline() while line.startswith(' ') or line.startswith('\t'): recipients.extend( emailre.findall(line) ) line = fd.readline() break fd.close() if len(recipients): recipientparams = ' '.join(map(shellquote,recipients)) sys.stdout.write('sendmail %s < %s\n' % (recipientparams, shellquote(filename)) ) else: sys.stdout.write('sendmail -t -i < %s\n' % (shellquote(filename)) ) def do_limit(mailsperhour): cpanel = CpanelData() cpanel.setitem("MAX_EMAIL_PER_HOUR",str(mailsperhour)) os.system("/usr/local/cpanel/scripts/update_email_limits '%s'" % cpanel.user) def suspend(): cpanel = CpanelData() r={'user': cpanel.user, } os.system(""" /scripts/suspendacct %(user)s """ % r ) def unsuspend(): cpanel = CpanelData() r={'user': cpanel.user, } os.system(""" /scripts/unsuspendacct %(user)s """ % r ) def lockout(): cpanel = CpanelData() r={'user': cpanel.user, } print("HARD suspend to lock out account %(user)s for 1 week" % r) os.system(""" [ -f /usr/bin/at ] || yum -y install at && service atd start chattr -i /var/cpanel/users/%(user)s /scripts/suspendacct %(user)s chattr +i /var/cpanel/users/%(user)s chattr +i /home/%(user)s/public_html/.htaccess echo "chattr -i /var/cpanel/users/%(user)s /home/%(user)s/public_html/.htaccess" | at now + 2 days """ % r ) def unlockout(): sys.stderr.write(" frob unlockout auth token:"); r=sys.stdin.readline() if not r.startswith(chr(32)+chr(32)): # auth token is print(" Incorrect auth token") return cpanel = CpanelData() r={'user': cpanel.user, 'taken_down': 'taken-down'} os.system(""" if [ -d '%(taken_down)s' ] ; then echo "Undoing takedown" rm .htaccess index.php mv %(taken_down)s/* %(taken_down)s/.* . rmdir %(taken_down)s chown --reference=.. . chgrp nobody . chmod 710 . fi chattr -i /var/cpanel/users/%(user)s chattr -i /home/%(user)s/public_html/.htaccess """ % r ) def do_slow_query_log(parameters): username = pwd.getpwuid(os.stat(".").st_uid).pw_name userprefix='# User@Host: '+username+'_' r = os.popen( "tail -c 2000000 /var/log/mysql_slow_query_log", 'r'); printy=False for line in r: if line.startswith('# User@'): printy=False if line.startswith(userprefix): printy=True if printy: sys.stdout.write(line); pass def do_corrupt(parameters): for filename in parameters: junkpos=64 junklength=64 if not os.path.exists(filename): continue fd=open(filename,'r+b') fd.seek(0,2) length = fd.tell() fd.seek(min(junkpos,length),0) fd.write(open('/dev/urandom','r').read(junklength)) fd.close() pass def lockmailbox_exim(username): m = re.match(r'(\S+)@(\S+.\S+)',username); if not m: sys.stderr.write("%s: invalid mailbox\n" % username); return {'locked': False } user,domain = m.groups() user=user.lower() domain=domain.lower() exim_bt = os.popen("exim -d -bt %s 2>&1" % shellquote(username),'r') passwd_file = '' passwd_re=re.compile(r'(/(\S*/)?home/\S*/etc/\S+\.\S+/(passwd|quota))\b') for line in exim_bt: m = passwd_re.search(line) if not m: continue passwd_file =m.group(1) if not passwd_file: userdomains = get_userdomains() account=userdomains.get(domain,'') homedir = pwd.getpwnam(account).pw_dir f = homedir+'/etc/'+domain+'/passwd' if os.path.exists(f): passwd_file=f if not passwd_file: sys.stderr.write("%s: No password file" % username); return {'locked': False } if not os.path.exists(passwd_file): sys.stderr.write("%s: does not exist" % passwd_file); return {'locked': False } shadow_file = ('/'.join(passwd_file.split('/')[:-1]))+'/shadow' if not os.path.exists(shadow_file): sys.stderr.write("%s: does not exist" % passwd_file); return {'locked': False } result={} result['filename']=shadow_file result['locked'] = lock_mailbox_shadow_file(shadow_file,user); return result def getcontactaddress(filename, there_can_only_be_one=False): 'Figure out who to notify about this file' from maintenance import config more_contacts = config.option('notify_list','support@afrihost.com').split() contacts = [] bits = filename.split('/') home_index = bits.index('home') homedir='/'.join(bits[:home_index+2]) contactemailfile = homedir+'/.contactemail' if os.path.exists(contactemailfile): contacts.append(open(contactemailfile,'r').readline().strip()) if not there_can_only_be_one: contacts.extend(more_contacts); return contacts def notify(contacts, info): # info['subject'] # info['body'] info['hostname'] = socket.gethostname() info['email'] = '>, <'.join(contacts[1:]) info['email0'] = contacts[0] headerlines=[] headerlines.append( "From: %(app)s Notifier \n" ) headerlines.append( "To: <%(email0)s>\n" ) if info.get('email'): headerlines.append( "Bcc: <%(email)s>\n" ) headerlines.append( "Subject: %(subject)s\n" ) template = ''.join(headerlines)+ \ "\n"+ \ "Hi %(email0)s,\n"+ \ "\n" + \ info['body'] + "\n" sys.stderr.write(template % info) fd = os.popen('/usr/sbin/sendmail -t -i','w') fd.write(template % info) fd.close() def do_newpassword(parameters,force=0): # Returns 0 if something went right import posix,pwd import ftpsetrandompasswd stat = os.stat('.') uid = stat.st_uid pwent = pwd.getpwuid(stat.st_uid) username = pwent.pw_name r = ftpsetrandompasswd.setrandompassword(username) contacts = getcontactaddress(os.getcwd(), there_can_only_be_one=True) r['hostname' ] = socket.gethostname() info = { 'app':'Security', 'subject':r['sitename']+' password change, user '+username, 'username': username, 'body': ( 'The system administrator has set a new password for the account\n' '%(username)s on %(hostname)s.\n' '\n' 'The new credentials for %(sitename)s are now:\n' '\n' ' user name: %(username)s\n' ' password : %(newpassword)s\n' '\n' 'This message has been semi-automatically generated. Please ensure\n' 'that you use SSL when using this password, to prevent interception.\n' 'Do not use this password from a desktop system that has viruses or\n' 'other security problems.\n' '\n'), } info.update(r); rc = notify(contacts,info) return rc def do_spamming(parameters,force=0): # Returns 0 if something went right rc = 1 for username in parameters: results = lockmailbox_exim(username) filename=results.get('filename') locked=results.get('locked') os.system('eximanage --demail %s' % shellquote(username)); if not filename or not locked: continue rc = 0 # something went well contacts = getcontactaddress(filename) if locked: has_been_locked = '\nThe password for the mailbox '+username+' has been locked\n' else: has_been_locked = '' info = { 'app':'Mail system', 'subject':':: MAIL SECURITY ALERT :: Mailbox password locked. '+username, 'username': username, 'has_been_locked': has_been_locked, 'body': ( 'The mailbox %(username)s has been either sending spam, or has triggered a\n' 'high vulnerability alert in the form of creation attempts of highly suspect interception\n' 'filters to facilitate fraudulent activities.\n' '\n' 'This means that the email password is known to spammers or fraudsters,\n' 'and your domain is being targeted.\n' '\n' 'The password has been locked, and domain is safe.\n' '\n' 'Reset your password to something new and complex to remain safe,\n' 'it is suggested that you update email passwords of all email accounts,\n' 'and be vigilant in regards to unknown forwardres and unusual email activity.\n' '\n' 'Your password could have been attained due to one of the following:\n' '\n' ' 1. a poor choice of password (e.g. p@55w0rd!@#)\n' ' 2. a desktop virus\n' ' 3. failure to use SSL when retrieving mail\n' ' 4. Phishing scam\n' '%(has_been_locked)s\n' '\n' 'Please check the user systems for viruses, enable SSL and then\n' 'set a new password (not the same password!). Also check other\n' 'mailboxes and systems that may have the same password scheme,\n' 'virus or security problems.\n' '\n' 'We also suggest you check your email filters and forwarders to ensure no filters or forwarders,\n' 'have been created in an attempt to intercept mail.\n' 'https://help.afrihost.com/topic/domains-and-hosting/how-to-manage-email-filters-in-cpanel\n' 'https://help.afrihost.com/topic/security/how-to-protect-yourself-from-email-interception-fraud\n' 'Visit http://www.icode.org.za/ for help with desktop security.\n' '\n'), } hostname = socket.gethostname() if hostname.endswith('.au') or hostname.find('ewsprou')>0 or username.endswith('.au'): info['body'] = info['body'].replace('http://www.icode.org.za/','http://www.icode.net.au/') notify(contacts,info) return rc def do_notify(parameters,force=0): # Send a nastygram to the client rc = 1 contacts = getcontactaddress('.') info = { 'app':'System notification', 'subject':'Notification '+username, 'username': username, 'body': ( 'Hello,\n' '\n' 'Please take note of the following notification\n' '\n' + (' '.join(parameters) ) + '\n' '\n' 'Thank you\n' '\n'), } hostname = socket.gethostname() notify(contacts,info) return rc def quasiurlparse(quasiurl=''): i=quasiurl.find('//') if i>=0: vhost, fileuri = quasiurl[i+2:].split('/',1) fileuri=fileuri.lstrip('/'); else: vhost='localhost' fileuri = quasiurl if fileuri.find('?')>=0: filepart,querystring=fileuri.split('?',1) questionmark='?' else: filepart=fileuri querystring=''; questionmark='' if not os.path.exists('../public_html'): cwd=os.getcwd(); public_html = '/public_html/' i=cwd.find(public_html); if i>0: filepart=cwd[i+len(public_html):]+'/'+filepart.lstrip('/') url = 'http://'+vhost+'/'+filepart+questionmark+querystring filename='index.php' bits = filepart.split('/'); #path_info = filepart is_dir = False for r in range(0,len(bits)): somefile = '/'.join(bits[:r+1]) if not somefile: somefile='.' is_dir = True if os.path.isfile(somefile+'/index.php'): filename = somefile+'/index.php' is_dir = True if os.path.isfile(somefile): filename = somefile; is_dir = False if is_dir: filepart = filepart.rstrip('/')+'/' return { 'url': 'http://'+vhost+'/'+filepart+questionmark+querystring, 'request_uri': '/'+filepart+questionmark+querystring, #'path_info': path_info, 'vhost': vhost, 'filepart': filepart, 'uri': filepart+questionmark+querystring, 'querystring': querystring, 'questionmark': questionmark, 'filename': filename, } def do_phptest(quasiurls): for quasiurl in quasiurls: for line in phptest(quasiurl): sys.stdout.write(line) def linkfarmscan(filenames): import subprocess cmd = [ '/usr/local/afribin/maintenance/linkfarmscan.py', '-N', '/dev/null','--limit','999999' ] cmd.extend(filenames) rc = subprocess.call(cmd, shell=False) def find_html_urls(line, base_url, urls=[]): '''Nasty regex parser to find html urls''' '''base_url is like http://site/fir/index.php''' re_href=re.compile(r'\ ]{1,})',re.I); for m in re_href.finditer(line): relative_url = m.group(1) protocol = relative_url.find('//') if protocol>=1 and protocol < 5: if relative_url.startswith(base_url): urls.append(relative_url) # same site continue if relative_url.startswith('/'): bits=base_url.split('/') urls.append('/'.join(bits[:3])+relative_url) continue i=base_url.rfind('/') # okay, so this is sucky, since it assumes get parameters don't contain /, but hey urls.append(base_url[:i]+'/'+relative_url) return urls def do_rfc3514(quasiurls): droppriv() disabledcodeprefix='Reached disabled code in '; # Also disabled code: filenames = [] rfc3514fd=open('.rfc3514','a') # Try running directly: for quasiurl in quasiurls: for iteration in range(0,10000): foundsomething = False for line in phptest(quasiurl): i=line.find(disabledcodeprefix) if i<0: continue filename=line[i+len(disabledcodeprefix):].strip() do_undisable([filename,]) filenames.append(filename) rfc3514fd.write(filename+'\n'); rfc3514fd.flush() # don't buffer please foundsomething=True if not foundsomething: break pass # Try running through curl: for url in quasiurls: if url.find('://')<0: continue for iteration in range(0,1000): foundsomething = False for line in rfc3541_with_curl(url): i=line.find(disabledcodeprefix) if i<0: continue filename=line[i+len(disabledcodeprefix):].strip() do_undisable([filename,]) filenames.append(filename) rfc3514fd.write(filename+'\n'); rfc3514fd.flush() # don't buffer please foundsomething=True if not foundsomething: break pass if len(filenames): linkfarmscan(filenames) def rfc3541_with_curl(url): fd = os.popen('curl --cookie RFC3541=1 \'%s\'' % url.replace("'","'\\''"),'r') for line in fd: yield(line) fd.close(); def phptest(quasiurl): "Run the PHP code, and return its output" quasiurlinfo = quasiurlparse(quasiurl); if not os.path.exists(quasiurlinfo['filename']): return phpcode = r"""0: line0 = re.sub(r'<\?php .* /\*.* disabled for security \*/ \?>\s*','',lines[0]); if line0.strip()=='': line0=''; if line0==lines[0]: sys.stderr.write('frob undisable: %s: no change\n' % filename); continue # no change to this file lines[0]=line0 changes += 1 st = os.stat(filename); fd = open(filename,'w') fd.writelines(lines); fd.close() os.utime(filename,(st.st_atime, st.st_mtime) ); sys.stdout.write(filename+'\n'); return changes #def do_disable(files,no_act=False, die=''): def do_disable(files,no_act=False,die=r''): """Hack a disable into a file """ for filename in files: p = { 'file': filename.replace("'","'\\''"), # shellquote 'die': die, 'timestamp': time.strftime("%Y-%m-%d %H:%M:%S"), } if not os.path.exists(filename): sys.stderr.write("%s not found\n" % filename); continue if not os.path.isfile(filename): sys.stderr.write("%s not a regular file\n" % filename); continue if filename.find(".php")<0: if open(filename,'r').read().find("\n' % p); if no_act: print("frob disable: %s: not disabling" % filename) else: fd=open(filename,'w'); fd.writelines(file_lines); fd.close() os.utime(filename,(st.st_atime, st.st_mtime) ); sys.stderr.write(filename+"\n"); def get_userdomains(): r={} u = open('/etc/userdomains','r') for line in u: bits=line.split(": ") if len(bits)!=2: continue r[bits[0].strip()]=bits[1].strip(); # r[domain.co.za] = domaixnq u.close() return r def lockmailbox(mailbox): 'Returns filename locked and true/false' name,domain=mailbox.split("@",1) userdomains = get_userdomains() account=userdomain.get(domain,'') if not account: print("No account for "+mailbox) return homedir = pwd.getpwnam(account).pw_dir shadow_file = homedir+'/etc/'+domain+'/shadow' if not os.path.exists(shadow_file): print(shadow_file+' not present') return return shadow_file, lock_mailbox_shadow_file(shadow_file,name); def lock_mailbox_shadow_file(shadow_file,name): 'Lock the user in the shadow file: returns True if a change was made' mailboxshadows=open(shadow_file,'r').readlines() mod=False out=[] for mailboxshadow in mailboxshadows: mb_user,mb_crypt,mb_more=mailboxshadow.split(":",2) if mb_user==name: if mb_crypt[:1] in ('X','*','!'): sys.stderr.write( shadow_file+' already has locked user '+name+'\n'); return False else: mb_crypt='X'+mb_crypt; mod=True out.append(":".join( (mb_user,mb_crypt, mb_more) )) if not mod: sys.stderr.write( shadow_file+' does not contain user '+name+'\n'); return None print("locking "+name+" in "+shadow_file) open(shadow_file,'w').writelines(out) os.system('killall pop3-login imap-login') # kill cached users return True def lockmailboxes(mailbox): for mailbox in mailboxes: lockmailbox(mailbox) # VERY basic yml getter def ymlread(filename, parameter): fd = open(filename,'r'); for line in fd: if line.startswith(parameter+': '): fd.close() return line[line.find(': ')+2:].strip() fd.close() # VERY basic yml setter def ymlwrite(filename, parameter, value): fd = open(filename,'r'); out=[] for line in fd: if line.startswith(parameter+': '): out.append(parameter+': '+value+'\n'); else: out.append(line) fd.close() fd=open(filename,'w'); fd.writelines(out); fd.close() def do_codesniffer(params): os.system(''' [ -d /usr/local/lib/php/PHP/CodeSniffer ] || pear install PHP_CodeSniffer '''); droppriv() os.system(''' phpcs --extensions=php,phtml --standard=/usr/local/lib/php/PEAR/PHPCompatibility '%s' ''' % os.getcwd()) def tailfile(filename, tail=200000): if not os.path.isfile(filename): return fd = open(filename,'r') fd.seek(0,2) length = fd.tell() fd.seek(max(0,length-tail),0) drop = fd.readline(); for line in fd: yield(line); fd.close def tailmmin1domlogs(seconds=60, tail=200000): dirlist = glob.glob('/usr/local/apache/domlogs/*') time0 = time.time(); for domlogfile in dirlist: if domlogfile.find('_')>0: continue st=os.stat(domlogfile) if abs(time0 - st.st_mtime) > seconds: continue for line in tailfile(domlogfile, tail): yield line def trawl_log_files_for_evilips(additionalregex=[], tail=200000, seconds=60): "Trawl the log files for brute force attempts" evilips={} reasons={} accesslog_evil_re = re.compile(r'POST .*(login| (500|40[34]) |administrator/[^/]* )') accesslog_evil_re = re.compile(r'POST .*((login|administrator).* HTTP/1\.." (500|40[34]))') rip_re = re.compile(r'rip=(\d+\.\d+\.\d+\.\d+),') smtp_evil_re = re.compile(r'authenticator failed for') smtp_rip_re = re.compile(r'\[(\d+.\d+\.\d+\.\d+)\]:\d+') smtp_good_re = re.compile(r' A=dovecot_'); messages_evil_re = re.compile(r'Authentication failed for') messages_ip_re = re.compile(r'@(\d+.\d+\.\d+\.\d+)\)') additionalregex_re = list(map(re.compile,additionalregex)) r_0_255=r'(0|[1-9]\d?|1\d\d|2[0-4]\d|25[0-5])'; any_ip_re = re.compile(r'\b' + r_0_255 + r'\.' + r_0_255 + r'\.' + r_0_255 + r'\.' + r_0_255 + r'\b' ); ipwhitelist = [] for ipinfo in list_myips(): ipwhitelist.append(ipinfo['ip']) # Attack of the cloned code: # HTTP ATTACKS -- this goes first, since we might consider the thing less evil later for line in tailmmin1domlogs(tail=tail, seconds=seconds): if accesslog_evil_re.search(line): ip=line.split(' ')[0] evilips[ip]=evilips.get(ip,0)+1 reasons[ip]='http brute force' # SMTP ATTACKS for line in tailfile('/var/log/messages',tail=2000000): # Nov 14 12:55:43 porkypig pure-ftpd: (?@91.109.5.130) [WARNING] Authentication failed for user [hendrikvanheerden] if messages_evil_re.search(line): m=messages_ip_re.search(line) if m: ip = m.group(1) evilips[ip]=evilips.get(ip,0)+1 reasons[ip]='ftp brute force' for r in additionalregex_re: for ip in any_ip_re.finditer(line): if ip in ipwhitelist: continue ip = m.group(1) reason='messages abuse' evilips[ip]=evilips.get(ip,0)+1 reasons[ip]=reason break # SMTP ATTACKS for line in tailfile('/var/log/exim_mainlog',tail=2000000): for r in additionalregex_re: for ip in any_ip_re.finditer(line): if ip in ipwhitelist: continue ip = m.group(1) reason='exim_mainlog abuse' evilips[ip]=evilips.get(ip,0)+1 reasons[ip]=reason break # 2014-11-14 12:28:21 dovecot_login authenticator failed for (WIN-7IEAI6D9UC9) [188.190.118.90]:54400: 535 Incorrect authentication data (set_id=laurel) if smtp_evil_re.search(line): m=smtp_rip_re.search(line) if m: ip = m.group(1) evilips[ip]=evilips.get(ip,0)+1 reasons[ip]='smtp brute force' # 2014-11-14 12:24:51 1XpE3d-0003ik-C1 <= jimmy@jsengineering.co.za H=ti-227-166-130.telkomadsl.co.za (jimmy) [105.227.166.130]:2594 P=esmtpa A=dovecot_login:jimmy@jsengineering.co.za S=103252 id=A4AF717B0E524DA3A3B383ED58EFA129@jimmy T="Estimate from JS Engineering # quotation no: 45437" for sachin.teeka@unilever.com if smtp_good_re.search(line): m=smtp_rip_re.search(line) if m: ip = m.group(1) if ip in evilips: evilips.pop(ip) # POP3 ATTACKS for line in tailfile('/var/log/maillog',tail=2000000): i=line.find('-login: Login: '); if i>0: m = rip_re.search(line) if m: ip=m.group(1) if ip in evilips: evilips.pop(ip) continue i=line.find('auth failed,') if i>0: m = rip_re.search(line) if m: ip = m.group(1) evilips[ip]=evilips.get(ip,0)+1 reasons[ip]='pop/imap brute force' for r in additionalregex_re: for ip in any_ip_re.finditer(line): if ip in ipwhitelist: continue ip = m.group(1) reason='maillog abuse' evilips[ip]=evilips.get(ip,0)+1 reasons[ip]=reason break return evilips,reasons ddos_times = 0 ddos_blocked_ips = {} syslog_program = ''; def send_syslog(program, message, priority=syslog.LOG_ERR): global syslog_program if syslog_program!=program: syslog.openlog(program, syslog.LOG_PID, syslog.LOG_DAEMON) syslog_program=program syslog.syslog(priority,message) def nullroute_blocker(r): return "ip route add prohibit %(ip)s # antibruteforce: %(n)d hits, %(reason)s" % r def do_antibruteforce(params, seconds=60, maximum_conections=8192, blocker=nullroute_blocker): # number of hits in t filecount = 0 global ddos_blocked_ips global ddos_times if ddos_times==0: fd = os.popen('ip route ls','r') prohibit_re = re.compile(r'prohibit (\d+\.\d+\.\d+\.\d+)\s*$') for line in fd: m=prohibit_re.search(line) if not m: continue ip=m.group(1) ddos_blocked_ips[ip]=True fd.close() if len(ddos_blocked_ips) > maximum_conections: send_syslog('ddos','Dropping %d host null routes' % len(ddos_blocked_ips) ) os.system(''' echo 1>&2 "Too many hosts prohibited: dropping ALL host prohibit routes" ip route ls | grep 'prohibit [0-9.]* *$' | while read RULE ; do ip route del $RULE; done ''') ddos_blocked_ips={} ips,reasons = trawl_log_files_for_evilips(seconds=seconds) remaining = 64 cmds=[] for ip in sorted(ips, key=ips.get, reverse=True): if ip in ddos_blocked_ips: continue if ip.startswith('127.'): continue n = ips[ip] if n<75: break r = { 'ip': ip, 'n': ips[ip], 'reason': reasons[ip]} send_syslog('ddos','null routing %(ip)s, %(n)s %(reason)s' % r) ddos_blocked_ips[ip]=True cmds.append ( blocker(r) ) remaining -= 1 if remaining==0: break if len(cmds): print('\n'.join(cmds)); os.system( '\n'.join(cmds) ) if ddos_times==0: print("# DDOS first time complete") ddos_times += 1 return cmds def do_php54ini(params): for phpini in params: ini = open(phpini,'r').read() if not re.search(r'allow_call_time_pass_reference|magic_quotes_gpc|register_globals|ioncube_loader_lin_5',ini,re.I) and \ not re.search(r'display_errors\s*=\s*(on|true)',ini,re.I): continue sys.stderr.write(phpini+"\n"); os.system(r''' sed -i ' /allow_call_time_pass_reference\|magic_quotes_gpc\|register_globals/ d; s/ioncube_loader_lin_5...so/ioncube_loader_lin_5.4.so/; s/^display_errors.*/display_errors = Off/; ' ../php.ini '''.replace('../php.ini','%s') % shellquote(phpini) ) def do_active_mailboxes(params): cmd = 'find /home/*/mail -mindepth 3 -maxdepth 3 -name cur -mtime -92 -type d' fd = os.popen(cmd,'r') for cur in fd: cur=cur.strip('\n') parent = cur[:cur.rfind('/')] try: # new_mtime = os.stat(parent+'/new').st_mtime cur_mtime = os.stat(cur).st_mtime parent_mtime = os.stat(parent).st_mtime except: continue if abs(cur_mtime - parent_mtime) < 86400*21: # three weeks of operation on this mailbox please continue # unused print(cur) fd.close() def serveralias(names): "Add a server alias to the site, all cpanel like" cpanel = CpanelData() cpanelinfo = dict( user=cpanel.user, domain=cpanel.cpanel['DNS']) userdatafile='/var/cpanel/userdata/%(user)s/%(domain)s' % cpanelinfo if not os.path.exists(userdatafile): print("User datafile "+userdatafile+" does not exist"); return False serveraliases_in=ymlread(userdatafile,'serveralias'); aliases=serveraliases_in.split() for name in names: if not (name in aliases): aliases.append(name) serveraliases_out = ' '.join(aliases) if serveraliases_out != serveraliases_in: ymlwrite(userdatafile,'serveralias',serveraliases_out); apachectlgraceful(build=True) print(serveraliases_out) def do_vmwaretools(params): os.system(''' set -x set -e cd rm -rf vmware-tools-distrib VMwareTools-* curl -k https://answers.afrihost.com/updates/VMwareTools-latest.tar.gz | tar -xz vmware-tools-distrib/vmware-install.pl -d vmware-toolbox-cmd timesync enable ''') def do_syslog(params): if len(params)==1: program='frob' else: program=params.pop(0) message=' '.join(params) send_syslog(program, message); def previewalias(usernames): "Add preview aliases for a list of user names" if len(usernames)==0: usernames=[ CpanelData().user, ] mod=False for username in usernames: i=username.rfind('/') username=username[i+1:] # whole name or something if not os.path.exists('/var/cpanel/users/'+username): continue cpanel = CpanelData(username) cpanelinfo = dict( hostname=socket.gethostname(), user=cpanel.user, domain=cpanel.cpanel['DNS']) userdatafile='/var/cpanel/userdata/%(user)s/%(domain)s' % cpanelinfo if not os.path.exists(userdatafile): sys.stderr.write("User datafile %s does not exist\n" % userdatafile) continue serveralias='%(user)s.%(hostname)s' % cpanelinfo serveraliases_in=ymlread(userdatafile,'serveralias'); aliases=serveraliases_in.split() if not (serveralias in aliases): aliases.append(serveralias) serveraliases_out = ' '.join(aliases) if serveraliases_out != serveraliases_in: ymlwrite(userdatafile,'serveralias',serveraliases_out); mod=True print(('%(user)s: %(domain)s : ' % cpanelinfo) + serveraliases_out) if mod: apachectlgraceful(build=True) if __name__=="__main__": dircmd=None import optparse parser=optparse.OptionParser() parser.add_option("-n","--number", dest="number", action="store", type='int', default=0, help="How many") parser.add_option("-v","--verbose", dest="verbose", action="count", default=0, help="How verbose") (options,args) = parser.parse_args() rc = 0 operation='' parameters=[] if len(args)>0: operation=args[0] parameters=args[1:] if operation == "complete": source = open(sys.argv[0], 'r').read() for operation in re.findall(r'''if\s+operation\s*==\s*["'](.*?)['"]''',source): if operation not in ('complete',): print(operation) sys.exit(); send_syslog('frob','%s' % (' '.join(args)),priority=syslog.LOG_ERR) if operation == "safe": dircmd=safe if operation == "images": dircmd=do_images if dircmd: cwd = os.getcwd() for wd in parameters: dircmd(wd) elif operation=="sessions": mysql_sessiontable_repair() elif operation=="cronfix": cronfix() elif operation=="mysqllimit": mysqllimit(parameters, options.number) elif operation=="redirect": redirect(url=parameters[0],title=parameters[1]) elif operation=="disable": do_disable(parameters) elif operation=="undisable": do_undisable(parameters) elif operation=="subscribe": subscribe() elif operation=="fcgiphpini": customphpini(rebuild=True) elif operation=="phpinfo": show_phpinfo() elif operation=="securephp": customphpini(disable_functions=True, open_basedir=True, setupfw='fw.php') elif operation=="limit": do_limit(int(parameters[0])) elif operation=="suspend": suspend() elif operation=="unsuspend": unsuspend() elif operation=="lockout": # Lock out a site which is too easily unsuspended lockout() elif operation=="unlockout": # Un-Lock out a site which is too easily unsuspended unlockout() elif operation=="phishing": print("WARNING: INPUT POSTED TO THIS SITE WILL BE CORRUPTED!!!") print("WARNING: INPUT POSTED TO THIS SITE WILL BE CORRUPTED!!!") print("WARNING: INPUT POSTED TO THIS SITE WILL BE CORRUPTED!!!") customphpini(disable_functions=True, open_basedir=True, setupfw='checksum.php') do_limit(25); print("WARNING: INPUT POSTED TO THIS SITE WILL BE CORRUPTED!!!") print("WARNING: INPUT POSTED TO THIS SITE WILL BE CORRUPTED!!!") print("WARNING: INPUT POSTED TO THIS SITE WILL BE CORRUPTED!!!") elif operation=="htaccess": htaccess_fixup() elif operation=="modsec": modsecurity_onoff(True) elif operation=="nomodsec": print("Dude. Solve the problem. This is not a troubleshooting step. Solve the problem. Start with the error log.") #elif operation=="nomodsec": # if len(parameters)==0 or parameters[0].find("//")<0: # print "MISSING INFORMATION: Please supply the URL which produces an error when mod_security intrusion prevension is enabled" # else: # #print "Checking for timthumb and patching it it found..." # #os.system("find . -name '*thumb*php' -print0|xargs -0 %s timthumb" % frob() ) # modsecurity_onoff(False,reason=parameters[0]) elif operation=="modsecrule": modsecurity_addrules(parameters) elif operation=="modsecauto": if not modsecurity_autorule() : print("No change") else: # build with new rules apachectlgraceful(build=True) elif operation=="killall": if not parameters: killall() elif operation=="rebuild": apachectlgraceful(build=True) elif operation.find("dcpu")==0: dcpumonview() elif operation=="domlogs": domlogs() elif operation=="graceful": apachectlgraceful(build=False) elif operation=="hacking": if len(parameters)==0: parameters=['.'] for dir in parameters: hacking(dir) elif operation=="takedown": if len(parameters)==0: parameters=['.'] for dir in parameters: takedown(dir) elif operation=="sanitise": f=[] for do_cleanup in (cleanup_eval, cleanup_iframe, cleanup_htaccess) : f.extend(do_cleanup(parameters)) print('\n'.join(f)) elif operation=="autocleanup": do_autocleanup(parameters) elif operation=="iframe": cleanup_iframe(parameters) elif operation=="uneval": cleanup_eval(parameters) elif operation=="spamming": rc=do_spamming(parameters) elif operation=="newpassword": rc=do_newpassword(parameters) elif operation=="unhtaccess": cleanup_htaccess(parameters) elif operation=="notify_ftp": notify_ftp(parameters) elif operation=="drupal": drupal() elif operation=="drush": do_drush(parameters) elif operation=="phptest": do_phptest(parameters) elif operation=="rfc3514": do_rfc3514(parameters) elif operation=="Reached": do_rfc3514(parameters) elif operation=="login": do_login() elif operation=="strace": do_strace(parameters) elif operation=="pwtest": pwtest(parameters[0],parameters[1]) elif operation in ("db", 'mysql','mysqldump','grant'): do_dbaccess(operation,parameters) elif operation in ("dbpass", ): do_dbpass(operation,parameters) elif operation=="secure": do_secure() elif operation=="check": do_secure(no_act=True) elif operation=="jce": do_jce() elif operation=="nocomment": do_nocomment() elif operation=="crack": res=dbaccess(show=False) do_crack(res) elif operation=="mv": mv(parameters[0],parameters[1]) elif operation=="arping": arping() elif operation=="su": droppriv() if len(parameters): cmdline=[] for a in parameters: cmdline.append("'"+a.replace("'","'\\''")+"'") os.system(' '.join(cmdline)) else: import tempfile tmp=tempfile.NamedTemporaryFile('w') tmp.write( "PS1='\\u@\\h [\\w]\\$ '\n" ) tmp.flush() os.system("bash --rcfile "+tmp.name+" -i") elif operation=="panic": panic() elif operation=="crack2": john_dump() elif operation=="john": res=dbaccess(show=False, unknown_application='an unknown application') do_john(res,options.verbose,guesses=parameters) elif operation=="nice": niceness = int(options.number) if not niceness: niceness=14 if niceness>19: ionice="ionice -c 3 ; " else: ionice='true'; if len(parameters)==0: parameters = ['rdiff-backup','cpanellogd','rrdtool', 'logrunner','analog', ] pids=[] programs_list=[] for n in parameters: if n[:1] in '0123456789': pids.append(n) else: programs_list.append(n) programs=' '.join(programs_list) pidof=os.popen("pidof -x "+programs,'r').readline().strip().split() pids.extend(pidof) if len(pids): params = dict( ionice = ionice, pids = ' '.join(pids), niceness = niceness) print("# nicing these PIDs to prio %(niceness)s: %(pids)s" % params) os.system("%(ionice)s %(pids)s; renice %(niceness)s %(pids)s; ps %(pids)s" % params ) else: print("# found nothing to nice") elif operation=="upgrade": do_upgrade() # do_upgrade_joomla(do_upgrade=True,do_password=False) elif operation=="joomla_password": res=dbaccess(show=False) do_upgrade_joomla(res,do_upgrade=False) elif operation=="ipcs": fix_ipcs() elif operation=="certs": do_certs() # update the certificates on this box elif operation=="runphpinstall": runphpinstall() elif operation=="ereg_replace": do_patch_ereg_replace(parameters) elif operation=="patch": do_patch_ereg_replace(parameters) do_patch_upload(parameters) do_patch_timthumb(parameters) elif operation=="timthumb": do_patch_timthumb(parameters) elif operation=="remail": remail(parameters) elif operation=="lockmailbox": lockmailboxes(parameters) elif operation=="generosity": do_generosity(parameters) elif operation=="previewalias": previewalias(parameters) elif operation=="serveralias": serveralias(parameters) elif operation=="software": do_software_list(parameters) elif operation=="fcgiauthheaders": do_fcgiauthheaders(parameters) elif operation=="quarantine": do_script('quarantine',parameters) elif operation=="rblcheck": do_script('rblcheck',parameters) elif operation=="slow_query_log": do_slow_query_log(parameters) elif operation=="corrupt": do_corrupt(parameters) elif operation=="antibruteforce": do_antibruteforce(parameters) elif operation=="active_mailboxes": do_active_mailboxes(parameters) elif operation=="codesniffer": do_codesniffer(parameters) elif operation=="php54ini": do_php54ini(parameters) elif operation=="vmwaretools": do_vmwaretools(parameters) elif operation=="frob": do_frob(parameters) elif operation=="wp": do_wp(parameters) elif operation=="syslog": do_syslog(parameters) else: print("Usage:") print("Misc tools:") print(" frob fcgiphpini # Set up link to custom php.ini in the user's home directory") print(" frob htaccess # Fix up htaccess in the current dir to work with fcgi (EXPERIMENTAL)") print(" frob sessions # Check and fix all mysql 'sessions' tables") print(" frob redirect url title # Write an embedded frame redirect for the site") print(" frob disable files... # Disable php files by adding at the head") print(" frob undisable files... # Make php files work by removing die() in the first line") print(" frob serveralias domain.test.afrihost.com # Add a host header for this site") print("Mod security:") # print " frob nomodsec 'url' # Turn off mod_security" print(" frob modsecrule 123 456 # Turn off specific mod_security rules") print(" frob modsec # Turn on mod_security again") print(" frob modsecauto # Experimental: show most recent site mod_sec violation, bypass and reload") print("Big hammer:") print(" frob rebuild # Enter rules into httpd.conf and reload apache") print(" frob graceful # Just reload apache") print(" frob killall # kill apache, php and restart mysql, run ddos.py") print("User behaviour cluebats:") print(" frob cronfix # Don't allow user cron jobs more frequently than 15 minutes") print(" frob mysqllimit user -n 30 # Set a limit of 30 sql connections for each user") print(" frob hacking # Block access to current dir: set a password for user, password denied123") print(" frob takedown # Take this site down due to a violation, e.g. ISPA") print(" frob notify_ftp ... # Write up a warning message for the user about domain in /var/log/messages*") print(" frob drupal # Hack evil modules out of drupal") print(" frob remail new/* | sh # Remail mail in the new/ directory to whoever it was to") print(" frob generosity 'reason' # Set generous limits for this user") print(" frob fcgiauthheaders # Pass Authorization header through to PHP for basic auth") print("Troubleshooting:") print(" frob strace user # Hack /usr/local/cpanel/cgi-sys/php5 to strace PHP for user") print(" frob strace # Remove strace hack from /usr/local/cpanel/cgi-sys/php5") print(" frob slow_query_log # Find slow queries for this user") print(" frob pwtest user pass # Test the password and print debugging") print(" frob ipcs # Kill ipcs semaphores left lying around") print(" frob panic # Configure this system to panic easily") print(" frob nice ... # Make processes be nice - by default rdiff-backup and stuff") print(" frob mv src dst # move a live dir src to dest and leave a link") print(" frob arping # announce our IP addresses with arping -U") print(" frob vmwaretools # install vmware tools") print("Security:") print(" frob su [ -- command ] # Drop privileges and be the user of the current directory") print(" frob upgrade # Upgrade joomla and change the passwords, or wordpress") print(" frob joomla_password # Change the database passwords for joomla 1.5") print(" frob safe dir ... # Set 755, 644 permissions") print(" frob images dir ... # Set noscript in directory in .htaccess file") print(" frob securephp # set safer php settings for insecure users (assumes /home/user/php.ini is used)") print(" frob software # List installed software") print(" frob sanitise # remove evil iframes, evals and htaccess bits from the files listed") print(" frob iframe ... # remove evil iframes from the files listed") print(" frob uneval ... # remove eval code from the files listed") print(" frob spamming u@dom # kill a spamming mailbox and its mail") print(" frob newpassword # set a new password for this account and send it to the contact e-mail") print(" frob corrupt file... # corrupt a problematic file ") print(" frob antibruteforce # null route hosts that are trying brute force logins") print("PHP randoms:") print(" frob db|mysql[dump]|grant# Show mysql parameters for the current application") print(" frob dbpass # Set new DB password") print(" frob login # Print a URL to log in to the web app") print(" frob codesniffer # check php compatibility") print(" frob phpinfo # Show phpinfo on the console for the current user") print(" frob php54ini # set php.ini configuration settings for php 5.4") print(" frob drush ... # run drush for a drupal site") print(" frob wp .. . # run wpcli for a wordpress site") print(" frob phptest file.php # run a php in browser style") print(" frob rfc3514 http://url/k # remove disabled code so that the possibly evil code runs - do a manual review") print("Pass through:") print(" frob rblcheck [ip ...] # See if this computer is listed in a DNSBL") print(" frob quarantine file... # Move files, directories to quarantine") sys.exit(rc)