#!/usr/bin/python
"""
Module oratool for Inguma
Copyright (c) 2007 Joxean Koret <joxeankoret@yahoo.es>

License is GPL
"""

import sys
import urllib

from lib.libexploit import CIngumaModule
from lib.libhttp import CIngumaHTMLParser, CIngumaHttp
from lib.libvulnoas import getVulnerableDad

try:
    import cx_Oracle
    oracleSupport = True
except:
    print sys.exc_info()[1]
    oracleSupport = False

name = "oratool"
brief_description = "Oracle wrapper for all related stuff"
type = "gather"

globals = ["dad", "method", "ssl", "injectionPoint"]

class COracleMode(CIngumaModule):

    sid = "orcl"
    dad = None
    method = None
    user = "test"
    password = "test"
    console = False

    colSize = 15
    connection = None

    baseUrl = None
    ssl = False

    injectionPoint = ""

    def help(self):
        print "target = <target host or network>"
        print "port = <target port>"
        print "sid = <sid name>"
        print "user = <database's username>"
        print "password = <user's password>"
        print "dad = <dad>"
        print "method = <PL/SQL gateway bypass method>"
        print
        print 'Use dad="?" to autoresolve DAD'

    def showHelp(self):
        print
        print "Inguma's Oracle mode help"
        print "-------------------------"
        print
        print "help                         Show this help"
        print "exit                         Exit from oracle mode"
        print "sql                          Opens an interactive SQL terminal"
        print "sid=<sid>                    Specify the database's SID"
        print "dad=<dad>                    Specify the server's DAD name"
        print "user=<user>                  Specify the user to use"
        print "password=<password>          Specify the password to use"
        print "print <var>                  Print the value of one variable"
        print "set colsize <size>           Set the result's column size"
        print
        print "Any other typed expression will be evaled as a python expression"
        print

    def connect(self):
        if self.dad == None:
            link    = "%s/%s@(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(HOST=%s)(PORT=%d)))"
            link += "(CONNECT_DATA=(SERVICE_NAME=%s)))"
            link    = link % (self.user, self.password, self.target, int(self.port), self.sid)

            self.connection = cx_Oracle.connect(link)
            self.connection.rollback()
            self.connection.commit()

    def funnySQLCommand(self, sql):
        objHttp = CIngumaHttp("")
        objParser = CIngumaHTMLParser()
        
        self.baseUrl = objHttp.buildUrl(self.target, self.port, self.ssl, "")
        
        if self.injectionPoint == "":
            self.baseUrl = objHttp.buildUrl(self.target, self.port, self.ssl, self.baseUrl + self.dad + self.method + ".cellsprint?p_thequery=" + urllib.quote(sql))
        else:
            self.baseUrl = objHttp.buildUrl(self.target, self.port, self.ssl, self.baseUrl + self.dad + self.injectionPoint.replace("XXX", urllib.quote(sql)))

        res = objHttp.open()
        objParser.feed(res.read())
        objParser.close()

    def runSQLCommand(self, sql, pprint=True, *params):

        """ Are we using a DAD? """
        if self.dad != None:
            self.funnySQLCommand(sql)
            return

        MAGIC_SIZE = self.colSize
        cur = self.connection.cursor()
        cur.execute(sql, *params)

        if sql.lower().find("select") > -1:
            buf = ""
            for col in cur.description:
                buf += col[0].ljust(MAGIC_SIZE) + " "*4

            print buf
            print "-" * len(buf)

            for row in cur.fetchall():
                buf = ""
                for col in row:
                    buf += str(col).ljust(MAGIC_SIZE) + " "*4
                print buf
            print
            print "Total of",cur.rowcount, "row(s) selected."
        else:
            print "Statement executed."

    def sqlLoop(self):
        self.connect()
        buf = ""

        print "Type ';' or '/' in a single line to run a command. Exit to quit."

        i = 1
        prompt = "SQL> "

        while 1:
            try:
                res = raw_input(prompt)
                tmp = buf + res

                if res.lower().startswith("set colsize"):
                    x = res.split(" ")
                    self.colSize = int(x[len(x)-1])
                    continue
                elif res.lower() in ["exit", "quit", "disconn", "disconnect", "agur", "urten"]: # :P
                    break
                elif res in [";", "r", "/"]:
                    if buf == "":
                        print "No data in buffer"
                        continue

                    prompt = "SQL> "
                    i = 1
                    self.runSQLCommand(buf, True)
                    buf = ""
                elif res.endswith(";") and tmp[0:5].upper() not in ["BEGIN", "DECLA"]:
                    buf += res[:len(res)-1] + "\n"
                    prompt = "SQL> "
                    self.runSQLCommand(buf, True)
                    buf = ""
                else:
                    buf += res + "\n"
                    i += 1
                    prompt = " %d   " % i

            except KeyboardInterrupt:
                break
            except EOFError:
                break
            except:
                print "SQL Error:", sys.exc_info()[1]
                buf = ""

    def showExploits(self):
        pass

    def runOracleModeLoop(self):

        dad = self.dad
        sid = self.sid
        port = self.port
        target = self.target
        user = self.user
        password = self.password

        while 1:
            try:
                self.dad = dad
                self.sid = sid
                self.target = target
                self.port = port
                self.user = user
                self.password = password

                res = raw_input("ORAMODE> ")
            except KeyboardInterrupt:
                break
            except EOFError:
                break
            except:
                print "raw_input:", sys.exc_info()[1]
            
            words = res.split(" ")

            if len(words) == 1 and words[0] == "":
                continue
            elif words[0].lower() == "exit" or words[0].lower() == "quit":
                break
            elif words[0].lower() == "help":
                self.showHelp()
            elif words[0].lower() == "sql":
                self.sqlLoop()
            elif words[0].lower() == "show" and words[1].lower() == "exploits":
                self.showExploits()
            else:
                try:
                    exec(res)
                except:
                    print "Error:",sys.exc_info()[1]

        return True

    def run(self):
        if not oracleSupport and self.dad == None:
            print "No support for cx_Oracle. Please, install it first."
            return False

        if self.console:
            self.sqlLoop()
        else:
            self.runOracleModeLoop()

        return True

    def printSummary(self):
        pass
