# Pantera - Web Pen-Test Proxy
#
# FILENAME      : panteraAnalyzer.py
# CODER         : Simon Roses Femerling
# DATE          : 11/28/2005
# LAST UPDATE   : 06/29/2006
# ABSTRACT      : Used by Pantera to perform analysis on data.
#       
# - Roses Labs Innovations (RL+I)
# Roses Labs
# http://www.roseslabs.com
#
# Copyright (c) 2003-2006 Roses Labs.
#
# You may not distribute, transmit, repost this software for commercial 
# purposes without Roses Labs written permission. 
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, publish,
# distribute the Software, and to permit persons to whom the Software 
# is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included
# in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#

'''
@author:       Simon Roses Femerling
@license:      GNU General Public License 2.0 or later
@contact:      pantera.proxy@gmail.com
@organization: OWASP / Roses Labs
'''

'''
PanteraPassiveAnalyzer (PPA): Pantera passive analysis engine.
'''

import threading
import Queue
import sys
import time
import re
import md5
import string
import panteraSnitch
import pickle

#############################################################################################
# classes
#############################################################################################

#############################################################################################
# FUNC     : class PanteraPassiveAnalyzer
# PARAMS   : threading.Thread
# RETURN   : ...
# ABSTRACT : PPA Analyzer
class PanteraPassiveAnalyzer(threading.Thread):
    '''
    
    '''
    """Base class for PanteraPassiveAnalyzer (PPA)"""

    #############################################################################################
    # FUNC     : def __init__
    # PARAMS   : ppa, input, value, con
    # RETURN   : ...
    # ABSTRACT : Init PPA Analyzer internal variables
    def __init__(self, ppa, input, project_id, connection):
        '''
    
        '''
        self._ppa = ppa
        self._input = input
        self._project_id = project_id
        self._connection = connection
        threading.Thread.__init__(self)
        self._input_obj = ""
    # EOF: def __init__

    #############################################################################################
    # FUNC     : def run
    # PARAMS   : ...
    # RETURN   : ...
    # ABSTRACT : Analyze page using PPA plugins and insert into DDBB
    def run(self):
        '''
    
        '''
        """Main function"""
        while True:
            try:
                feed = self._input.get_nowait()
            except Queue.Empty:
                #print >>sys.stderr, "feeder thread %s reached end of job queue" % self
                break
                       
            self._input_id, self._input_obj = feed
            
            self.data = {} # all valid tags for DDBB
            self.data['have_ssl'] = 0
            self.data['have_email'] = 0
            self.data['have_script'] = 0
            self.data['have_form'] = 0
            self.data['have_cookie'] = 0
            self.data['have_session_id'] = 0
            self.data['have_external_link'] = 0
            self.data['have_comment'] = 0
            self.data['have_vuln'] = 0
            self.data['have_hidden'] = 0
            self.data['have_auth_form'] = 0
            self.data['have_object'] = 0
            self.data['have_querystr'] = 0
            self.data['have_postauth'] = 0
            self.data['have_auth'] = 0
            self.data['method'] = "'" + self._input_obj.clientheader.verb + "'"
            m = md5.new(self._input_obj.serverbody.printme())
            self.data['page_hash'] = "'" + m.hexdigest() + "'"
            self.data['return_code'] = "'" + self._input_obj.serverheader.returncode + "'"
            self.data['return_msg'] = "'" + self._input_obj.serverheader.returnmessage + "'"
            self.data['version'] = "'" + self._input_obj.serverheader.version + "'" 
            ext = self._input_obj.clientheader.URL.split(".")[-1]
            ext = ext.lower() 
            self.data['host'] = "'" + self._input_obj.clientheader.connectHost + "'"
            self.data['extension'] = "'" + ext + "'"
            
            add_vuln = ""
            
            # Begin running plugins
            for mod in self._ppa.GetPlugins():
                mod_dat = self._ppa.GetPluginData(mod)
                if mod_dat is None:
                    continue
                check = self._ppa.CheckTag(mod_dat['tag']) 
                if check == 1 and mod_dat.has_key('tag'):
                    do_class = self._ppa.GetPluginClass(mod,mod_dat['id'])
                    if do_class is None:
                        continue
                    dc = do_class()
                    dc.InitAnalyzer()
                    dc.BeginAnalyzer(self._input_obj)
                    t = dc.GetHave()
                    if t != '' and t != 'recon' and dc.GetResult() == 1: # Set flag 1 on some tags (script, comment, etc...)
                        icheck = self._ppa.CheckTag(t) 
                        if icheck == 1:
                            self.data['have_'+t] = 1 # not be best but will do :/
                    r = dc.GetType()
                    if r != "" and dc.GetResult() == 1: #we got data
                        l = dc.GetTypeData()
                        for n in l:
                            d = {}
                            d['project_id'] = self._project_id
                            d['type'] = "'" + r + "'"  
                            d['value'] = n   
                            d['domain'] = "'" + self._input_obj.clientheader.connectHost + "'"
                            self._connection.Insert_Project_Info(d)
                    if dc.GetCheckResultData()==1:
                        for a_v in dc.GetResultData():
                            a_v = string.replace(a_v,"'","") # make sure not ' goes into DDBB
                            add_vuln +=  a_v + "|<>|"
                    f = dc.GetOtherType()
                    if f != "":
                        icheck = self._ppa.CheckTag(f) 
                        if icheck == 1:
                            self.data['have_'+f] = 1
            # End running plugins
            
            # Begin Pantera Snitch
            ps = panteraSnitch.PanteraSnitch(self._input_obj.printBody(1))
            p = ps.BeginSnitch()
            if p == 0:
                u = ps.ReturnURLS()
                c = ps.ReturnComment()
                s = ps.ReturnScript()  
                o = panteraSnitch.PanteraSnithStore()
                o.InsertStore(self._input_id,u,c,s)
                od = pickle.dumps(o,1)
                self._connection.Insert_Snitch(self._project_id, self._input_id, od)
            
            # End Pantera Snitch
            
            # Insert data into page_info_t  
            if add_vuln != "":
                self.data['vuln_data'] = "'" + add_vuln + "'"
            self.data['project_id'] = self._project_id
            self.data['link_id'] = self._input_id
            self._connection.Insert_New_Page(self.data)    
    # EOF: def run
    
# RL+I EOF