Skip to Main Content

 

Auswahl  

Komplett Übersicht aller Oracle Tipps

Security Scoring 

Oracle
PL/SQL
RDBMS 11.x
25.06.18 (MP)
05.07.23(MP)
Security, PL/SQL, Standard Packages

Passende Schulungen zum Thema

In diesem Tipp zeigen wir eine Möglichkeit zur Bewertung verschiedener potentieller Gefahren in der Oracle Datenbank, welche individualisiert überprüft werden können. Da wären SQL Injection-anfällige Eingaben oder auch die Gefährlichkeit der Rechte bzw. Rollen von Oracle Datenbanknutzern.

SQL INJECTION
Um SQL Injection-Angriffe abzuwehren, bietet Oracle das Package dbms_assert welches Eingaben bzw. Strings auf Gültigkeit überprüft. Möchte man allerdings eine Unterscheidung bzw. Einschätzung der potentiellen Gefahr haben, hilft einem dieses Package nicht weiter. Deshalb erstellen wir die Funktion "get_score", die einen zu überprüfenden String entgegennimmt. Zurückgegeben wird ein Scoring-Wert.

CREATE OR REPLACE FUNCTION get_score (p_text IN VARCHAR2) RETURN NUMBER
IS
   TYPE wordsArray IS TABLE OF VARCHAR2(64);
   l_words wordsArray := wordsArray(
      'WITH','SELECT','FROM','WHERE','LIKE','IN','UNION','OR','DUAL','CREATE',
      'ALTER','DROP','GATHER','STATS','EXECUTE','GRANT','HOST','TABLE',
      'TABLESPACE','DATABASE','INDEX','TRIGGER','REPLACE','DELETE','FUNCTION',
      'PROCEDURE','BEGIN','RETURN','END','FOR','LOOP','IF','THEN','SYS','DBMS',
      'UTL','DBA','ALL','PRIVS','WWV','v\$','SDO','UTIL','CHECK','SECURITY',
      'MDSYS','JAVA',';','--','(1)=\1','[;].[--]');
   v_pattern  VARCHAR2(32700);
   v_score NUMBER:=0;
BEGIN
   FOR i IN 1 .. l_words.count LOOP
      v_pattern:=v_pattern||'|'||l_words(i);
   END LOOP;
   v_score:=v_score+REGEXP_COUNT(p_text,LTRIM(v_pattern,'|'),1,'i');
   RETURN v_score;
END;

Innerhalb dieser Funktion legen wir uns ein Array mit allen Schlüsselwörter an, die als verdächtig erachtet werden. Da "REGEXP_COUNT" nur ein Schlüsselwort erwartet, konkatenieren wir die Schlüsselwörter jeweils mit einem Pipe "|". Der Parameter "1" legt den ersten Buchstaben als Start fest und "i" beachtet keinen Unterschied zwischen Groß- und Kleinschreibung. Sobald "REGEXP_COUNT" einen Match gefunden hat, wird der Score um eins erhöht.

Die Scoring-Abfrage auf einen typischen SQL Injection-String sieht folgend aus:

select GET_SCORE('falscher_nutzer'' OR 1=1 ; DROP TABLE users --') as score
from dual;
     SCORE
----------
         6

Hinweis: Oracle kann nur einen Befehl dynamisch ausführen, der DROP TABLE würde sowieso nicht funktionieren. Aber manche Hacker gehen von SQL Server oder MYSQL Datenbanken aus, da geht so etwas.

Folgende Schlüsselwort wurden also abgefangen: "OR", "1=1", "DROP", "TABLE", ";", "--"

Regex aus dem oberen Beispiel für Schlüsselwörter erklärt:
"(1)=\1": Wahrheitsvergleich 1=1 der sehr beliebt ist
"[;].[--]": Typisches Ende einer SQL Injection: ";--" oder auch "; DROP TABLE wichtig -------"

Da das Thema Reguläre Ausdrücke sehr komplex ist, verweisen wir auf eine gute Stelle im Internet: Opens external link in new windowhttp://www.sqlsnippets.com/en/topic-10764.html

RECHTE & ROLLEN
Das oben beschriebene Prinzip bleibt das selbe:
In ein Array speichern wir potentiell gefährliche Rechte, die wir abfragen möchten. Die Funktion bekommt einen Benutzernamen übergeben, den es zu überprüfen gilt, und zurückgegeben wird ein Scoring-Wert. Es werden nicht nur direkte Rechte abgefragt, sondern auch Rechte die über eine Rolle erteilt wurden.

CREATE OR REPLACE PACKAGE priv_check IS
   /* Notwendige Rechte:
   grant select on sys.sysauth$ to system;
   grant select on sys.user$ to system;
   grant select on sys.system_privilege_map to system;
   grant select on sys.DBA_ROLE_PRIVS to system;
   */
   FUNCTION get_syspriv_score (p_user IN VARCHAR2) RETURN NUMBER;
END;
/
CREATE OR REPLACE PACKAGE BODY priv_check IS
    FUNCTION get_syspriv_score (p_user IN VARCHAR2) RETURN NUMBER
IS
    TYPE privArray IS TABLE OF VARCHAR2(64);
    l_privs privArray := privArray(
        'ALTER ANY LIBRARY',
        'ALTER DATABASE',
        'ALTER USER',
        'BECOME USER',
        'CREATE ANY LIBRARY',
        'CREATE EXTERNAL JOB',
        'CREATE LIBRARY',
        'CREATE USER',
        'DROP ANY INDEX',
        'DROP ANY LIBRARY',
        'DROP ANY PROCEDURE',
        'DROP ANY ROLE',
        'DROP ANY SYNONYM',
        'DROP ANY TABLE',
        'DROP TABLESPACE',
        'DROP ANY TRIGGER',
        'DROP ANY VIEW',
        'DROP PUBLIC DATABASE LINK',
        'DROP USER',
        'EXECUTE ANY LIBRARY',
        'EXEMPT ACCESS POLICY',
        'EXEMPT IDENTITY POLICY',
        'EXPORT FULL DATABASE',
        'GRANT ANY PRIVILEGE',
        'GRANT ANY OBJECT PRIVILEGE',
        'IMPORT FULL DATABASE',
        'SYSDBA',
        'SYSOPER',
        'SYSBACKUP'
    );
    v_score NUMBER:=0;
begin
    -- Direkte Rechte
    FOR c IN (
        select
            u.name username,
            spm.name privilege
        from
            sys.user$ u,
            sys.sysauth$ s,
            sys.system_privilege_map spm
        where
            u.user#=s.grantee# and
            s.privilege#=spm.privilege and
            U.NAME=p_user
    )
    LOOP
        FOR i IN 1 .. l_privs.count LOOP
            IF l_privs(i)= c.privilege THEN
                v_score:=v_score+1;
            END IF;
        END LOOP;
    END LOOP;
    -- Rechte via Rollen
    FOR c IN (
        SELECT
            u1.name USERNAME,
            U2.NAME ROLENAME,
            SUBSTR(SPM.NAME,1,27) PRIVILEGE
        FROM
            SYS.SYSAUTH$ SA1,
            SYS.SYSAUTH$ SA2,
            SYS.USER$ U1,
            SYS.USER$ U2,
            SYS.SYSTEM_PRIVILEGE_MAP SPM
        WHERE
            SA1.GRANTEE# = U1.USER# AND
            SA1.PRIVILEGE# = U2.USER# AND
            U2.USER# = SA2.GRANTEE# (+) AND
            SA2.PRIVILEGE# = SPM.PRIVILEGE (+) AND
            (U1.NAME IN (
                SELECT
                    GRANTEE
                FROM
                    sys.DBA_ROLE_PRIVS connect by prior
                    granted_role=GRANTEE start with GRANTEE IN (
                        SELECT
                            NAME
                        FROM
                            sys.USER$
                        WHERE
                            user# in (
                                select
                                    privilege#
                                from
                                    sys.sysauth$ t1,
                                    sys.user$ t2
                                where
                                    t1.grantee#=t2.user# and
                                    t2.name=p_user
                            )
                    )
                UNION
                SELECT
                    GRANTED_ROLE
                FROM
                    DBA_ROLE_PRIVS connect by prior
                    granted_role=GRANTEE start with GRANTEE IN (
                        SELECT
                            NAME
                        FROM
                            sys.USER$
                        WHERE
                            user# in (
                                select
                                    privilege#
                                from
                                    sys.sysauth$ t1,
                                    sys.user$ t2
                                where
                                    t1.grantee#=t2.user# and
                                    t2.name=p_user
                            )
                    )
            ) OR
            U1.NAME=p_user
            )
    )
    LOOP
        FOR i IN 1 .. l_privs.count LOOP
            IF l_privs(i)= c.privilege THEN
                v_score:=v_score+1;
            END IF;
        END LOOP;
    END LOOP;
    RETURN v_score;
    EXCEPTION WHEN OTHERS THEN
        RETURN sqlcode;
    END get_syspriv_score;
END;
/
Eine Abfrage des Scores selektiert man wie folgt:
SELECT
   *
FROM
   (
   SELECT
      username,
      priv_check.get_syspriv_score(username) AS score
   FROM
      dba_users
   )
WHERE
   score>0
ORDER BY
   score DESC;
USERNAME                         SCORE
--------------------------- ----------
SYS                                140
SYSTEM                              74
WMSYS                                8
APEX_050000                          5
APEX_040200                          5
DVSYS                                4
SPATIAL_CSW_ADMIN_USR                2
SPATIAL_WFS_ADMIN_USR                2
OLAPSYS                              2
GSMADMIN_INTERNAL                    2
SYSBACKUP                            2
MDSYS                                2
SYSDG                                1
XDB                                  1

Der administrative Benutzer "SYS" besitzt logischerweise den höchsten Scoring-Wert.

Um überhaupt herauszufinden welche Benutzer direkt oder über eine Rolle solche Rechte besitzen, kann man dies mithilfe von folgendem Statement herausfinden. Der SELECT listet alle betroffenen Benutzer und zeigt diese in hierachischer Struktur an.

SELECT
    lpad(' ', 2*level) || c "Privilege, Roles and Users"
FROM (
    /* THE PRIVILEGES */
    select
        null   p,
        name   c
    from
        system_privilege_map
    where
        name in (
            'DROP TABLESPACE',
            'CREATE USER',
            'BECOME USER',                            
            'ALTER USER',  
            'DROP USER',   
            'DROP ANY TABLE',        
            'DROP ANY INDEX',       
            'DROP ANY SYNONYM',
            'SYSDBA',          
            'SYSOPER',        
            'DROP ANY VIEW',         
            'DROP PUBLIC DATABASE LINK',
            'DROP ANY ROLE',