Security Scoring

02.
Januar
2017
Veröffentlicht von: Marco Patzwahl

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 Datenbanknutzern.

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 Datenbanknutzern.

SQL Injection

Um SQL Injection-Angriffe abzuwehren bietet Oracle das Package "<link blog-detailansicht sql-injection-erschweren-durch-dbms-assert.html external-link-new-window internal link in current>Öffnet externen Link in neuem FensterDBMS_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örtern 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',          
            'ALTER DATABASE',       
            'DROP ANY PROCEDURE',           
            'DROP ANY TRIGGER',  
            'GRANT ANY PRIVILEGE',            
            'CREATE LIBRARY',         
            'CREATE ANY LIBRARY',
            'ALTER ANY LIBRARY',  
            'DROP ANY LIBRARY',   
            'EXECUTE ANY LIBRARY',             
            'EXEMPT ACCESS POLICY',          
            'EXPORT FULL DATABASE',        
            'IMPORT FULL DATABASE',        
            'EXEMPT IDENTITY POLICY',       
            'CREATE EXTERNAL JOB'
        )
            /* THE ROLES TO ROLES RELATIONS */
    UNION
    SELECT
      granted_role  p,
      grantee       c
    FROM
      dba_role_privs
    /* THE ROLES TO PRIVILEGE RELATIONS */
    UNION
    SELECT
      privilege     p,
      grantee       c
    FROM
      dba_sys_privs
  )
START WITH p IS NULL
CONNECT BY p = PRIOR c;

Ein ebenfalls hilfreiches Statement um herauszufinden, welche Benutzer welche Rechte vererben dürfen:

select
    privilege,
    grantee,
    admin_option
from
    dba_sys_privs
where
    privilege 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',          
        'ALTER DATABASE',       
        'DROP ANY PROCEDURE',           
        'DROP ANY TRIGGER',  
        'GRANT ANY PRIVILEGE',            
        'CREATE LIBRARY',         
        'CREATE ANY LIBRARY',
        'ALTER ANY LIBRARY',  
        'DROP ANY LIBRARY',   
        'EXECUTE ANY LIBRARY',             
        'EXEMPT ACCESS POLICY',          
        'EXPORT FULL DATABASE',        
        'IMPORT FULL DATABASE',        
        'EXEMPT IDENTITY POLICY',       
        'CREATE EXTERNAL JOB'
    )
ORDER BY
    1;

PRIVILEGE               GRANTEE             ADM
----------------------- ------------------- ---
ALTER ANY LIBRARY       DBA                 NO
ALTER ANY LIBRARY       SYS                 NO
ALTER DATABASE          SYS                 NO
ALTER DATABASE          APEX_040200         NO
...

Weitere Ideen und Konzepte erhalten Sie in einem unserer drei Security-Kurse (<link kursdetail-seite datenbank-security-i-grundlagen-ms-1310.html external-link-new-window internal link in current>Opens external link in new windowDB Security I, <link kursdetail-seite datenbank-security-ii-fortschritt-ms-1320.html external-link-new-window internal link in current>Opens external link in new windowDB Security II und <link kursdetail-seite application-express-security-ms-501.html external-link-new-window internal link in current>Opens external link in new windowAPEX Security). Oder Sie fragen nach unserem Consulting Security Check bei Ihnen im Hause.

Jede Menge Know-how für Sie!

In unserer Know-How Datenbank finden Sie mehr als 300 ausführliche Beiträge zu den Oracle-Themen wie DBA, SQL, PL/SQL, APEX und vielem mehr.
Hier erhalten Sie Antworten auf Ihre Fragen.